diff options
author | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-07-16 00:30:23 +0000 |
---|---|---|
committer | tromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-07-16 00:30:23 +0000 |
commit | c8875fb97fc03779a5bba09872227b1d08e5d52a (patch) | |
tree | a0b991cf5866ae1d616639b906ac001811d74508 /libjava/classpath/java | |
parent | c40c1730800ed292b6db39a83d592476fa59623c (diff) | |
download | gcc-c8875fb97fc03779a5bba09872227b1d08e5d52a.tar.gz |
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@102074 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java')
1147 files changed, 301154 insertions, 0 deletions
diff --git a/libjava/classpath/java/applet/Applet.java b/libjava/classpath/java/applet/Applet.java new file mode 100644 index 00000000000..d0610ba0ea1 --- /dev/null +++ b/libjava/classpath/java/applet/Applet.java @@ -0,0 +1,524 @@ +/* Applet.java -- Java base applet class + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Panel; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Locale; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * This is the base applet class. An applet is a Java program that + * runs inside a web browser or other applet viewer in a restricted + * environment. + * + * <p>To be useful, a subclass should override at least start(). Also useful + * are init, stop, and destroy for control purposes, and getAppletInfo and + * getParameterInfo for descriptive purposes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Applet extends Panel +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5836846270535785031L; + + /** The applet stub for this applet. */ + private transient AppletStub stub; + + /** Some applets call setSize in their constructors. In that case, + these fields are used to store width and height values until a + stub is set. */ + private transient int width; + private transient int height; + + /** + * The accessibility context for this applet. + * + * @serial the accessibleContext for this + * @since 1.2 + */ + private AccessibleContext accessibleContext; + + /** + * Default constructor for subclasses. + * + * @throws HeadlessException if in a headless environment + */ + public Applet() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + /** + * The browser calls this method to set the applet's stub, which is the + * low level interface to the browser. Manually setting this to null is + * asking for problems down the road. + * + * @param stub the applet stub for this applet + */ + public final void setStub(AppletStub stub) + { + this.stub = stub; + + if (width != 0 && height != 0) + stub.appletResize (width, height); + } + + /** + * Tests whether or not this applet is currently active. An applet is active + * just before the browser invokes start(), and becomes inactive just + * before the browser invokes stop(). + * + * @return <code>true</code> if this applet is active + */ + public boolean isActive() + { + return stub.isActive(); + } + + /** + * Returns the basename URL of the document this applet is embedded in. This + * is everything up to the final '/'. + * + * @return the URL of the document this applet is embedded in + * @see #getCodeBase() + */ + public URL getDocumentBase() + { + return stub.getDocumentBase(); + } + + /** + * Returns the URL of the code base for this applet. + * + * @return the URL of the code base for this applet + */ + public URL getCodeBase() + { + return stub.getCodeBase(); + } + + /** + * Returns the value of the specified parameter that was specified in + * the <code><APPLET></code> tag for this applet. + * + * @param name the parameter name + * @return the parameter value, or null if the parameter does not exist + * @throws NullPointerException if name is null + */ + public String getParameter(String name) + { + return stub.getParameter(name); + } + + /** + * Returns the applet context for this applet. + * + * @return the applet context for this applet + */ + public AppletContext getAppletContext() + { + return stub.getAppletContext(); + } + + /** + * Requests that the applet window for this applet be resized. + * + * @param width the new width in pixels + * @param height the new height in pixels + */ + public void resize(int width, int height) + { + if (stub == null) + { + this.width = width; + this.height = height; + } + else + stub.appletResize(width, height); + } + + /** + * Requests that the applet window for this applet be resized. + * + * @param dim the requested dimensions + * @throws NullPointerException if dim is null + */ + public void resize(Dimension dim) + { + resize(dim.width, dim.height); + } + + /** + * Displays the specified message in the status window if that window + * exists. + * + * @param message the status message, may be null + */ + public void showStatus(String message) + { + getAppletContext().showStatus(message); + } + + /** + * Returns an image from the specified URL. Note that the image is not + * actually retrieved until the applet attempts to display it, so this + * method returns immediately. + * + * @param url the URL of the image + * @return the retrieved image + * @throws NullPointerException if url is null + */ + public Image getImage(URL url) + { + return getAppletContext().getImage(url); + } + + /** + * Returns an image from the specified absolute URL, and relative path + * from that URL. Note that the image is not actually retrieved until the + * applet attempts to display it, so this method returns immediately. + * This calls <code>getImage(new URL(url, name))</code>, but if building + * the new URL fails, this returns null. + * + * @param url the base URL of the image + * @param name the name of the image relative to the URL + * @return the retrieved image, or null on failure + * @see #getImage(URL) + */ + public Image getImage(URL url, String name) + { + try + { + return getImage(new URL(url, name)); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** + * Returns an audio clip from the specified URL. This clip is not tied to + * any particular applet. + * + * XXX Classpath does not yet implement this. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + * @see #getAudioClip(URL) + * @since 1.2 + */ + public static final AudioClip newAudioClip(URL url) + { + // This requires an implementation of AudioClip in gnu.java.applet. + throw new Error("Not implemented"); + } + + /** + * Returns an audio clip from the specified URL. Note that the clip is not + * actually retrieved until the applet attempts to play it, so this method + * returns immediately. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + */ + public AudioClip getAudioClip(URL url) + { + return getAppletContext().getAudioClip(url); + } + + /** + * Returns an audio clip from the specified absolute URL, and relative path + * from that URL. Note that the clip is not actually retrieved until the + * applet attempts to play it, so this method returns immediately. This + * calls <code>getAudioClip(new URL(url, name))</code>, but if building + * the new URL fails, this returns null. + * + * @param url the base URL of the audio clip + * @param name the name of the clip relative to the URL + * @return the retrieved audio clip, or null on failure + * @see #getAudioClip(URL) + */ + public AudioClip getAudioClip(URL url, String name) + { + try + { + return getAudioClip(new URL(url, name)); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** + * Returns a descriptive string with applet defined information. The + * implementation in this class returns <code>null</code>, so subclasses + * must override to return information. + * + * @return a string describing the author, version, and applet copyright + */ + public String getAppletInfo() + { + return null; + } + + /** + * Returns the locale for this applet, if it has been set. If no applet + * specific locale has been set, the default locale is returned. + * + * @return the locale for this applet + * @see Component#setLocale(Locale) + * @since 1.1 + */ + public Locale getLocale() + { + return super.getLocale(); + } + + /** + * Returns a list of parameters this applet supports. Each element of + * the outer array is an array of three strings with the name of the + * parameter, the data type or valid values, and a description. This + * method is optional and the default implementation returns null. + * + * @return the list of parameters supported by this applet + */ + public String[][] getParameterInfo() + { + return null; + } + + /** + * Loads and plays the audio clip pointed to by the specified URL. This does + * nothing if the URL does not point to a valid audio clip. + * + * @param url the URL of the audio clip + * @throws NullPointerException if url is null + * @see #getAudioClip(URL) + */ + public void play(URL url) + { + AudioClip ac = getAudioClip(url); + try + { + ac.play(); + } + catch (Exception ignored) + { + } + } + + /** + * Loads and plays the audio clip pointed to by the specified absolute URL, + * and relative path from that URL. This does nothing if the URL cannot be + * constructed, or if it does not point to a valid audio clip. + * + * @param url the base URL of the audio clip + * @param name the name of the audio clip relative to the URL + * @see #getAudioClip(URL, String) + * @see #play(URL) + */ + public void play(URL url, String name) + { + try + { + getAudioClip(url, name).play(); + } + catch (Exception ignored) + { + } + } + + /** + * This method is called when the applet is first loaded, before start(). + * The default implementation does nothing; override to do any one-time + * initialization. + * + * @see #start() + * @see #stop() + * @see #destroy() + */ + public void init() + { + } + + /** + * This method is called when the applet should start running. This is + * normally each time a web page containing it is loaded. The default + * implemention does nothing; override for your applet to be useful. + * + * @see #init() + * @see #stop() + * @see #destroy() + */ + public void start() + { + } + + /** + * This method is called when the applet should stop running. This is + * normally when the next web page is loaded. The default implementation + * does nothing; override for your applet to stop using resources when + * it is no longer visible, but may be restarted soon. + * + * @see #init() + * @see #start() + * @see #destroy() + */ + public void stop() + { + } + + /** + * This method is called when the applet is being unloaded. The default + * implementation does nothing; override for your applet to clean up + * resources on exit. + * + * @see #init() + * @see #start() + * @see #stop() + */ + public void destroy() + { + } + + /** + * Gets the AccessibleContext associated with this applet, creating one if + * necessary. This always returns an instance of {@link AccessibleApplet}. + * + * @return the accessibility context of this applet + * @since 1.3 + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleApplet(); + return accessibleContext; + } + + /** + * Read an applet from an object stream. This checks for a headless + * environment, then does the normal read. + * + * @param s the stream to read from + * @throws ClassNotFoundException if a class is not found + * @throws IOException if deserialization fails + * @throws HeadlessException if this is a headless environment + * @see GraphicsEnvironment#isHeadless() + * @since 1.4 + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + s.defaultReadObject(); + } + + /** + * This class provides accessibility support for Applets, and is the + * runtime type returned by {@link #getAccessibleContext()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ + protected class AccessibleApplet extends AccessibleAWTPanel + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 8127374778187708896L; + + /** + * The default constructor. + */ + protected AccessibleApplet() + { + } + + /** + * Get the role of this accessible object, a frame. + * + * @return the role of the object + * @see AccessibleRole#FRAME + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FRAME; + } + + /** + * Get the state set of this accessible object. In addition to the default + * states of a Component, the applet can also be active. + * + * @return the role of the object + * @see AccessibleState + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet s = super.getAccessibleStateSet(); + if (isActive()) + s.add(AccessibleState.ACTIVE); + return s; + } + } // class AccessibleApplet +} // class Applet diff --git a/libjava/classpath/java/applet/AppletContext.java b/libjava/classpath/java/applet/AppletContext.java new file mode 100644 index 00000000000..a17508fd4a4 --- /dev/null +++ b/libjava/classpath/java/applet/AppletContext.java @@ -0,0 +1,154 @@ +/* AppletContext.java -- access the applet's runtime environment + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.awt.Image; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Iterator; + +/** + * This interface allows an applet access to the browser to retrieve + * additional data files and display documents. It also allows the + * applet to find out other applets in the same document. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface AppletContext +{ + /** + * Returns an audio clip from the specified URL. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + */ + AudioClip getAudioClip(URL url); + + /** + * Returns an image from the specified URL. Note that the image is not + * actually retrieved until the applet attempts to display it, so this + * method returns immediately. + * + * @param url the absolute URL of the image + * @return the retrieved image + * @throws NullPointerException if url is null + */ + Image getImage(URL url); + + /** + * Returns the applet in the document for this object that has the + * specified name. + * + * @param name the applet name + * @return the requested applet, or <code>null</code> if not found + */ + Applet getApplet(String name); + + /** + * Returns a list of all the applets in the document for this object. + * + * @return a list of all the applets + */ + Enumeration getApplets(); + + /** + * Displays the web page pointed to by the specified URL in the window + * for this object. This page replaces the document that is currently + * there. + * + * @param url the URL of the web page to load; unspecified on an error + */ + void showDocument(URL url); + + /** + * Displays the web page pointed to be the sepcified URL in the window + * with the specified name. The standard names "_top", "_blank", + * "_parent", and "_self" are allowed. An applet viewer may disregard + * this request. + * + * @param url the URL of the web page to load + * @param target the target window + */ + void showDocument(URL url, String target); + + /** + * Displays the specified message in the status window if that window + * exists. + * + * @param message the status message, may be null + */ + void showStatus(String message); + + /** + * Associate a stream to a key for this applet context, possibly replacing + * the old value. Stream associations are local to the applet context, for + * security purposes. + * + * @param key the key to associate with + * @param stream the stream value to tie to the key, or null to remove + * @throws IOException if the stream is too large + * @since 1.4 + */ + void setStream(String key, InputStream stream) throws IOException; + + /** + * Return the stream associated with a given key in this applet context, or + * null if nothing is associated. Stream associations are local to the + * applet context, for security purposes. + * + * @param key the key to look up + * @return the associated stream, or null + * @since 1.4 + */ + InputStream getStream(String key); + + /** + * Iterate over all keys that have associated streams. Stream associated + * are local to the applet context, for security purposes. + * + * @return an iterator over the association keys + * @since 1.4 + */ + Iterator getStreamKeys(); +} // interface AppletContext diff --git a/libjava/classpath/java/applet/AppletStub.java b/libjava/classpath/java/applet/AppletStub.java new file mode 100644 index 00000000000..879a01638a5 --- /dev/null +++ b/libjava/classpath/java/applet/AppletStub.java @@ -0,0 +1,103 @@ +/* AppletStub.java -- low level interface to the browser + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.net.URL; + +/** + * This interface is the low level interface between the applet and the + * browser. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Applet#setStub(AppletStub) + * @since 1.0 + * @status updated to 1.4 + */ +public interface AppletStub +{ + /** + * Tests whether or not this applet is currently active. An applet is active + * just before the browser invokes start(), and becomes inactive just + * before the browser invokes stop(). + * + * @return <code>true</code> if this applet is active + */ + boolean isActive(); + + /** + * Returns the basename URL of the document this applet is embedded in. This + * is everything up to the final '/'. + * + * @return the URL of the document this applet is embedded in + * @see #getCodeBase() + */ + URL getDocumentBase(); + + /** + * Returns the URL of the code base for this applet. + * + * @return the URL of the code base for this applet + */ + URL getCodeBase(); + + /** + * Returns the value of the specified parameter that was specified in + * the <code><APPLET></code> tag for this applet. + * + * @param name the parameter name + * @return the parameter value, or null if the parameter does not exist + * @throws NullPointerException if name is null + */ + String getParameter(String name); + + /** + * Returns the applet context for this applet. + * + * @return the applet context for this applet + */ + AppletContext getAppletContext(); + + /** + * Requests that the applet window for this applet be resized. + * + * @param width the new width in pixels + * @param height the new height in pixels + */ + void appletResize(int width, int height); +} // interface AppletStub diff --git a/libjava/classpath/java/applet/AudioClip.java b/libjava/classpath/java/applet/AudioClip.java new file mode 100644 index 00000000000..eeafa8aa7a8 --- /dev/null +++ b/libjava/classpath/java/applet/AudioClip.java @@ -0,0 +1,68 @@ +/* AudioClip.java -- play an audio clip in an applet + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + + +/** + * This interface provides a simple mechanism for playing audio clips. + * If multiple clips are played at once, the browser combines them into a + * composite clip. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface AudioClip +{ + /** + * Plays the audio clip starting from the beginning. + */ + void play(); + + /** + * Stops playing this audio clip. There is no mechanism for restarting + * at the point where the clip is stopped. + */ + void stop(); + + /** + * Plays this audio clip in a continuous loop. + */ + void loop(); +} // interface AudioClip diff --git a/libjava/classpath/java/applet/package.html b/libjava/classpath/java/applet/package.html new file mode 100644 index 00000000000..f2cbc57260f --- /dev/null +++ b/libjava/classpath/java/applet/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.applet package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.applet</title></head> + +<body> +<p>Classes and interfaces for small embeddable applications +often used in web pages.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/AWTError.java b/libjava/classpath/java/awt/AWTError.java new file mode 100644 index 00000000000..80356eee440 --- /dev/null +++ b/libjava/classpath/java/awt/AWTError.java @@ -0,0 +1,64 @@ +/* AWTError.java -- A serious AWT error occurred. + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This error is thrown when a critical Abstract Window Toolkit (AWT) error + * occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AWTError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1819846354050686206L; + + /** + * Create a new instance with the specified descriptive error message. + * + * @param message the descriptive error message + */ + public AWTError(String message) + { + super(message); + } +} // class AWTError diff --git a/libjava/classpath/java/awt/AWTEvent.java b/libjava/classpath/java/awt/AWTEvent.java new file mode 100644 index 00000000000..ad9533f9c26 --- /dev/null +++ b/libjava/classpath/java/awt/AWTEvent.java @@ -0,0 +1,278 @@ + +/* AWTEvent.java -- the root event in AWT + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.EventObject; + +/** + * AWTEvent is the root event class for all AWT events in the JDK 1.1 event + * model. It supersedes the Event class from JDK 1.0. Subclasses outside of + * the java.awt package should have IDs greater than RESERVED_ID_MAX. + * + * <p>Event masks defined here are used by components in + * <code>enableEvents</code> to select event types not selected by registered + * listeners. Event masks are appropriately set when registering on + * components. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class AWTEvent extends EventObject +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1825314779160409405L; + + /** + * The ID of the event. + * + * @see #getID() + * @see #AWTEvent(Object, int) + * @serial the identifier number of this event + */ + protected int id; + + /** + * Indicates if the event has been consumed. False mean it is passed to + * the peer, true means it has already been processed. Semantic events + * generated by low-level events always have the value true. + * + * @see #consume() + * @see #isConsumed() + * @serial whether the event has been consumed + */ + protected boolean consumed; + + /** + * Who knows? It's in the serial version. + * + * @serial No idea what this is for. + */ + byte[] bdata; + + /** Mask for selecting component events. */ + public static final long COMPONENT_EVENT_MASK = 0x00001; + + /** Mask for selecting container events. */ + public static final long CONTAINER_EVENT_MASK = 0x00002; + + /** Mask for selecting component focus events. */ + public static final long FOCUS_EVENT_MASK = 0x00004; + + /** Mask for selecting keyboard events. */ + public static final long KEY_EVENT_MASK = 0x00008; + + /** Mask for mouse button events. */ + public static final long MOUSE_EVENT_MASK = 0x00010; + + /** Mask for mouse motion events. */ + public static final long MOUSE_MOTION_EVENT_MASK = 0x00020; + + /** Mask for window events. */ + public static final long WINDOW_EVENT_MASK = 0x00040; + + /** Mask for action events. */ + public static final long ACTION_EVENT_MASK = 0x00080; + + /** Mask for adjustment events. */ + public static final long ADJUSTMENT_EVENT_MASK = 0x00100; + + /** Mask for item events. */ + public static final long ITEM_EVENT_MASK = 0x00200; + + /** Mask for text events. */ + public static final long TEXT_EVENT_MASK = 0x00400; + + /** + * Mask for input method events. + * @since 1.3 + */ + public static final long INPUT_METHOD_EVENT_MASK = 0x00800; + + /** + * Mask if input methods are enabled. Package visible only. + */ + static final long INPUT_ENABLED_EVENT_MASK = 0x01000; + + /** + * Mask for paint events. + * @since 1.3 + */ + public static final long PAINT_EVENT_MASK = 0x02000; + + /** + * Mask for invocation events. + * @since 1.3 + */ + public static final long INVOCATION_EVENT_MASK = 0x04000; + + /** + * Mask for hierarchy events. + * @since 1.3 + */ + public static final long HIERARCHY_EVENT_MASK = 0x08000; + + /** + * Mask for hierarchy bounds events. + * @since 1.3 + */ + public static final long HIERARCHY_BOUNDS_EVENT_MASK = 0x10000; + + /** + * Mask for mouse wheel events. + * @since 1.4 + */ + public static final long MOUSE_WHEEL_EVENT_MASK = 0x20000; + + /** + * Mask for window state events. + * @since 1.4 + */ + public static final long WINDOW_STATE_EVENT_MASK = 0x40000; + + /** + * Mask for window focus events. + * @since 1.4 + */ + public static final long WINDOW_FOCUS_EVENT_MASK = 0x80000; + + /** + * This is the highest number for event ids that are reserved for use by + * the AWT system itself. Subclasses outside of java.awt should use higher + * ids. + */ + public static final int RESERVED_ID_MAX = 1999; + + + /** + * Initializes a new AWTEvent from the old Java 1.0 event object. + * + * @param event the old-style event + * @throws NullPointerException if event is null + */ + public AWTEvent(Event event) + { + this(event.target, event.id); + consumed = event.consumed; + } + + /** + * Create an event on the specified source object and id. + * + * @param source the object that caused the event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public AWTEvent(Object source, int id) + { + super(source); + this.id = id; + } + + /** + * Retarget the event, such as converting a heavyweight component to a + * lightweight child of the original. This is not for general use, but + * is for event targeting systems like KeyboardFocusManager. + * + * @param source the new source + */ + public void setSource(Object source) + { + this.source = source; + } + + /** + * Returns the event type id. + * + * @return the id number of this event + */ + public int getID() + { + return id; + } + + /** + * Create a string that represents this event in the format + * <code>classname[eventstring] on sourcecomponentname</code>. + * + * @return a string representing this event + */ + public String toString () + { + String string = getClass ().getName () + "[" + paramString () + "] on " + + source; + + return string; + } + + /** + * Returns a string representation of the state of this event. It may be + * empty, but must not be null; it is implementation defined. + * + * @return a string representation of this event + */ + public String paramString() + { + return ""; + } + + /** + * Consumes this event so that it will not be processed in the default + * manner. + */ + protected void consume() + { + consumed = true; + } + + /** + * Tests whether not not this event has been consumed. A consumed event + * is not processed in the default manner. + * + * @return true if this event has been consumed + */ + protected boolean isConsumed() + { + return consumed; + } +} // class AWTEvent diff --git a/libjava/classpath/java/awt/AWTEventMulticaster.java b/libjava/classpath/java/awt/AWTEventMulticaster.java new file mode 100644 index 00000000000..f7b9163cf67 --- /dev/null +++ b/libjava/classpath/java/awt/AWTEventMulticaster.java @@ -0,0 +1,1209 @@ +/* AWTEventMulticaster.java -- allows multicast chaining of listeners + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; +import java.awt.event.WindowStateListener; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.EventListener; + +/** + * This class is used to implement a chain of event handlers. Dispatching + * using this class is thread safe. Here is a quick example of how to + * add and delete listeners using this class. For this example, we will + * assume are firing <code>AdjustmentEvent</code>'s. However, this + * same approach is useful for all events in the <code>java.awt.event</code> + * package, and more if this class is subclassed. + * + * <p><code> + * AdjustmentListener al; + * public void addAdjustmentListener(AdjustmentListener listener) + * { + * al = AWTEventMulticaster.add(al, listener); + * } + * public void removeAdjustmentListener(AdjustmentListener listener) + * { + * al = AWTEventMulticaster.remove(al, listener); + * } + * </code> + * + * <p>When it come time to process an event, simply call <code>al</code>, + * assuming it is not <code>null</code>, and all listeners in the chain will + * be fired. + * + * <p>The first time <code>add</code> is called it is passed + * <code>null</code> and <code>listener</code> as its arguments. This + * starts building the chain. This class returns <code>listener</code> + * which becomes the new <code>al</code>. The next time, <code>add</code> + * is called with <code>al</code> and <code>listener</code> and the + * new listener is then chained to the old. + * + * @author Bryce McKinlay + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class AWTEventMulticaster + implements ComponentListener, ContainerListener, FocusListener, KeyListener, + MouseListener, MouseMotionListener, WindowListener, + WindowFocusListener, WindowStateListener, ActionListener, + ItemListener, AdjustmentListener, TextListener, + InputMethodListener, HierarchyListener, HierarchyBoundsListener, + MouseWheelListener +{ + /** + * A variable in the event chain. + */ + protected final EventListener a; + + /** + * A variable in the event chain. + */ + protected final EventListener b; + + /** + * Initializes a new instance of <code>AWTEventMulticaster</code> with + * the specified event listener parameters. The parameters should not be + * null, although it is not required to enforce this with a + * NullPointerException. + * + * @param a the "a" listener object + * @param b the "b" listener object + */ + protected AWTEventMulticaster(EventListener a, EventListener b) + { + this.a = a; + this.b = b; + } + + /** + * Removes one instance of the specified listener from this multicaster + * chain. This descends recursively if either child is a multicaster, and + * returns a multicaster chain with the old listener removed. + * + * @param oldl the object to remove from this multicaster + * @return the resulting multicaster with the specified listener removed + */ + protected EventListener remove(EventListener oldl) + { + // If oldl is an immediate child, return the other child. + if (a == oldl) + return b; + if (b == oldl) + return a; + // If a and/or b are Multicaster's, search them recursively. + if (a instanceof AWTEventMulticaster) + { + EventListener newa = ((AWTEventMulticaster) a).remove(oldl); + if (newa != a) + return new AWTEventMulticaster(newa, b); + } + if (b instanceof AWTEventMulticaster) + { + EventListener newb = ((AWTEventMulticaster) b).remove(oldl); + if (newb != b) + return new AWTEventMulticaster(a, newb); + } + // oldl was not found. + return this; + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentResized(ComponentEvent e) + { + ((ComponentListener) a).componentResized(e); + ((ComponentListener) b).componentResized(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentMoved(ComponentEvent e) + { + ((ComponentListener) a).componentMoved(e); + ((ComponentListener) b).componentMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentShown(ComponentEvent e) + { + ((ComponentListener) a).componentShown(e); + ((ComponentListener) b).componentShown(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentHidden(ComponentEvent e) + { + ((ComponentListener) a).componentHidden(e); + ((ComponentListener) b).componentHidden(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentAdded(ContainerEvent e) + { + ((ContainerListener) a).componentAdded(e); + ((ContainerListener) b).componentAdded(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentRemoved(ContainerEvent e) + { + ((ContainerListener) a).componentRemoved(e); + ((ContainerListener) b).componentRemoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void focusGained(FocusEvent e) + { + ((FocusListener) a).focusGained(e); + ((FocusListener) b).focusGained(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void focusLost(FocusEvent e) + { + ((FocusListener) a).focusLost(e); + ((FocusListener) b).focusLost(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyTyped(KeyEvent e) + { + ((KeyListener) a).keyTyped(e); + ((KeyListener) b).keyTyped(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyPressed(KeyEvent e) + { + ((KeyListener) a).keyPressed(e); + ((KeyListener) b).keyPressed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyReleased(KeyEvent e) + { + ((KeyListener) a).keyReleased(e); + ((KeyListener) b).keyReleased(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseClicked(MouseEvent e) + { + ((MouseListener) a).mouseClicked(e); + ((MouseListener) b).mouseClicked(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mousePressed(MouseEvent e) + { + ((MouseListener) a).mousePressed(e); + ((MouseListener) b).mousePressed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseReleased(MouseEvent e) + { + ((MouseListener) a).mouseReleased(e); + ((MouseListener) b).mouseReleased(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseEntered(MouseEvent e) + { + ((MouseListener) a).mouseEntered(e); + ((MouseListener) b).mouseEntered(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseExited(MouseEvent e) + { + ((MouseListener) a).mouseExited(e); + ((MouseListener) b).mouseExited(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseDragged(MouseEvent e) + { + ((MouseMotionListener) a).mouseDragged(e); + ((MouseMotionListener) b).mouseDragged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseMoved(MouseEvent e) + { + ((MouseMotionListener) a).mouseMoved(e); + ((MouseMotionListener) b).mouseMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowOpened(WindowEvent e) + { + ((WindowListener) a).windowOpened(e); + ((WindowListener) b).windowOpened(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowClosing(WindowEvent e) + { + ((WindowListener) a).windowClosing(e); + ((WindowListener) b).windowClosing(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowClosed(WindowEvent e) + { + ((WindowListener) a).windowClosed(e); + ((WindowListener) b).windowClosed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowIconified(WindowEvent e) + { + ((WindowListener) a).windowIconified(e); + ((WindowListener) b).windowIconified(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowDeiconified(WindowEvent e) + { + ((WindowListener) a).windowDeiconified(e); + ((WindowListener) b).windowDeiconified(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowActivated(WindowEvent e) + { + ((WindowListener) a).windowActivated(e); + ((WindowListener) b).windowActivated(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowDeactivated(WindowEvent e) + { + ((WindowListener) a).windowDeactivated(e); + ((WindowListener) b).windowDeactivated(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowStateChanged(WindowEvent e) + { + ((WindowStateListener) a).windowStateChanged(e); + ((WindowStateListener) b).windowStateChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowGainedFocus(WindowEvent e) + { + ((WindowFocusListener) a).windowGainedFocus(e); + ((WindowFocusListener) b).windowGainedFocus(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowLostFocus(WindowEvent e) + { + ((WindowFocusListener) a).windowLostFocus(e); + ((WindowFocusListener) b).windowLostFocus(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void actionPerformed(ActionEvent e) + { + ((ActionListener) a).actionPerformed(e); + ((ActionListener) b).actionPerformed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void itemStateChanged(ItemEvent e) + { + ((ItemListener) a).itemStateChanged(e); + ((ItemListener) b).itemStateChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void adjustmentValueChanged(AdjustmentEvent e) + { + ((AdjustmentListener) a).adjustmentValueChanged(e); + ((AdjustmentListener) b).adjustmentValueChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void textValueChanged(TextEvent e) + { + ((TextListener) a).textValueChanged(e); + ((TextListener) b).textValueChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.2 + */ + public void inputMethodTextChanged(InputMethodEvent e) + { + ((InputMethodListener) a).inputMethodTextChanged(e); + ((InputMethodListener) b).inputMethodTextChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.2 + */ + public void caretPositionChanged(InputMethodEvent e) + { + ((InputMethodListener) a).caretPositionChanged(e); + ((InputMethodListener) b).caretPositionChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void hierarchyChanged(HierarchyEvent e) + { + ((HierarchyListener) a).hierarchyChanged(e); + ((HierarchyListener) b).hierarchyChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void ancestorMoved(HierarchyEvent e) + { + ((HierarchyBoundsListener) a).ancestorMoved(e); + ((HierarchyBoundsListener) b).ancestorMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void ancestorResized(HierarchyEvent e) + { + ((HierarchyBoundsListener) a).ancestorResized(e); + ((HierarchyBoundsListener) b).ancestorResized(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void mouseWheelMoved(MouseWheelEvent e) + { + ((MouseWheelListener) a).mouseWheelMoved(e); + ((MouseWheelListener) b).mouseWheelMoved(e); + } + + /** + * Chain <code>ComponentListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ComponentListener add(ComponentListener a, ComponentListener b) + { + return (ComponentListener) addInternal(a, b); + } + + /** + * Chain <code>ContainerListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ContainerListener add(ContainerListener a, ContainerListener b) + { + return (ContainerListener) addInternal(a, b); + } + + /** + * Chain <code>FocusListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static FocusListener add(FocusListener a, FocusListener b) + { + return (FocusListener) addInternal(a, b); + } + + /** + * Chain <code>KeyListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static KeyListener add(KeyListener a, KeyListener b) + { + return (KeyListener) addInternal(a, b); + } + + /** + * Chain <code>MouseListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static MouseListener add(MouseListener a, MouseListener b) + { + return (MouseListener) addInternal(a, b); + } + + /** + * Chain <code>MouseMotionListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static MouseMotionListener add(MouseMotionListener a, + MouseMotionListener b) + { + return (MouseMotionListener) addInternal(a, b); + } + + /** + * Chain <code>WindowListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static WindowListener add(WindowListener a, WindowListener b) + { + return (WindowListener) addInternal(a, b); + } + + /** + * Chain <code>WindowStateListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static WindowStateListener add(WindowStateListener a, + WindowStateListener b) + { + return (WindowStateListener) addInternal(a, b); + } + + /** + * Chain <code>WindowFocusListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static WindowFocusListener add(WindowFocusListener a, + WindowFocusListener b) + { + return (WindowFocusListener) addInternal(a, b); + } + + /** + * Chain <code>ActionListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ActionListener add(ActionListener a, ActionListener b) + { + return (ActionListener) addInternal(a, b); + } + + /** + * Chain <code>ItemListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ItemListener add(ItemListener a, ItemListener b) + { + return (ItemListener) addInternal(a, b); + } + + /** + * Chain <code>AdjustmentListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static AdjustmentListener add(AdjustmentListener a, + AdjustmentListener b) + { + return (AdjustmentListener) addInternal(a, b); + } + + /** + * Chain <code>AdjustmentListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static TextListener add(TextListener a, TextListener b) + { + return (TextListener) addInternal(a, b); + } + + /** + * Chain <code>InputMethodListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.2 + */ + public static InputMethodListener add(InputMethodListener a, + InputMethodListener b) + { + return (InputMethodListener) addInternal(a, b); + } + + /** + * Chain <code>HierarchyListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.3 + */ + public static HierarchyListener add(HierarchyListener a, HierarchyListener b) + { + return (HierarchyListener) addInternal(a, b); + } + + /** + * Chain <code>HierarchyBoundsListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.3 + */ + public static HierarchyBoundsListener add(HierarchyBoundsListener a, + HierarchyBoundsListener b) + { + return (HierarchyBoundsListener) addInternal(a, b); + } + + /** + * Chain <code>MouseWheelListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static MouseWheelListener add(MouseWheelListener a, + MouseWheelListener b) + { + return (MouseWheelListener) addInternal(a, b); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ComponentListener remove(ComponentListener l, + ComponentListener oldl) + { + return (ComponentListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ContainerListener remove(ContainerListener l, + ContainerListener oldl) + { + return (ContainerListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static FocusListener remove(FocusListener l, FocusListener oldl) + { + return (FocusListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static KeyListener remove(KeyListener l, KeyListener oldl) + { + return (KeyListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static MouseListener remove(MouseListener l, MouseListener oldl) + { + return (MouseListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static MouseMotionListener remove(MouseMotionListener l, + MouseMotionListener oldl) + { + return (MouseMotionListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static WindowListener remove(WindowListener l, WindowListener oldl) + { + return (WindowListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static WindowStateListener remove(WindowStateListener l, + WindowStateListener oldl) + { + return (WindowStateListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static WindowFocusListener remove(WindowFocusListener l, + WindowFocusListener oldl) + { + return (WindowFocusListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ActionListener remove(ActionListener l, ActionListener oldl) + { + return (ActionListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ItemListener remove(ItemListener l, ItemListener oldl) + { + return (ItemListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static AdjustmentListener remove(AdjustmentListener l, + AdjustmentListener oldl) + { + return (AdjustmentListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static TextListener remove(TextListener l, TextListener oldl) + { + return (TextListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.2 + */ + public static InputMethodListener remove(InputMethodListener l, + InputMethodListener oldl) + { + return (InputMethodListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.3 + */ + public static HierarchyListener remove(HierarchyListener l, + HierarchyListener oldl) + { + return (HierarchyListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.3 + */ + public static HierarchyBoundsListener remove(HierarchyBoundsListener l, + HierarchyBoundsListener oldl) + { + return (HierarchyBoundsListener) removeInternal(l, oldl); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static MouseWheelListener remove(MouseWheelListener l, + MouseWheelListener oldl) + { + return (MouseWheelListener) removeInternal(l, oldl); + } + + /** + * Chain <code>EventListener</code> a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + protected static EventListener addInternal(EventListener a, EventListener b) + { + if (a == null) + return b; + if (b == null) + return a; + return new AWTEventMulticaster(a, b); + } + + /** + * Removes the listener <code>oldl</code> from the listener <code>l</code>. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + protected static EventListener removeInternal(EventListener l, + EventListener oldl) + { + if (l == oldl) + return null; + if (l instanceof AWTEventMulticaster) + return ((AWTEventMulticaster) l).remove(oldl); + return l; + } + + /** + * Saves all Serializable listeners to a serialization stream. + * + * @param s the stream to save to + * @param k a prefix stream put before each serializable listener + * @throws IOException if serialization fails + */ + protected void saveInternal(ObjectOutputStream s, String k) + throws IOException + { + // This is not documented by Sun, but I think it is correct. + if (a instanceof AWTEventMulticaster) + ((AWTEventMulticaster) a).saveInternal(s, k); + else if (a instanceof Serializable) + { + s.writeObject(k); + s.writeObject(a); + } + if (b instanceof AWTEventMulticaster) + ((AWTEventMulticaster) b).saveInternal(s, k); + else if (b instanceof Serializable) + { + s.writeObject(k); + s.writeObject(b); + } + } + + /** + * Saves a Serializable listener chain to a serialization stream. + * + * @param s the stream to save to + * @param k a prefix stream put before each serializable listener + * @param l the listener chain to save + * @throws IOException if serialization fails + */ + protected static void save(ObjectOutputStream s, String k, EventListener l) + throws IOException + { + // This is not documented by Sun, but I think it is correct. + if (l instanceof AWTEventMulticaster) + ((AWTEventMulticaster) l).saveInternal(s, k); + else if (l instanceof Serializable) + { + s.writeObject(k); + s.writeObject(l); + } + } + + /** + * Returns an array of all chained listeners of the specified type in the + * given chain. A null listener returns an empty array, and a listener + * which is not an AWTEventMulticaster returns an array of one element. If + * no listeners in the chain are of the specified type, an empty array is + * returned. + * + * @param l the listener chain to convert to an array + * @param type the type of listeners to collect + * @return an array of the listeners of that type in the chain + * @throws ClassCastException if type is not assignable from EventListener + * @throws NullPointerException if type is null + * @throws IllegalArgumentException if type is Void.TYPE + * @since 1.4 + */ + public static EventListener[] getListeners(EventListener l, Class type) + { + ArrayList list = new ArrayList(); + if (l instanceof AWTEventMulticaster) + ((AWTEventMulticaster) l).getListeners(list, type); + else if (type.isInstance(l)) + list.add(l); + EventListener[] r = (EventListener[]) Array.newInstance(type, list.size()); + list.toArray(r); + return r; + } + + /** + * Collects all instances of the given type in the chain into the list. + * + * @param l the list to collect into + * @param type the type of listeners to collect + * @throws NullPointerException if type is null + * @see #getListeners(EventListener, Class) + */ + private void getListeners(ArrayList l, Class type) + { + if (a instanceof AWTEventMulticaster) + ((AWTEventMulticaster) a).getListeners(l, type); + else if (type.isInstance(a)) + l.add(a); + if (b instanceof AWTEventMulticaster) + ((AWTEventMulticaster) b).getListeners(l, type); + else if (type.isInstance(b)) + l.add(b); + } +} // class AWTEventMulticaster diff --git a/libjava/classpath/java/awt/AWTException.java b/libjava/classpath/java/awt/AWTException.java new file mode 100644 index 00000000000..2df3dd80102 --- /dev/null +++ b/libjava/classpath/java/awt/AWTException.java @@ -0,0 +1,64 @@ +/* AWTException.java -- Generic AWT exception + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This is a generic exception that indicates an exception occurred in the + * Abstract Window Toolkit (AWT) system. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AWTException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1900414231151323879L; + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public AWTException(String message) + { + super(message); + } +} // class AWTException diff --git a/libjava/classpath/java/awt/AWTKeyStroke.java b/libjava/classpath/java/awt/AWTKeyStroke.java new file mode 100644 index 00000000000..c10d53e4d10 --- /dev/null +++ b/libjava/classpath/java/awt/AWTKeyStroke.java @@ -0,0 +1,660 @@ +/* AWTKeyStroke.java -- an immutable key stroke + Copyright (C) 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * This class mirrors KeyEvents, representing both low-level key presses and + * key releases, and high level key typed inputs. However, this class forms + * immutable strokes, and can be efficiently reused via the factory methods + * for creating them. + * + * <p>For backwards compatibility with Swing, this supports a way to build + * instances of a subclass, using reflection, provided the subclass has a + * no-arg constructor (of any accessibility). + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see #getAWTKeyStroke(char) + * @since 1.4 + * @status updated to 1.4 + */ +public class AWTKeyStroke implements Serializable +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -6430539691155161871L; + + /** The mask for modifiers. */ + private static final int MODIFIERS_MASK = 0x3fef; + + /** + * The cache of recently created keystrokes. This maps KeyStrokes to + * KeyStrokes in a cache which removes the least recently accessed entry, + * under the assumption that garbage collection of a new keystroke is + * easy when we find the old one that it matches in the cache. + */ + private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true) + { + /** The largest the keystroke cache can grow. */ + private static final int MAX_CACHE_SIZE = 2048; + + /** Prune stale entries. */ + protected boolean removeEldestEntry(Map.Entry eldest) + { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround. + return size() > MAX_CACHE_SIZE; + } + }; + + /** The most recently generated keystroke, or null. */ + private static AWTKeyStroke recent; + + /** + * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note + * that this will be left accessible, to get around private access; but + * it should not be a security risk as it is highly unlikely that creating + * protected instances of the subclass via reflection will do much damage. + */ + private static Constructor ctor; + + /** + * A table of keyCode names to values. This is package-private to + * avoid an accessor method. + * + * @see #getAWTKeyStroke(String) + */ + static final HashMap vktable = new HashMap(); + static + { + // Using reflection saves the hassle of keeping this in sync with KeyEvent, + // at the price of an expensive initialization. + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + Field[] fields = KeyEvent.class.getFields(); + int i = fields.length; + try + { + while (--i >= 0) + { + Field f = fields[i]; + String name = f.getName(); + if (name.startsWith("VK_")) + vktable.put(name.substring(3), f.get(null)); + } + } + catch (Exception e) + { + throw (Error) new InternalError().initCause(e); + } + return null; + } + }); + } + + /** + * The typed character, or CHAR_UNDEFINED for key presses and releases. + * + * @serial the keyChar + */ + private char keyChar; + + /** + * The virtual key code, or VK_UNDEFINED for key typed. Package visible for + * use by Component. + * + * @serial the keyCode + */ + int keyCode; + + /** + * The modifiers in effect. To match Sun, this stores the old style masks + * for shift, control, alt, meta, and alt-graph (but not button1); as well + * as the new style of extended modifiers for all modifiers. + * + * @serial bitwise or of the *_DOWN_MASK modifiers + */ + private int modifiers; + + /** + * True if this is a key release; should only be true if keyChar is + * CHAR_UNDEFINED. + * + * @serial true to distinguish key pressed from key released + */ + private boolean onKeyRelease; + + /** + * Construct a keystroke with default values: it will be interpreted as a + * key typed event with an invalid character and no modifiers. Client code + * should use the factory methods instead. + * + * @see #getAWTKeyStroke(char) + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int, boolean) + * @see #getAWTKeyStroke(int, int) + * @see #getAWTKeyStrokeForEvent(KeyEvent) + * @see #getAWTKeyStroke(String) + */ + protected AWTKeyStroke() + { + keyChar = KeyEvent.CHAR_UNDEFINED; + } + + /** + * Construct a keystroke with the given values. Client code should use the + * factory methods instead. + * + * @param keyChar the character entered, if this is a key typed + * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed + * @param modifiers the modifier keys for the keystroke, in old or new style + * @param onKeyRelease true if this is a key release instead of a press + * @see #getAWTKeyStroke(char) + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int, boolean) + * @see #getAWTKeyStroke(int, int) + * @see #getAWTKeyStrokeForEvent(KeyEvent) + * @see #getAWTKeyStroke(String) + */ + protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, + boolean onKeyRelease) + { + this.keyChar = keyChar; + this.keyCode = keyCode; + // No need to call extend(), as only trusted code calls this constructor. + this.modifiers = modifiers; + this.onKeyRelease = onKeyRelease; + } + + /** + * Registers a new subclass as being the type of keystrokes to generate in + * the factory methods. This operation flushes the cache of stored keystrokes + * if the class differs from the current one. The new class must be + * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may + * be private). + * + * @param subclass the new runtime type of generated keystrokes + * @throws IllegalArgumentException subclass doesn't have no-arg constructor + * @throws ClassCastException subclass doesn't extend AWTKeyStroke + */ + protected static void registerSubclass(final Class subclass) + { + if (subclass == null) + throw new IllegalArgumentException(); + if (subclass.equals(ctor == null ? AWTKeyStroke.class + : ctor.getDeclaringClass())) + return; + if (subclass.equals(AWTKeyStroke.class)) + { + cache.clear(); + recent = null; + ctor = null; + return; + } + try + { + ctor = (Constructor) AccessController.doPrivileged + (new PrivilegedExceptionAction() + { + public Object run() + throws NoSuchMethodException, InstantiationException, + IllegalAccessException, InvocationTargetException + { + Constructor c = subclass.getDeclaredConstructor(null); + c.setAccessible(true); + // Create a new instance, to make sure that we can, and + // to cause any ClassCastException. + AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(null); + return c; + } + }); + } + catch (PrivilegedActionException e) + { + // e.getCause() will not ever be ClassCastException; that should + // escape on its own. + throw (RuntimeException) + new IllegalArgumentException().initCause(e.getCause()); + } + cache.clear(); + recent = null; + } + + /** + * Returns a keystroke representing a typed character. + * + * @param keyChar the typed character + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(char keyChar) + { + return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); + } + + /** + * Returns a keystroke representing a typed character with the given + * modifiers. Note that keyChar is a <code>Character</code> instead of a + * <code>char</code> to avoid accidental ambiguity with + * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise + * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK) + * is preferred, but the old style will work. + * + * @param keyChar the typed character + * @param modifiers the modifiers, or 0 + * @return the specified keystroke + * @throws IllegalArgumentException if keyChar is null + */ + public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) + { + if (keyChar == null) + throw new IllegalArgumentException(); + return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, + extend(modifiers), false); + } + + /** + * Returns a keystroke representing a pressed or released key event, with + * the given modifiers. The "virtual key" should be one of the VK_* + * constants in {@link KeyEvent}. The modifiers are the bitwise or of the + * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is + * preferred, but the old style will work. + * + * @param keyCode the virtual key + * @param modifiers the modifiers, or 0 + * @param release true if this is a key release instead of a key press + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, + boolean release) + { + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, + extend(modifiers), release); + } + + /** + * Returns a keystroke representing a pressed key event, with the given + * modifiers. The "virtual key" should be one of the VK_* constants in + * {@link KeyEvent}. The modifiers are the bitwise or of the masks found + * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the + * old style will work. + * + * @param keyCode the virtual key + * @param modifiers the modifiers, or 0 + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) + { + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, + extend(modifiers), false); + } + + /** + * Returns a keystroke representing what caused the key event. + * + * @param event the key event to convert + * @return the specified keystroke, or null if the event is invalid + * @throws NullPointerException if event is null + */ + public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event) + { + switch (event.id) + { + case KeyEvent.KEY_TYPED: + return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED, + extend(event.getModifiersEx()), false); + case KeyEvent.KEY_PRESSED: + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), + extend(event.getModifiersEx()), false); + case KeyEvent.KEY_RELEASED: + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), + extend(event.getModifiersEx()), true); + default: + return null; + } + } + + /** + * Parses a string and returns the keystroke that it represents. The syntax + * for keystrokes is listed below, with tokens separated by an arbitrary + * number of spaces: + * <pre> + * keyStroke := <modifiers>* ( <typedID> | <codeID> ) + * modifiers := ( shift | control | ctrl | meta | alt + * | button1 | button2 | button3 ) + * typedID := typed <single Unicode character> + * codeID := ( pressed | released )? <name> + * name := <the KeyEvent field name less the leading "VK_"> + * </pre> + * + * <p>Note that the grammar is rather weak, and not all valid keystrokes + * can be generated in this manner (for example, a typed space, or anything + * with the alt-graph modifier!). The output of AWTKeyStroke.toString() + * will not meet the grammar. If pressed or released is not specified, + * pressed is assumed. Examples:<br> + * <code> + * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br> + * "control DELETE" => + * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br> + * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, + * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br> + * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, + * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br> + * "typed a" => getAWTKeyStroke('a'); + * </code> + * + * @param s the string to parse + * @throws IllegalArgumentException if s is null or cannot be parsed + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(String s) + { + if (s == null) + throw new IllegalArgumentException("null argument"); + StringTokenizer t = new StringTokenizer(s, " "); + if (! t.hasMoreTokens()) + throw new IllegalArgumentException("no tokens '" + s + "'"); + int modifiers = 0; + boolean released = false; + String token = null; + do + { + token = t.nextToken(); + if ("shift".equals(token)) + modifiers |= KeyEvent.SHIFT_DOWN_MASK; + else if ("ctrl".equals(token) || "control".equals(token)) + modifiers |= KeyEvent.CTRL_DOWN_MASK; + else if ("meta".equals(token)) + modifiers |= KeyEvent.META_DOWN_MASK; + else if ("alt".equals(token)) + modifiers |= KeyEvent.ALT_DOWN_MASK; + else if ("button1".equals(token)) + modifiers |= KeyEvent.BUTTON1_DOWN_MASK; + else if ("button2".equals(token)) + modifiers |= KeyEvent.BUTTON2_DOWN_MASK; + else if ("button3".equals(token)) + modifiers |= KeyEvent.BUTTON3_DOWN_MASK; + else if ("typed".equals(token)) + { + if (t.hasMoreTokens()) + { + token = t.nextToken(); + if (! t.hasMoreTokens() && token.length() == 1) + return getAWTKeyStroke(token.charAt(0), + KeyEvent.VK_UNDEFINED, modifiers, + false); + } + throw new IllegalArgumentException("Invalid 'typed' argument '" + + s + "'"); + } + else if ("pressed".equals(token)) + { + if (t.hasMoreTokens()) + token = t.nextToken(); + break; + } + else if ("released".equals(token)) + { + released = true; + if (t.hasMoreTokens()) + token = t.nextToken(); + break; + } + else + break; + } + while (t.hasMoreTokens()); + // Now token contains the VK name we must parse. + Integer code = (Integer) vktable.get(token); + if (code == null) + throw new IllegalArgumentException("Unknown token '" + token + + "' in '" + s + "'"); + if (t.hasMoreTokens()) + throw new IllegalArgumentException("Too many tokens: " + s); + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(), + modifiers, released); + } + + /** + * Returns the character of this keystroke, if it was typed. + * + * @return the character value, or CHAR_UNDEFINED + * @see #getAWTKeyStroke(char) + */ + public final char getKeyChar() + { + return keyChar; + } + + /** + * Returns the virtual key code of this keystroke, if it was pressed or + * released. This will be a VK_* constant from KeyEvent. + * + * @return the virtual key code value, or VK_UNDEFINED + * @see #getAWTKeyStroke(int, int) + */ + public final int getKeyCode() + { + return keyCode; + } + + /** + * Returns the modifiers for this keystroke. This will be a bitwise or of + * constants from InputEvent; it includes the old style masks for shift, + * control, alt, meta, and alt-graph (but not button1); as well as the new + * style of extended modifiers for all modifiers. + * + * @return the modifiers + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int) + */ + public final int getModifiers() + { + return modifiers; + } + + /** + * Tests if this keystroke is a key release. + * + * @return true if this is a key release + * @see #getAWTKeyStroke(int, int, boolean) + */ + public final boolean isOnKeyRelease() + { + return onKeyRelease; + } + + /** + * Returns the AWT event type of this keystroke. This is one of + * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or + * {@link KeyEvent#KEY_RELEASED}. + * + * @return the key event type + */ + public final int getKeyEventType() + { + return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED + : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED; + } + + /** + * Returns a hashcode for this key event. It is not documented, but appears + * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1) + * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>. + * + * @return the hashcode + */ + public int hashCode() + { + return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2 + + (onKeyRelease ? 1 : 2); + } + + /** + * Tests two keystrokes for equality. + * + * @param o the object to test + * @return true if it is equal + */ + public final boolean equals(Object o) + { + if (! (o instanceof AWTKeyStroke)) + return false; + AWTKeyStroke s = (AWTKeyStroke) o; + return this == o || (keyChar == s.keyChar && keyCode == s.keyCode + && modifiers == s.modifiers + && onKeyRelease == s.onKeyRelease); + } + + /** + * Returns a string representation of this keystroke. For typed keystrokes, + * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers()) + + getKeyChar()</code>; for pressed and released keystrokes, this is + * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers()) + * + KeyEvent.getKeyText(getKeyCode()) + * + (isOnKeyRelease() ? "-R" : "-P")</code>. + * + * @return a string representation + */ + public String toString() + { + if (keyCode == KeyEvent.VK_UNDEFINED) + return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar; + return "keyCode " + KeyEvent.getKeyModifiersText(modifiers) + + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P"); + } + + /** + * Returns a cached version of the deserialized keystroke, if available. + * + * @return a cached replacement + * @throws ObjectStreamException if something goes wrong + */ + protected Object readResolve() throws ObjectStreamException + { + AWTKeyStroke s = (AWTKeyStroke) cache.get(this); + if (s != null) + return s; + cache.put(this, this); + return this; + } + + /** + * Gets the appropriate keystroke, creating one if necessary. + * + * @param keyChar the keyChar + * @param keyCode the keyCode + * @param modifiers the modifiers + * @param release true for key release + * @return the specified keystroke + */ + private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode, + int modifiers, boolean release) + { + // Check level 0 cache. + AWTKeyStroke stroke = recent; // Avoid thread races. + if (stroke != null && stroke.keyChar == keyChar + && stroke.keyCode == keyCode && stroke.modifiers == modifiers + && stroke.onKeyRelease == release) + return stroke; + // Create a new object, on the assumption that if it has a match in the + // cache, the VM can easily garbage collect it as it is temporary. + Constructor c = ctor; // Avoid thread races. + if (c == null) + stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release); + else + try + { + stroke = (AWTKeyStroke) c.newInstance(null); + stroke.keyChar = keyChar; + stroke.keyCode = keyCode; + stroke.modifiers = modifiers; + stroke.onKeyRelease = release; + } + catch (Exception e) + { + throw (Error) new InternalError().initCause(e); + } + // Check level 1 cache. + AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke); + if (cached == null) + cache.put(stroke, stroke); + else + stroke = cached; + return recent = stroke; + } + + /** + * Converts the modifiers to the appropriate format. + * + * @param mod the modifiers to convert + * @return the adjusted modifiers + */ + private static int extend(int mod) + { + if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0) + mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK; + if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0) + mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK; + if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0) + mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK; + if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0) + mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK; + if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0) + mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK; + if ((mod & KeyEvent.BUTTON1_MASK) != 0) + mod |= KeyEvent.BUTTON1_DOWN_MASK; + return mod & MODIFIERS_MASK; + } +} // class AWTKeyStroke diff --git a/libjava/classpath/java/awt/AWTPermission.java b/libjava/classpath/java/awt/AWTPermission.java new file mode 100644 index 00000000000..3e50c059f88 --- /dev/null +++ b/libjava/classpath/java/awt/AWTPermission.java @@ -0,0 +1,121 @@ +/* AWTPermission.java -- AWT related permissions + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.security.BasicPermission; + +/** + * This class implements permissions for AWT. This is a named + * permission. No actions are defined. + * + * <p>The following table provides a list of all the possible AWTPermission + * permission names with a description of what that permission allows.<br> + * <table border=1> + * <tr><th>Permission Name</th><th>Permission Allows</th><th>Risks</th</tr> + * <tr> + * <td><code>accessClipboard</code></td> + * <td>posting and reading the AWT clipboard</td> + * <td>the clipboard may contain sensitive data</td></tr> + * <tr> + * <td><code>accessEventQueue</code></td> + * <td>access to the AWT event queue</td> + * <td>malicious code could remove real events and replace them with bogus + * ones, including simulating the user granting permission</td></tr> + * <tr> + * <td><code>listenToAllAWTEvents</code></td> + * <td>listen to system-wide AWT events</td> + * <td>malicious code can read passwords entered in an AWT event, and in + * combination with accessEventQueue, could fake system events</td></tr> + * <tr> + * <td><code>showWindowWithoutWarningBanner</code></td> + * <td>display a window without a banner notification of insecurity</td> + * <td>malicious code could install a Trojan horse applet that looks like + * a normal window, and thus steal data like passwords</td></tr> + * <tr> + * <td><code>readDisplayPixels</code></td> + * <td>read back pixels from the display screen</td> + * <td>malicious code could snoop on the user's actions</td></tr> + * <tr> + * <td><code>createRobot</code></td> + * <td>create an instance of java.awt.Robot</td> + * <td>these objects can generate events as though they were the user; so + * malicious code could control the system</td></tr> + * <tr> + * <td><code>fullScreenExclusive</code></td> + * <td>enter full-screen exclusive mode</td> + * <td>malicious code could masquerade as a trusted program</td></tr> + * </table> + * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.2 + * @status updated to 1.4 + */ +public final class AWTPermission extends BasicPermission +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8890392402588814465L; + + /** + * Construct a AWTPermission with the given name. + * + * @param name the permission name + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public AWTPermission(String name) + { + super(name); + } + + /** + * Create a new permission with the specified name. The actions argument + * is ignored, as AWT permissions have no actions. + * + * @param name the permission name + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public AWTPermission(String name, String actions) + { + super(name); + } +} // class AWTPermission diff --git a/libjava/classpath/java/awt/ActiveEvent.java b/libjava/classpath/java/awt/ActiveEvent.java new file mode 100644 index 00000000000..e42959fe3d6 --- /dev/null +++ b/libjava/classpath/java/awt/ActiveEvent.java @@ -0,0 +1,61 @@ +/* ActiveEvent.java -- a self-dispatching event + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * An interface for events which can dispatch themselves in another thread. + * This has two uses: first, if your code is in a critical section, calling a + * synchronized method might deadlock. But by using an ActiveEvent to call + * the second section, it will not obtain the lock until you have left the + * critical section, avoiding deadlock. The second use is for calling + * untrusted code. For example, system code should use an ActiveEvent to + * invoke user code securely. + * + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.2 + * @status updated to 1.4 + */ +public interface ActiveEvent +{ + /** + * Dispatch the event, according to what the event needs done. Invoked + * automatically if this is placed on the <code>EventDispatchQueue</code>. + */ + void dispatch(); +} // interface ActiveEvent diff --git a/libjava/classpath/java/awt/Adjustable.java b/libjava/classpath/java/awt/Adjustable.java new file mode 100644 index 00000000000..8f633e91a62 --- /dev/null +++ b/libjava/classpath/java/awt/Adjustable.java @@ -0,0 +1,171 @@ +/* Adjustable.java -- Objects with a numeric adjustment scale + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentListener; + +/** + * This interface is for objects that take a numeric value that can be + * adjusted within a bounded range. For example, a scroll bar. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface Adjustable +{ + /** Constant for an adjustable object with horizontal orientation. */ + int HORIZONTAL = 0; + + /** Constant for an adjustable object with vertical orientation. */ + int VERTICAL = 1; + + /** Constant for an adjustable object with no orientation. */ + int NO_ORIENTATION = 2; + + /** + * Returns a constant representing the orientation of the object. + * + * @return the orientation of this object + * @see #HORIZONTAL + * @see #VERTICAL + * @see #NO_ORIENTATION + */ + int getOrientation(); + + /** + * Sets the minimum value this object can have. + * + * @param minimum the new minimum value + */ + void setMinimum(int minimum); + + /** + * Returns the minimum value this object can have. + * + * @return the minimum value + */ + int getMinimum(); + + /** + * Sets the maximum value this object can have. + * + * @param maximum the new maximum value + */ + void setMaximum(int maximum); + + /** + * Returns the maximum value this object can have. + * + * @return the maximum value + */ + int getMaximum(); + + /** + * Sets the increment value for incrementing the value by units. + * + * @param increment the unit increment value + */ + void setUnitIncrement(int increment); + + /** + * Returns the increment value for incrementing the value by units. + * + * @return the unit increment value + */ + int getUnitIncrement(); + + /** + * Sets the increment value for incrementing the value by blocks. + * + * @param increment the block increment value + */ + void setBlockIncrement(int increment); + + /** + * Returns the increment value for incrementing the value by blocks. + * + * @return the block increment value + */ + int getBlockIncrement(); + + /** + * Sets the length of the indicator for this object to the specified value. + * + * @param length the indicator length + */ + void setVisibleAmount(int length); + + /** + * Returns the length of the indicator for this object. + * + * @return the indicator length + */ + int getVisibleAmount(); + + /** + * Sets the current value of the object. + * + * @param value the new value + */ + void setValue(int value); + + /** + * Returns the current value of the object. + * + * @return the current value + */ + int getValue(); + + /** + * Adds a listener that will receive adjustment events for this object. + * + * @param listener the adjustment listener to add + * @see java.awt.event.AdjustmentEvent + */ + void addAdjustmentListener(AdjustmentListener listener); + + /** + * Removes an adjustment listener from this object. + * + * @param listener the adjustment listener to remove + * @see java.awt.event.AdjustmentEvent + */ + void removeAdjustmentListener(AdjustmentListener listener); +} // interface Adjustable diff --git a/libjava/classpath/java/awt/AlphaComposite.java b/libjava/classpath/java/awt/AlphaComposite.java new file mode 100644 index 00000000000..435cfd054c3 --- /dev/null +++ b/libjava/classpath/java/awt/AlphaComposite.java @@ -0,0 +1,167 @@ +/* AlphaComposite.java -- provides a context for performing alpha compositing + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Composite + * @see CompositeContext + * @since 1.3 + * @status updated to 1.4 except for createContext, needs documentation + */ +public final class AlphaComposite implements Composite +{ + /** Map Long to AlphaComposites. See getInstance for details. */ + private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true) + { + /** The largest the alpha composite cache can grow. */ + private static final int MAX_CACHE_SIZE = 2048; + + /** Prune stale entries. */ + protected boolean removeEldestEntry(Map.Entry eldest) + { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround. + return size() > MAX_CACHE_SIZE; + } + }; + + public static final int CLEAR = 1; + public static final int SRC = 2; + public static final int DST = 9; + public static final int SRC_OVER = 3; + public static final int DST_OVER = 4; + public static final int SRC_IN = 5; + public static final int DST_IN = 6; + public static final int SRC_OUT = 7; + public static final int DST_OUT = 8; + public static final int SRC_ATOP = 10; + public static final int DST_ATOP = 11; + public static final int XOR = 12; + public static final AlphaComposite Clear = getInstance(CLEAR); + public static final AlphaComposite Src = getInstance(SRC); + public static final AlphaComposite Dst = getInstance(DST); + public static final AlphaComposite SrcOver = getInstance(SRC_OVER); + public static final AlphaComposite DstOver = getInstance(DST_OVER); + public static final AlphaComposite SrcIn = getInstance(SRC_IN); + public static final AlphaComposite DstIn = getInstance(DST_IN); + public static final AlphaComposite SrcOut = getInstance(SRC_OUT); + public static final AlphaComposite DstOut = getInstance(DST_OUT); + public static final AlphaComposite SrcAtop = getInstance(SRC_ATOP); + public static final AlphaComposite DstAtop = getInstance(DST_ATOP); + public static final AlphaComposite Xor = getInstance(XOR); + + private final int rule; + private final float alpha; + private AlphaComposite(int rule, float alpha) + { + this.rule = rule; + this.alpha = alpha; + } + + /** + * Creates an AlphaComposite object with the specified rule. + * + * @param rule The compositing rule. + * + * @exception IllegalArgumentException If rule is not one of the following: + * CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, + * SRC_ATOP, DST_ATOP, or XOR. + */ + public static AlphaComposite getInstance(int rule) + { + return getInstance(rule, 1); + } + + /** + * Creates an AlphaComposite object with the specified rule and the constant + * alpha to multiply with the alpha of the source. The source is multiplied + * with the specified alpha before being composited with the destination. + * + * @param rule The compositing rule. + * + * @exception IllegalArgumentException If rule is not one of the following: + * CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, + * SRC_ATOP, DST_ATOP, or XOR. + */ + public static AlphaComposite getInstance(int rule, float alpha) + { + if (rule < CLEAR || rule > XOR || ! (alpha >= 0 && alpha <= 1)) + throw new IllegalArgumentException(); + // This long is guaranteed unique for all valid alpha composites. + Long l = new Long(rule + Double.doubleToLongBits(alpha)); + AlphaComposite a = (AlphaComposite) cache.get(l); + if (a == null) + { + a = new AlphaComposite(rule, alpha); + cache.put(l, a); + } + return a; + } + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) + { + // XXX Implement. Sun uses undocumented implementation class + // sun.java2d.SunCompositeContext. + throw new Error("not implemented"); + } + public float getAlpha() + { + return alpha; + } + public int getRule() + { + return rule; + } + public int hashCode() + { + return 31 * Float.floatToIntBits(alpha) + rule; + } + public boolean equals(Object o) + { + if (! (o instanceof AlphaComposite)) + return false; + AlphaComposite a = (AlphaComposite) o; + return rule == a.rule && alpha == a.alpha; + } +} // class AlphaComposite diff --git a/libjava/classpath/java/awt/AttributeValue.java b/libjava/classpath/java/awt/AttributeValue.java new file mode 100644 index 00000000000..080e92e221f --- /dev/null +++ b/libjava/classpath/java/awt/AttributeValue.java @@ -0,0 +1,98 @@ +/* AttributeValue.java -- parent of type-safe enums of attributes + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class is undocumented by Sun, but it is the parent of several other + * classes, all of which are type-safe enumerations. This takes care of + * <code>equals</code>, <code>toString</code>, and <code>hashCode</code>, so + * that you don't have to (although hashCode is commonly overridden). + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class AttributeValue +{ + /** The value of the enumeration. Package visible for speed. */ + final int value; + + /** The list of enumeration names for the given subclass. */ + private final String[] names; + + /** + * Construct a type-safe enumeration element. For example,<br> + * <pre> + * class Foo extends AttributeValue + * { + * private static final String[] names = { "one", "two" } + * public static final Foo ONE = new Foo(0); + * public static final Foo TWO = new Foo(1); + * private Foo(int value) { super(value, names); } + * } + * </pre> + * + * @param value the position of this enumeration element, consecutive from 0 + * @param names the constant list of enumeration names for the subclass + */ + AttributeValue(int value, String[] names) + { + this.value = value; + this.names = names; + } + + /** + * Returns the hashcode of this element. This is the index of the element + * in the enumeration. Note that equals defaults to the == relation. + * + * @return the hashcode + */ + public int hashCode() + { + return value; + } + + /** + * Returns the name of this enumeration element. + * + * @return the element name + */ + public String toString() + { + return names[value]; + } +} // class AttributeValue diff --git a/libjava/classpath/java/awt/BasicStroke.java b/libjava/classpath/java/awt/BasicStroke.java new file mode 100644 index 00000000000..bb008e4c791 --- /dev/null +++ b/libjava/classpath/java/awt/BasicStroke.java @@ -0,0 +1,248 @@ +/* BasicStroke.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Arrays; + +/** + * STUB CLASS ONLY + */ +public class BasicStroke implements Stroke +{ + public static final int JOIN_MITER = 0; + public static final int JOIN_ROUND = 1; + public static final int JOIN_BEVEL = 2; + + public static final int CAP_BUTT = 0; + public static final int CAP_ROUND = 1; + public static final int CAP_SQUARE = 2; + + private final float width; + private final int cap; + private final int join; + private final float limit; + private final float[] dash; + private final float phase; + + /** + * Creates a basic stroke. + * + * @param width May not be negative . + * @param cap May be either CAP_BUTT, CAP_ROUND or CAP_SQUARE. + * @param join May be either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER. + * @param miterlimit the limit to trim the miter join. The miterlimit must be + * greater than or equal to 1.0f. + * @param dash The array representing the dashing pattern. There must be at + * least one non-zero entry. + * @param dashPhase is negative and dash is not null. + * + * @exception IllegalArgumentException If one input parameter doesn't meet + * its needs. + */ + public BasicStroke(float width, int cap, int join, float miterlimit, + float[] dash, float dashPhase) + { + if (width < 0.0f ) + throw new IllegalArgumentException("width " + width + " < 0"); + else if (cap < CAP_BUTT || cap > CAP_SQUARE) + throw new IllegalArgumentException("cap " + cap + " out of range [" + + CAP_BUTT + ".." + CAP_SQUARE + "]"); + else if (miterlimit < 1.0f && join == JOIN_MITER) + throw new IllegalArgumentException("miterlimit " + miterlimit + + " < 1.0f while join == JOIN_MITER"); + else if (join < JOIN_MITER || join > JOIN_BEVEL) + throw new IllegalArgumentException("join " + join + " out of range [" + + JOIN_MITER + ".." + JOIN_BEVEL + + "]"); + else if (dashPhase < 0.0f && dash != null) + throw new IllegalArgumentException("dashPhase " + dashPhase + + " < 0.0f while dash != null"); + else if (dash != null) + if (dash.length == 0) + throw new IllegalArgumentException("dash.length is 0"); + else + { + boolean allZero = true; + + for ( int i = 0; i < dash.length; ++i) + { + if (dash[i] != 0.0f) + { + allZero = false; + break; + } + } + + if (allZero) + throw new IllegalArgumentException("all dashes are 0.0f"); + } + + this.width = width; + this.cap = cap; + this.join = join; + limit = miterlimit; + this.dash = dash == null ? null : (float[]) dash.clone(); + phase = dashPhase; + } + + /** + * Creates a basic stroke. + * + * @param width The width of the BasicStroke. May not be negative . + * @param cap May be either CAP_BUTT, CAP_ROUND or CAP_SQUARE. + * @param join May be either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER. + * @param miterlimit the limit to trim the miter join. The miterlimit must be + * greater than or equal to 1.0f. + * + * @exception IllegalArgumentException If one input parameter doesn't meet + * its needs. + */ + public BasicStroke(float width, int cap, int join, float miterlimit) + { + this(width, cap, join, miterlimit, null, 0); + } + + /** + * Creates a basic stroke. + * + * @param width The width of the BasicStroke. May not be nehative. + * @param cap May be either CAP_BUTT, CAP_ROUND or CAP_SQUARE. + * @param join May be either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER. + * + * @exception IllegalArgumentException If one input parameter doesn't meet + * its needs. + * @exception IllegalArgumentException FIXME + */ + public BasicStroke(float width, int cap, int join) + { + this(width, cap, join, 10, null, 0); + } + + /** + * Creates a basic stroke. + * + * @param width The width of the BasicStroke. + * + * @exception IllegalArgumentException If width is negative. + */ + public BasicStroke(float width) + { + this(width, CAP_SQUARE, JOIN_MITER, 10, null, 0); + } + + /** + * Creates a basic stroke. + */ + public BasicStroke() + { + this(1, CAP_SQUARE, JOIN_MITER, 10, null, 0); + } + + public Shape createStrokedShape(Shape s) + { + throw new Error("not implemented"); + } + + public float getLineWidth() + { + return width; + } + + public int getEndCap() + { + return cap; + } + + public int getLineJoin() + { + return join; + } + + public float getMiterLimit() + { + return limit; + } + + public float[] getDashArray() + { + return dash; + } + + public float getDashPhase() + { + return phase; + } + + /** + * Returns the hash code for this object. The hash is calculated by + * xoring the hash, cap, join, limit, dash array and phase values + * (converted to <code>int</code> first with + * <code>Float.floatToIntBits()</code> if the value is a + * <code>float</code>). + */ + public int hashCode() + { + int hash = Float.floatToIntBits(width); + hash ^= cap; + hash ^= join; + hash ^= Float.floatToIntBits(limit); + + if (dash != null) + for (int i = 0; i < dash.length; i++) + hash ^= Float.floatToIntBits(dash[i]); + + hash ^= Float.floatToIntBits(phase); + + return hash; + } + + /** + * Returns true if the given Object is an instance of BasicStroke + * and the width, cap, join, limit, dash array and phase are all + * equal. + */ + public boolean equals(Object o) + { + if (! (o instanceof BasicStroke)) + return false; + BasicStroke s = (BasicStroke) o; + return width == s.width && cap == s.cap && join == s.join + && limit == s.limit && Arrays.equals(dash, s.dash) && phase == s.phase; + } +} // class BasicStroke diff --git a/libjava/classpath/java/awt/BorderLayout.java b/libjava/classpath/java/awt/BorderLayout.java new file mode 100644 index 00000000000..c9eb5dd8c37 --- /dev/null +++ b/libjava/classpath/java/awt/BorderLayout.java @@ -0,0 +1,731 @@ +/* BorderLayout.java -- A layout manager class + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class implements a layout manager that positions components + * in certain sectors of the parent container. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class BorderLayout implements LayoutManager2, java.io.Serializable +{ + +/* + * Static Variables + */ + +/** + * Constant indicating the top of the container + */ +public static final String NORTH = "North"; + +/** + * Constant indicating the bottom of the container + */ +public static final String SOUTH = "South"; + +/** + * Constant indicating the right side of the container + */ +public static final String EAST = "East"; + +/** + * Constant indicating the left side of the container + */ +public static final String WEST = "West"; + +/** + * Constant indicating the center of the container + */ +public static final String CENTER = "Center"; + + + /** + * The constant indicating the position before the first line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #NORTH}, for + * a bottom-to-top orientation, it is the same as {@link #SOUTH}. + * + * <p>This constant is an older name for {@link #PAGE_START} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String BEFORE_FIRST_LINE = "First"; + + + /** + * The constant indicating the position after the last line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #SOUTH}, for + * a bottom-to-top orientation, it is the same as {@link #NORTH}. + * + * <p>This constant is an older name for {@link #PAGE_END} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String AFTER_LAST_LINE = "Last"; + + + /** + * The constant indicating the position before the first item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #WEST}, for + * a right-to-left orientation, it is the same as {@link #EAST}. + * + * <p>This constant is an older name for {@link #LINE_START} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String BEFORE_LINE_BEGINS = "Before"; + + + /** + * The constant indicating the position after the last item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #EAST}, for + * a right-to-left orientation, it is the same as {@link #WEST}. + * + * <p>This constant is an older name for {@link #LINE_END} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String AFTER_LINE_ENDS = "After"; + + + /** + * The constant indicating the position before the first line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #NORTH}, for + * a bottom-to-top orientation, it is the same as {@link #SOUTH}. + * + * @since 1.4 + */ + public static final String PAGE_START = BEFORE_FIRST_LINE; + + + /** + * The constant indicating the position after the last line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #SOUTH}, for + * a bottom-to-top orientation, it is the same as {@link #NORTH}. + * + * @since 1.4 + */ + public static final String PAGE_END = AFTER_LAST_LINE; + + + /** + * The constant indicating the position before the first item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #WEST}, for + * a right-to-left orientation, it is the same as {@link #EAST}. + * + * @since 1.4 + */ + public static final String LINE_START = BEFORE_LINE_BEGINS; + + + /** + * The constant indicating the position after the last item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #EAST}, for + * a right-to-left orientation, it is the same as {@link #WEST}. + * + * @since 1.4 + */ + public static final String LINE_END = AFTER_LINE_ENDS; + + + +// Serialization constant +private static final long serialVersionUID = -8658291919501921765L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial + */ +private Component north; + +/** + * @serial + */ +private Component south; + +/** + * @serial + */ +private Component east; + +/** + * @serial + */ +private Component west; + +/** + * @serial + */ +private Component center; + +/** + * @serial + */ +private Component firstLine; + +/** + * @serial + */ +private Component lastLine; + +/** + * @serial + */ +private Component firstItem; + +/** + * @serial + */ +private Component lastItem; + +/** + * @serial The horizontal gap between components + */ +private int hgap; + +/** + * @serial The vertical gap between components + */ +private int vgap; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>BorderLayout</code> with no + * horiztonal or vertical gaps between components. + */ +public +BorderLayout() +{ + this(0,0); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>BorderLayout</code> with the + * specified horiztonal and vertical gaps between components. + * + * @param hgap The horizontal gap between components. + * @param vgap The vertical gap between components. + */ +public +BorderLayout(int hgap, int vgap) +{ + this.hgap = hgap; + this.vgap = vgap; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the horitzontal gap value. + * + * @return The horitzontal gap value. + */ +public int +getHgap() +{ + return(hgap); +} + +/*************************************************************************/ + +/** + * Sets the horizontal gap to the specified value. + * + * @param hgap The new horizontal gap. + */ +public void +setHgap(int hgap) +{ + this.hgap = hgap; +} + +/*************************************************************************/ + +/** + * Returns the vertical gap value. + * + * @return The vertical gap value. + */ +public int +getVgap() +{ + return(vgap); +} + +/*************************************************************************/ + +/** + * Sets the vertical gap to the specified value. + * + * @param vgap The new vertical gap value. + */ +public void +setVgap(int vgap) +{ + this.vgap = vgap; +} + +/*************************************************************************/ + +/** + * Adds a component to the layout in the specified constraint position, + * which must be one of the string constants defined in this class. + * + * @param component The component to add. + * @param constraints The constraint string. + * + * @exception IllegalArgumentException If the constraint object is not + * a string, or is not one of the specified constants in this class. + */ +public void +addLayoutComponent(Component component, Object constraints) +{ + if (constraints != null && ! (constraints instanceof String)) + throw new IllegalArgumentException("Constraint must be a string"); + + addLayoutComponent((String) constraints, component); +} + +/*************************************************************************/ + +/** + * Adds a component to the layout in the specified constraint position, + * which must be one of the string constants defined in this class. + * + * @param constraints The constraint string. + * @param component The component to add. + * + * @exception IllegalArgumentException If the constraint object is not + * one of the specified constants in this class. + * + * @deprecated This method is deprecated in favor of + * <code>addLayoutComponent(Component, Object)</code>. + */ +public void +addLayoutComponent(String constraints, Component component) +{ + String str = constraints; + + if (str == null || str.equals(CENTER)) + center = component; + else if (str.equals(NORTH)) + north = component; + else if (str.equals(SOUTH)) + south = component; + else if (str.equals(EAST)) + east = component; + else if (str.equals(WEST)) + west = component; + else if (str.equals(BEFORE_FIRST_LINE)) + firstLine = component; + else if (str.equals(AFTER_LAST_LINE)) + lastLine = component; + else if (str.equals(BEFORE_LINE_BEGINS)) + firstItem = component; + else if (str.equals(AFTER_LINE_ENDS)) + lastItem = component; + else + throw new IllegalArgumentException("Constraint value not valid: " + str); +} + +/*************************************************************************/ + +/** + * Removes the specified component from the layout. + * + * @param component The component to remove from the layout. + */ +public void +removeLayoutComponent(Component component) +{ + if (north == component) + north = null; + if (south == component) + south = null; + if (east == component) + east = null; + if (west == component) + west = null; + if (center == component) + center = null; + if (firstItem == component) + firstItem = null; + if (lastItem == component) + lastItem = null; + if (firstLine == component) + firstLine = null; + if (lastLine == component) + lastLine = null; +} + +/*************************************************************************/ + +/** + * Returns the minimum size of the specified container using this layout. + * + * @param target The container to calculate the minimum size for. + * + * @return The minimum size of the container + */ +public Dimension +minimumLayoutSize(Container target) +{ + return calcSize(target, MIN); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of the specified container using this layout. + * + * @param target The container to calculate the preferred size for. + * + * @return The preferred size of the container + */ +public Dimension +preferredLayoutSize(Container target) +{ + return calcSize(target, PREF); +} + +/*************************************************************************/ + +/** + * Returns the maximum size of the specified container using this layout. + * + * @param target The container to calculate the maximum size for. + * + * @return The maximum size of the container + */ +public Dimension +maximumLayoutSize(Container target) +{ + return calcSize(target, MAX); +} + +/*************************************************************************/ + +/** + * Returns the X axis alignment, which is a <code>float</code> indicating + * where along the X axis this container wishs to position its layout. + * 0 indicates align to the left, 1 indicates align to the right, and 0.5 + * indicates align to the center. + * + * @param parent The parent container. + * + * @return The X alignment value. + */ +public float +getLayoutAlignmentX(Container parent) +{ + return(parent.getAlignmentX()); +} + +/*************************************************************************/ + +/** + * Returns the Y axis alignment, which is a <code>float</code> indicating + * where along the Y axis this container wishs to position its layout. + * 0 indicates align to the top, 1 indicates align to the bottom, and 0.5 + * indicates align to the center. + * + * @param parent The parent container. + * + * @return The Y alignment value. + */ +public float +getLayoutAlignmentY(Container parent) +{ + return(parent.getAlignmentY()); +} + +/*************************************************************************/ + +/** + * Instructs this object to discard any layout information it might + * have cached. + * + * @param parent The parent container. + */ +public void +invalidateLayout(Container parent) +{ +} + +/*************************************************************************/ + +/** + * Lays out the specified container according to the constraints + * in this object. + * + * @param target The container to lay out. + */ +public void +layoutContainer(Container target) +{ + synchronized (target.getTreeLock ()) + { + Insets i = target.getInsets(); + + ComponentOrientation orient = target.getComponentOrientation (); + boolean left_to_right = orient.isLeftToRight (); + + Component my_north = north; + Component my_east = east; + Component my_south = south; + Component my_west = west; + + // Note that we currently don't handle vertical layouts. Neither + // does JDK 1.3. + if (firstLine != null) + my_north = firstLine; + if (lastLine != null) + my_south = lastLine; + if (firstItem != null) + { + if (left_to_right) + my_west = firstItem; + else + my_east = firstItem; + } + if (lastItem != null) + { + if (left_to_right) + my_east = lastItem; + else + my_west = lastItem; + } + + Dimension c = calcCompSize(center, PREF); + Dimension n = calcCompSize(my_north, PREF); + Dimension s = calcCompSize(my_south, PREF); + Dimension e = calcCompSize(my_east, PREF); + Dimension w = calcCompSize(my_west, PREF); + Dimension t = target.getSize(); + + /* + <-> hgap <-> hgap + +----------------------------+ } + |t | } i.top + | +----------------------+ | --- y1 } + | |n | | + | +----------------------+ | } vgap + | +---+ +----------+ +---+ | --- y2 } } + | |w | |c | |e | | } hh + | +---+ +----------+ +---+ | } vgap } + | +----------------------+ | --- y3 } + | |s | | + | +----------------------+ | } + | | } i.bottom + +----------------------------+ } + |x1 |x2 |x3 + <----------------------> + <--> ww <--> + i.left i.right + */ + + int x1 = i.left; + int x2 = x1 + w.width + hgap; + int x3; + if (t.width <= i.right + e.width) + x3 = x2 + w.width + hgap; + else + x3 = t.width - i.right - e.width; + int ww = t.width - i.right - i.left; + + int y1 = i.top; + int y2 = y1 + n.height + vgap; + int midh = Math.max(e.height, Math.max(w.height, c.height)); + int y3; + if (t.height <= i.bottom + s.height) + y3 = y2 + midh + vgap; + else + y3 = t.height - i.bottom - s.height; + int hh = y3-y2-vgap; + + setBounds(center, x2, y2, x3-x2-hgap, hh); + setBounds(my_north, x1, y1, ww, n.height); + setBounds(my_south, x1, y3, ww, s.height); + setBounds(my_west, x1, y2, w.width, hh); + setBounds(my_east, x3, y2, e.width, hh); + } +} + +/*************************************************************************/ + +/** + * Returns a string representation of this layout manager. + * + * @return A string representation of this object. + */ +public String +toString() +{ + return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; +} + +private void +setBounds(Component comp, int x, int y, int w, int h) +{ + if (comp == null) + return; + comp.setBounds(x, y, w, h); +} + +// Some constants for use with calcSize(). +private static final int MIN = 0; +private static final int MAX = 1; +private static final int PREF = 2; + +private Dimension +calcCompSize(Component comp, int what) +{ + if (comp == null || !comp.isVisible()) + return new Dimension(0, 0); + if (what == MIN) + return comp.getMinimumSize(); + else if (what == MAX) + return comp.getMaximumSize(); + return comp.getPreferredSize(); +} + +// This is a helper function used to compute the various sizes for +// this layout. +private Dimension +calcSize(Container target, int what) +{ + synchronized (target.getTreeLock ()) + { + Insets ins = target.getInsets(); + + ComponentOrientation orient = target.getComponentOrientation (); + boolean left_to_right = orient.isLeftToRight (); + + Component my_north = north; + Component my_east = east; + Component my_south = south; + Component my_west = west; + + // Note that we currently don't handle vertical layouts. Neither + // does JDK 1.3. + if (firstLine != null) + my_north = firstLine; + if (lastLine != null) + my_south = lastLine; + if (firstItem != null) + { + if (left_to_right) + my_west = firstItem; + else + my_east = firstItem; + } + if (lastItem != null) + { + if (left_to_right) + my_east = lastItem; + else + my_west = lastItem; + } + + Dimension ndim = calcCompSize(my_north, what); + Dimension sdim = calcCompSize(my_south, what); + Dimension edim = calcCompSize(my_east, what); + Dimension wdim = calcCompSize(my_west, what); + Dimension cdim = calcCompSize(center, what); + + int width = edim.width + cdim.width + wdim.width + (hgap * 2); + // check for overflow + if (width < edim.width || width < cdim.width || width < cdim.width) + width = Integer.MAX_VALUE; + + if (ndim.width > width) + width = ndim.width; + if (sdim.width > width) + width = sdim.width; + + width += (ins.left + ins.right); + + int height = edim.height; + if (cdim.height > height) + height = cdim.height; + if (wdim.height > height) + height = wdim.height; + + int addedHeight = height + (ndim.height + sdim.height + (vgap * 2) + + ins.top + ins.bottom); + // check for overflow + if (addedHeight < height) + height = Integer.MAX_VALUE; + else + height = addedHeight; + + return(new Dimension(width, height)); + } +} +} // class BorderLayout diff --git a/libjava/classpath/java/awt/BufferCapabilities.java b/libjava/classpath/java/awt/BufferCapabilities.java new file mode 100644 index 00000000000..bba83dcb0d9 --- /dev/null +++ b/libjava/classpath/java/awt/BufferCapabilities.java @@ -0,0 +1,253 @@ +/* BufferCapabilities.java -- double-buffering capabilities descriptor + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.BufferStrategy; + +/** + * A double-buffering capability descriptor. This class exposes + * details about the double-buffering algorithms used by image + * buffers. + * + * BufferCapabilities represents algorithms that involve at least two + * buffers but it can also specify so-called "multi-buffer" schemes + * involving more than two buffers. This class describes the + * capabilities of the front and back buffers as well as the results + * of "flipping" -- that is, what happens when an image is transferred + * from the back buffer to the front buffer. + * + * Flipping may or may not be supported or may be supported only in + * fullscreen mode. If it is not supported then "blitting" is implied + * -- that is, the contents of the back buffer are copied using a fast + * block transfer operation from the back buffer to the front buffer. + * + * The front buffer is the one that is displayed. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @see BufferStrategy#getCapabilities() + * @see GraphicsConfiguration#getBufferCapabilities() + * + * @since 1.4 + */ +public class BufferCapabilities implements Cloneable +{ + /** + * A type-safe enumeration of buffer flipping results. + * + * @see AttributeValue + */ + public static final class FlipContents extends AttributeValue + { + /** + * The names of the different flipping results. + */ + private static final String[] NAMES + = { "undefined", "background", "prior", "copied" }; + + /** + * The contents of the back buffer are undefined after flipping. + */ + public static final FlipContents UNDEFINED = new FlipContents(0); + + /** + * The back buffer is cleared with the background color after + * flipping. + */ + public static final FlipContents BACKGROUND = new FlipContents(1); + + /** + * The back buffer contains the pre-flipping contents of the front + * buffer after flipping. In other words a true "flip" has been + * performed. + */ + public static final FlipContents PRIOR = new FlipContents(2); + + /** + * The back buffer has the same contents as the front buffer after + * flipping. + */ + public static final FlipContents COPIED = new FlipContents(3); + + /** + * Create a new flipping result descriptor. + * + * @param value the enumeration value + */ + private FlipContents(int value) + { + super(value, NAMES); + } + } + + /** + * Front buffer capabilities descriptor. + */ + private final ImageCapabilities front; + + /** + * Back buffer capabilities descriptor. + */ + private final ImageCapabilities back; + + /** + * Describes the results of a "flip" operation. + */ + private final FlipContents flip; + + /** + * Creates a buffer capabilities object. + * + * @param frontCaps front buffer capabilities descriptor + * @param backCaps back buffer capabilities descriptor + * @param flip the results of a flip operation or null if + * flipping is not supported + * + * @exception IllegalArgumentException if frontCaps or backCaps is + * null + */ + public BufferCapabilities(ImageCapabilities frontCaps, + ImageCapabilities backCaps, + FlipContents flip) + { + if (frontCaps == null || backCaps == null) + throw new IllegalArgumentException(); + this.front = frontCaps; + this.back = backCaps; + this.flip = flip; + } + + /** + * Retrieve the front buffer's image capabilities. + * + * @return the front buffer's image capabilities + */ + public ImageCapabilities getFrontBufferCapabilities() + { + return front; + } + + /** + * Retrieve the back buffer's image capabilities. + * + * @return the back buffer's image capabilities + */ + public ImageCapabilities getBackBufferCapabilities() + { + return back; + } + + /** + * Return whether or not flipping is supported. + * + * @return true if flipping is supported, false otherwise + */ + public boolean isPageFlipping() + { + return flip != null; + } + + /** + * Retrieve the result of a flipping operation. If this method + * returns null then flipping is not supported. This implies that + * "blitting", a fast block transfer, is used to copy the contents + * of the back buffer to the front buffer. Other possible return + * values are: + * <ul> + * <li><code>FlipContents.UNDEFINED</code> the contents of the + * back buffer are undefined after flipping.</li> + * <li><code>FlipContents.BACKGROUND</code> the contents of the + * back buffer are cleared to the background color after + * flipping.</li> + * <li><code>FlipContents.PRIOR</code> the back buffer contains + * the pre-flipping contents of the front * buffer after + * flipping.</li> + * <li><code>FlipContents.COPIED</code> the back buffer has the + * same contents as the front buffer after flipping.</li> + * </ul> + * + * @return the result of a flipping operation or null if flipping is + * not supported + */ + public FlipContents getFlipContents() + { + return flip; + } + + /** + * Returns true if flipping is only supported in fullscreen mode. + * + * @return true if flipping is only supported in fullscreen mode, + * false otherwise + */ + public boolean isFullScreenRequired() + { + return true; + } + + /** + * Returns true if flipping can involve more than two buffers. One + * or more intermediate buffers may be available in addition to the + * front and back buffers. + * + * @return true if there are more than two buffers available for + * flipping, false otherwise + */ + public boolean isMultiBufferAvailable() + { + return false; + } + + /** + * Clone this buffering capability descriptor. + * + * @return a clone of this buffer capability descriptor + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); + } + } +} diff --git a/libjava/classpath/java/awt/Button.java b/libjava/classpath/java/awt/Button.java new file mode 100644 index 00000000000..90be1e5b111 --- /dev/null +++ b/libjava/classpath/java/awt/Button.java @@ -0,0 +1,466 @@ +/* Button.java -- AWT button widget + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.ButtonPeer; +import java.lang.reflect.Array; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleValue; + +/** + * This class provides a button widget for the AWT. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class Button extends Component + implements java.io.Serializable, Accessible +{ + +/* + * Static Variables + */ + +// FIXME: Need readObject/writeObject for serialization + +// Serialization version constant +private static final long serialVersionUID = -8774683716313001058L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The action command name for this button. + * This is package-private to avoid an accessor method. + */ +String actionCommand; + +/** + * @serial The label for this button. + * This is package-private to avoid an accessor method. + */ +String label; + +// List of ActionListeners for this class. +private transient ActionListener action_listeners; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_button_number; + + protected class AccessibleAWTButton extends AccessibleAWTComponent + implements AccessibleAction, AccessibleValue + { + protected AccessibleAWTButton() + { + // Do nothing here. + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + // Only 1 action possible + return 1; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + // JDK 1.4.2 returns the string "click" for action 0. However, the API + // docs don't say what the string to be returned is, beyond being a + // description of the action. So we return the same thing for + // compatibility with 1.4.2. + if (i == 0) + return "click"; + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i != 0) + return false; + processActionEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand)); + return true; + } + + public String getAccessibleName() + { + return label; + } + + public AccessibleAction getAccessibleAction() + { + return this; + } + + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + // Docs say return 1 if selected, but buttons can't be selected, right? + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + // Since there's no selection with buttons, we're ignoring this. + // TODO someone who knows shoulw check this. + return false; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(0); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PUSH_BUTTON; + } + } + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Button</code> with no label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ +public +Button() +{ + this(""); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Button</code> with the specified + * label. The action command name is also initialized to this value. + * + * @param label The label to display on the button. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ +public +Button(String label) +{ + this.label = label; + actionCommand = label; + + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the label for this button. + * + * @return The label for this button. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * Sets the label for this button to the specified value. + * + * @param label The new label for this button. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + actionCommand = label; + if (peer != null) + { + ButtonPeer bp = (ButtonPeer) peer; + bp.setLabel (label); + } +} + +/*************************************************************************/ + +/** + * Returns the action command name for this button. + * + * @return The action command name for this button. + */ +public String +getActionCommand() +{ + return(actionCommand); +} + +/*************************************************************************/ + +/** + * Sets the action command name for this button to the specified value. + * + * @param actionCommand The new action command name. + */ +public void +setActionCommand(String actionCommand) +{ + this.actionCommand = actionCommand == null ? label : actionCommand; +} + +/*************************************************************************/ + +/** + * Adds a new entry to the list of listeners that will receive + * action events from this button. + * + * @param listener The listener to add. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of listeners that will + * receive action events from this button. + * + * @param listener The listener to remove. + */ +public synchronized void +removeActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); +} + + /** + * Returns all added <code>ActionListener</code> objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public synchronized ActionListener[] getActionListeners() + { + return (ActionListener[]) + AWTEventMulticaster.getListeners(action_listeners, + ActionListener.class); + } + +/** + * Returns all registered EventListers of the given listenerType. + * listenerType must be a subclass of EventListener, or a + * ClassClassException is thrown. + * + * @param listenerType the listener type to return + * + * @return an array of listeners + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements @see java.util.EventListener. + * + * @since 1.3 + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == ActionListener.class) + return getActionListeners(); + return (EventListener[]) Array.newInstance(listenerType, 0); + } + +/*************************************************************************/ + +/** + * Notifies this button that it should create its native peer object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createButton (this); + super.addNotify(); +} + +/*************************************************************************/ + +/** + * Processes an event for this button. If the specified event is an + * instance of <code>ActionEvent</code>, then the + * <code>processActionEvent()</code> method is called to dispatch it + * to any registered listeners. Otherwise, the superclass method + * will be invoked. Note that this method will not be called at all + * unless <code>ActionEvent</code>'s are enabled. This will be done + * implicitly if any listeners are added. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * This method dispatches an action event for this button to any + * registered listeners. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + action_listeners.actionPerformed(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this button. + * + * @return A debugging string for this button. + */ +protected String +paramString() +{ + return getName () + "," + getX () + "," + getY () + "," + + getWidth () + "x" + getHeight () + ",label=" + getLabel (); +} + +/** + * Gets the AccessibleContext associated with this <code>Button</code>. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTButton(); + return accessibleContext; +} + + /** + * Generate a unique name for this button. + * + * @return A unique name for this button. + */ + String generateName () + { + return "button" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_button_number++; + } + +} // class Button + diff --git a/libjava/classpath/java/awt/Canvas.java b/libjava/classpath/java/awt/Canvas.java new file mode 100644 index 00000000000..fe2f854847a --- /dev/null +++ b/libjava/classpath/java/awt/Canvas.java @@ -0,0 +1,354 @@ +/* Canvas.java -- + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.BufferStrategy; +import java.awt.peer.ComponentPeer; +import java.io.Serializable; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * The <code>Canvas</code> component provides a blank rectangular + * area, which the client application can use for drawing and for + * capturing events. By overriding the <code>paint()</code> method, + * the canvas can be used for anything from simple line drawings to + * full-scale custom components. + * + * @author Original author unknown + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + */ + +public class Canvas + extends Component + implements Serializable, Accessible +{ + + /** + * Compatible with Sun's JDK. + */ + private static final long serialVersionUID = -2284879212465893870L; + + /** + * The graphics configuration associated with the canvas. + */ + transient GraphicsConfiguration graphicsConfiguration; + + /** + * The buffer strategy associated with this canvas. + */ + transient BufferStrategy bufferStrategy; + + /** + * Initializes a new instance of <code>Canvas</code>. + */ + public Canvas() + { + } + + /** + * Initializes a new instance of <code>Canvas</code> + * with the supplied graphics configuration. + * + * @param graphicsConfiguration the graphics configuration to use + * for this particular canvas. + */ + public Canvas(GraphicsConfiguration graphicsConfiguration) + { + this.graphicsConfiguration = graphicsConfiguration; + } + + GraphicsConfiguration getGraphicsConfigurationImpl() + { + if (graphicsConfiguration != null) + return graphicsConfiguration; + return super.getGraphicsConfigurationImpl(); + } + + /** + * Creates the native peer for this object. + */ + public void addNotify() + { + if (peer == null) + peer = (ComponentPeer) getToolkit().createCanvas(this); + super.addNotify(); + } + + /** + * Repaints the canvas window. This method should be overridden by + * a subclass to do something useful, as this method simply paints + * the window with the background color. + * + * @param gfx the <code>Graphics</code> to use for painting + */ + public void paint(Graphics gfx) + { + /* This implementation doesn't make much sense since the filling + of background color is guaranteed for heavyweight components + such as this. But there's no need to worry, since paint() is + usually overridden anyway. */ + gfx.setColor(getBackground()); + Dimension size = getSize(); + gfx.fillRect(0, 0, size.width, size.height); + } + + /** + * This class provides accessibility support for the canvas. + */ + protected class AccessibleAWTCanvas + extends AccessibleAWTComponent + { + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -6325592262103146699L; + + /** + * Constructor for the accessible canvas. + */ + protected AccessibleAWTCanvas() + { + } + + /** + * Returns the accessible role for the canvas. + * + * @return an instance of <code>AccessibleRole</code>, describing + * the role of the canvas. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.CANVAS; + } + + } + + /** + * Gets the AccessibleContext associated with this <code>Canvas</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTCanvas(); + return accessibleContext; + } + + /** + * A BltBufferStrategy for canvases. + */ + private class CanvasBltBufferStrategy extends BltBufferStrategy + { + /** + * Creates a block transfer strategy for this canvas. + * + * @param numBuffers the number of buffers in this strategy + * @param accelerated true if the buffer should be accelerated, + * false otherwise + */ + CanvasBltBufferStrategy(int numBuffers, boolean accelerated) + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(accelerated), + new ImageCapabilities(accelerated), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * A FlipBufferStrategy for canvases. + */ + private class CanvasFlipBufferStrategy extends FlipBufferStrategy + { + /** + * Creates a flip buffer strategy for this canvas. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws AWTException if the requested number of buffers is not + * supported + */ + CanvasFlipBufferStrategy(int numBuffers) + throws AWTException + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * Creates a buffering strategy that manages how this canvas is + * repainted. This method attempts to create the optimum strategy + * based on the desired number of buffers. Hardware or software + * acceleration may be used. + * + * createBufferStrategy attempts different levels of optimization, + * but guarantees that some strategy with the requested number of + * buffers will be created even if it is not optimal. First it + * attempts to create a page flipping strategy, then an accelerated + * blitting strategy, then an unaccelerated blitting strategy. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws IllegalArgumentException if requested number of buffers + * is less than one + * @throws IllegalStateException if this canvas is not displayable + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Canvas.createBufferStrategy: number" + + " of buffers is less than one"); + + if (!isDisplayable()) + throw new IllegalStateException("Canvas.createBufferStrategy: canvas is" + + " not displayable"); + + BufferStrategy newStrategy = null; + + // try a flipping strategy + try + { + newStrategy = new CanvasFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + + // fall back to an accelerated blitting strategy + if (newStrategy == null) + newStrategy = new CanvasBltBufferStrategy(numBuffers, true); + + bufferStrategy = newStrategy; + } + + /** + * Creates a buffering strategy that manages how this canvas is + * repainted. This method attempts to create a strategy based on + * the specified capabilities and throws an exception if the + * requested strategy is not supported. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * @param caps the requested buffering capabilities + * + * @throws AWTException if the requested capabilities are not + * supported + * @throws IllegalArgumentException if requested number of buffers + * is less than one or if caps is null + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers, + BufferCapabilities caps) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Canvas.createBufferStrategy: number" + + " of buffers is less than one"); + + if (caps == null) + throw new IllegalArgumentException("Canvas.createBufferStrategy:" + + " capabilities object is null"); + + // a flipping strategy was requested + if (caps.isPageFlipping()) + { + try + { + bufferStrategy = new CanvasFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + } + else + bufferStrategy = new CanvasBltBufferStrategy(numBuffers, true); + } + + /** + * Returns the buffer strategy used by the canvas. + * + * @return the buffer strategy. + * @since 1.4 + */ + public BufferStrategy getBufferStrategy() + { + return bufferStrategy; + } + + /** + * Updates the canvas in response to a request to + * <code>repaint()</code> it. The canvas is cleared + * with the current background colour, before <code>paint()</code> + * is called to add the new contents. Subclasses + * which override this method should either call this + * method via <code>super.update(graphics)</code> or re-implement + * this behaviour, so as to ensure that the canvas is + * clear before painting takes place. + * + * @param graphics the graphics context. + */ + public void update(Graphics graphics) + { + Dimension size; + + /* Clear the canvas */ + size = getSize(); + graphics.clearRect(0, 0, size.width, size.height); + /* Call the paint method */ + paint(graphics); + } +} diff --git a/libjava/classpath/java/awt/CardLayout.java b/libjava/classpath/java/awt/CardLayout.java new file mode 100644 index 00000000000..8582c5f099b --- /dev/null +++ b/libjava/classpath/java/awt/CardLayout.java @@ -0,0 +1,483 @@ +/* CardLayout.java -- Card-based layout engine + Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * This class implements a card-based layout scheme. Each included + * component is treated as a card. Only one card can be shown at a + * time. This class includes methods for changing which card is + * shown. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class CardLayout implements LayoutManager2, Serializable +{ + private static final long serialVersionUID = -4328196481005934313L; + + /** + * Initializes a new instance of <code>CardLayout</code> with horizontal + * and vertical gaps of 0. + */ + public CardLayout () + { + this (0, 0); + } + + /** + * Create a new <code>CardLayout</code> object with the specified + * horizontal and vertical gaps. + * + * @param hgap The horizontal gap + * @param vgap The vertical gap + */ + public CardLayout (int hgap, int vgap) + { + this.hgap = hgap; + this.vgap = vgap; + this.tab = new Hashtable (); + } + + /** + * Add a new component to the layout. The constraint must be a + * string which is used to name the component. This string can + * later be used to refer to the particular component. + * + * @param comp The component to add + * @param constraints The name by which the component can later be called + * + * @exception IllegalArgumentException If `constraints' is not a + * <code>String</code> + */ + public void addLayoutComponent (Component comp, Object constraints) + { + if (! (constraints instanceof String)) + throw new IllegalArgumentException ("Object " + constraints + + " is not a string"); + addLayoutComponent ((String) constraints, comp); + } + + /** + * Add a new component to the layout. The name can be used later + * to refer to the component. + * + * @param name The name by which the component can later be called + * @param comp The component to add + * + * @deprecated This method is deprecated in favor of + * <code>addLayoutComponent(Component, Object)</code>. + */ + public void addLayoutComponent (String name, Component comp) + { + tab.put (name, comp); + // First component added is the default component. + comp.setVisible(tab.size() == 1); + } + + /** + * Cause the first component in the container to be displayed. + * + * @param parent The parent container + */ + public void first (Container parent) + { + gotoComponent (parent, FIRST); + } + + /** + * Return this layout manager's horizontal gap. + * + * @return the horizontal gap + */ + public int getHgap () + { + return hgap; + } + + /** + * Return this layout manager's x alignment. This method always + * returns Component.CENTER_ALIGNMENT. + * + * @param parent Container using this layout manager instance + * + * @return the x-axis alignment + */ + public float getLayoutAlignmentX (Container parent) + { + return Component.CENTER_ALIGNMENT; + } + + /** + * Returns this layout manager's y alignment. This method always + * returns Component.CENTER_ALIGNMENT. + * + * @param parent Container using this layout manager instance + * + * @return the y-axis alignment + */ + public float getLayoutAlignmentY (Container parent) + { + return Component.CENTER_ALIGNMENT; + } + + /** + * Return this layout manager's vertical gap. + * + * @return the vertical gap + */ + public int getVgap () + { + return vgap; + } + + /** + * Invalidate this layout manager's state. + */ + public void invalidateLayout (Container target) + { + // Do nothing. + } + + /** + * Cause the last component in the container to be displayed. + * + * @param parent The parent container + */ + public void last (Container parent) + { + gotoComponent (parent, LAST); + } + + /** + * Lays out the container. This is done by resizing the child components + * to be the same size as the parent, less insets and gaps. + * + * @param parent The parent container. + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int width = parent.width; + int height = parent.height; + + Insets ins = parent.getInsets (); + + int num = parent.ncomponents; + Component[] comps = parent.component; + + int x = ins.left + hgap; + int y = ins.top + vgap; + width = width - 2 * hgap - ins.left - ins.right; + height = height - 2 * vgap - ins.top - ins.bottom; + + for (int i = 0; i < num; ++i) + comps[i].setBounds (x, y, width, height); + } + } + + /** + * Get the maximum layout size of the container. + * + * @param target The parent container + * + * @return the maximum layout size + */ + public Dimension maximumLayoutSize (Container target) + { + // The JCL says that this returns Integer.MAX_VALUE for both + // dimensions. But that just seems wrong to me. + return getSize (target, MAX); + } + + /** + * Get the minimum layout size of the container. + * + * @param target The parent container + * + * @return the minimum layout size + */ + public Dimension minimumLayoutSize (Container target) + { + return getSize (target, MIN); + } + + /** + * Cause the next component in the container to be displayed. If + * this current card is the last one in the deck, the first + * component is displayed. + * + * @param parent The parent container + */ + public void next (Container parent) + { + gotoComponent (parent, NEXT); + } + + /** + * Get the preferred layout size of the container. + * + * @param parent The parent container + * + * @return the preferred layout size + */ + public Dimension preferredLayoutSize (Container parent) + { + return getSize (parent, PREF); + } + + /** + * Cause the previous component in the container to be displayed. + * If this current card is the first one in the deck, the last + * component is displayed. + * + * @param parent The parent container + */ + public void previous (Container parent) + { + gotoComponent (parent, PREV); + } + + /** + * Remove the indicated component from this layout manager. + * + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + Enumeration e = tab.keys (); + while (e.hasMoreElements ()) + { + Object key = e.nextElement (); + if (tab.get (key) == comp) + { + tab.remove (key); + Container parent = comp.getParent(); + next(parent); + break; + } + } + } + + /** + * Set this layout manager's horizontal gap. + * + * @param hgap The new gap + */ + public void setHgap (int hgap) + { + this.hgap = hgap; + } + + /** + * Set this layout manager's vertical gap. + * + * @param vgap The new gap + */ + public void setVgap (int vgap) + { + this.vgap = vgap; + } + + /** + * Cause the named component to be shown. If the component name is + * unknown, this method does nothing. + * + * @param parent The parent container + * @param name The name of the component to show + */ + public void show (Container parent, String name) + { + Object target = tab.get (name); + if (target != null) + { + int num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + for (int i = 0; i < num; ++i) + { + if (comps[i].isVisible()) + { + if (target == comps[i]) + return; + comps[i].setVisible (false); + } + } + ((Component) target).setVisible (true); + } + } + + /** + * Returns a string representation of this layout manager. + * + * @return A string representation of this object. + */ + public String toString () + { + return getClass ().getName () + "[" + hgap + "," + vgap + "]"; + } + + /** + * This implements first(), last(), next(), and previous(). + * + * @param parent The parent container + * @param what The type of goto: FIRST, LAST, NEXT or PREV + */ + private void gotoComponent (Container parent, int what) + { + synchronized (parent.getTreeLock ()) + { + int num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + if (num == 1) + { + comps[0].setVisible(true); + return; + } + + int choice = -1; + + if (what == FIRST) + choice = 0; + else if (what == LAST) + choice = num - 1; + + for (int i = 0; i < num; ++i) + { + if (comps[i].isVisible ()) + { + if (what == NEXT) + { + choice = i + 1; + if (choice == num) + choice = 0; + } + else if (what == PREV) + { + choice = i - 1; + if (choice < 0) + choice = num - 1; + } + else if (choice == i) + { + // Do nothing if we're already looking at the right + // component. + return; + } + comps[i].setVisible (false); + + if (choice >= 0) + break; + } + } + + if (choice >= 0 && choice < num) + comps[choice].setVisible (true); + } + } + + // Compute the size according to WHAT. + private Dimension getSize (Container parent, int what) + { + synchronized (parent.getTreeLock ()) + { + int w = 0, h = 0, num = parent.ncomponents; + Component[] comps = parent.component; + + for (int i = 0; i < num; ++i) + { + Dimension d; + + if (what == MIN) + d = comps[i].getMinimumSize (); + else if (what == MAX) + d = comps[i].getMaximumSize (); + else + d = comps[i].getPreferredSize (); + + w = Math.max (d.width, w); + h = Math.max (d.height, h); + } + + Insets i = parent.getInsets (); + w += 2 * hgap + i.right + i.left; + h += 2 * vgap + i.bottom + i.top; + + // Handle overflow. + if (w < 0) + w = Integer.MAX_VALUE; + if (h < 0) + h = Integer.MAX_VALUE; + + return new Dimension (w, h); + } + } + + /** + * @serial Horizontal gap value. + */ + private int hgap; + + /** + * @serial Vertical gap value. + */ + private int vgap; + + /** + * @serial Table of named components. + */ + private Hashtable tab; + + // These constants are used by the private gotoComponent method. + private static final int FIRST = 0; + private static final int LAST = 1; + private static final int NEXT = 2; + private static final int PREV = 3; + + // These constants are used by the private getSize method. + private static final int MIN = 0; + private static final int MAX = 1; + private static final int PREF = 2; +} diff --git a/libjava/classpath/java/awt/Checkbox.java b/libjava/classpath/java/awt/Checkbox.java new file mode 100644 index 00000000000..cd39ad43617 --- /dev/null +++ b/libjava/classpath/java/awt/Checkbox.java @@ -0,0 +1,649 @@ +/* Checkbox.java -- An AWT checkbox widget + Copyright (C) 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.CheckboxPeer; +import java.io.Serializable; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a component which has an on/off state. Two + * or more Checkboxes can be grouped by a CheckboxGroup. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class Checkbox extends Component + implements ItemSelectable, Accessible, Serializable +{ + +// FIXME: Need readObject/writeObject for this. + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = 7270714317450821763L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The checkbox group for this checkbox. + */ +private CheckboxGroup group; + +/** + * @serial The label on this checkbox. + */ +private String label; + +/** + * @serial The state of this checkbox. + * This is package-private to avoid an accessor method. + */ +boolean state; + +// The list of listeners for this object. +private transient ItemListener item_listeners; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_checkbox_number; + +/** + * This class provides accessibility support for the + * checkbox. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +protected class AccessibleAWTCheckbox + extends AccessibleAWTComponent + implements ItemListener, AccessibleAction, AccessibleValue +{ + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = 7881579233144754107L; + + /** + * Default constructor which simply calls the + * super class for generic component accessibility + * handling. + */ + public AccessibleAWTCheckbox() + { + super(); + } + + /** + * Captures changes to the state of the checkbox and + * fires appropriate accessible property change events. + * + * @param event the event fired. + * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) + */ + public void itemStateChanged(ItemEvent event) + { + firePropertyChange(ACCESSIBLE_STATE_PROPERTY, + state ? null : AccessibleState.CHECKED, + state ? AccessibleState.CHECKED : null); + } + + /** + * Returns an implementation of the <code>AccessibleAction</code> + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible action as well as + * the context. + * + * @return the accessible action associated with this context. + * @see javax.accessibility.AccessibleAction + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns an implementation of the <code>AccessibleValue</code> + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible value as well as + * the context. + * + * @return the accessible value associated with this context. + * @see javax.accessibility.AccessibleValue + */ + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* + * The following methods are implemented in the JDK (up to + * 1.5) as stubs. We do likewise here. + */ + + /** + * Returns the number of actions associated with this accessible + * object. This default implementation returns 0. + * + * @return the number of accessible actions available. + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + // 1.4.1 and 1.5 do this + return 0; + } + + /** + * Returns a description of the action with the supplied id. + * This default implementation always returns null. + * + * @param i the id of the action whose description should be + * retrieved. + * @return a <code>String</code> describing the action. + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + // 1.5 does this + return null; + } + + /** + * Executes the action with the specified id. This + * default implementation simply returns false. + * + * @param i the id of the action to perform. + * @return true if the action was performed. + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + // 1.5 does this + return false; + } + + /** + * Returns the current value of this accessible object. + * If no value has been set, null is returned. This + * default implementation always returns null, regardless. + * + * @return the numeric value of this object, or null if + * no value has been set. + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + // 1.5 does this + return null; + } + + /** + * Sets the current value of this accessible object + * to that supplied. In this default implementation, + * the value is never set and the method always returns + * false. + * + * @param number the new accessible value. + * @return true if the value was set. + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + // 1.5 does this + return false; + } + + /** + * Returns the minimum acceptable accessible value used + * by this object, or null if no minimum value exists. + * This default implementation always returns null. + * + * @return the minimum acceptable accessible value, or null + * if there is no minimum. + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return null; + } + + /** + * Returns the maximum acceptable accessible value used + * by this object, or null if no maximum value exists. + * This default implementation always returns null. + * + * @return the maximum acceptable accessible value, or null + * if there is no maximum. + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return null; + } + + /** + * Returns the role of this accessible object. + * + * @return the instance of <code>AccessibleRole</code>, + * which describes this object. + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.CHECK_BOX; + } + + /** + * Returns the state set of this accessible object. + * + * @return a set of <code>AccessibleState</code>s + * which represent the current state of the + * accessible object. + * @see javax.accessibility.AccessibleState + * @see javax.accessibility.AccessibleStateSet + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet set = super.getAccessibleStateSet(); + if (state) + set.add(AccessibleState.CHECKED); + return set; + } + +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Checkbox</code> with no label, + * an initial state of off, and that is not part of any checkbox group. + */ +public +Checkbox() +{ + this("", false, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Checkbox</code> with the specified + * label, an initial state of off, and that is not part of any checkbox + * group. + * + * @param label The label for this checkbox. + */ +public +Checkbox(String label) +{ + this(label, false, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Checkbox</code> with the specified + * label and initial state, and that is not part of any checkbox + * group. + * + * @param label The label for this checkbox. + * @param state The initial state of the checkbox, <code>true</code> for + * on, <code>false</code> for off. + */ +public +Checkbox(String label, boolean state) +{ + this(label, state, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Checkbox</code> with the specified + * label, initial state, and checkbox group. + * + * @param label The label for this checkbox. + * @param group The checkbox group for this box, or <code>null</code> + * if there is no checkbox group. + * @param state The initial state of the checkbox, <code>true</code> for + * on, <code>false</code> for off. + */ +public +Checkbox(String label, CheckboxGroup group, boolean state) +{ + this(label, state, group); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Checkbox</code> with the specified + * label, initial state, and checkbox group. + * + * @param label The label for this checkbox. + * @param state The initial state of the checkbox, <code>true</code> for + * on, <code>false</code> for off. + * @param group The checkbox group for this box, or <code>null</code> + * if there is no checkbox group. + */ +public +Checkbox(String label, boolean state, CheckboxGroup group) +{ + this.label = label; + this.state = state; + this.group = group; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the label for this checkbox. + * + * @return The label for this checkbox. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * Sets the label for this checkbox to the specified value. + * + * @param label The new checkbox label. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setLabel(label); + } +} + +/*************************************************************************/ + +/** + * Returns the state of this checkbox. + * + * @return The state of this checkbox, which will be <code>true</code> for + * on and <code>false</code> for off. + */ +public boolean +getState() +{ + return(state); +} + +/*************************************************************************/ + +/** + * Sets the state of this checkbox to the specified value. + * + * @param state The new state of the checkbox, which will be <code>true</code> + * for on or <code>false</code> for off. + */ +public synchronized void +setState(boolean state) +{ + this.state = state; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setState (state); + } +} + +/*************************************************************************/ + +/** + * Returns an array of length one containing the checkbox label if this + * checkbox is selected. Otherwise <code>null</code> is returned. + * + * @return The selection state of this checkbox. + */ +public Object[] +getSelectedObjects() +{ + if (state == false) + return(null); + + Object[] objs = new Object[1]; + objs[0] = label; + + return(objs); +} + +/*************************************************************************/ + +/** + * Returns the checkbox group this object is a member of, if any. + * + * @return This object's checkbox group, of <code>null</code> if it is + * not a member of any group. + */ +public CheckboxGroup +getCheckboxGroup() +{ + return(group); +} + +/*************************************************************************/ + +/** + * Sets this object's checkbox group to the specified group. + * + * @param group The new checkbox group, or <code>null</code> to make this + * object part of no checkbox group. + */ +public synchronized void +setCheckboxGroup(CheckboxGroup group) +{ + this.group = group; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setCheckboxGroup (group); + } +} + +/*************************************************************************/ + +/** + * Creates this object's native peer. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createCheckbox (this); + super.addNotify (); +} + + public ItemListener[] getItemListeners () + { + return (ItemListener[]) + AWTEventMulticaster.getListeners (item_listeners, ItemListener.class); + } + +/** + * Adds a new listeners to the list of registered listeners for this object. + * + * @param listener The new listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes a listener from the list of registered listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes this event by calling <code>processItemEvent()</code> if it + * is any instance of <code>ItemEvent</code>. Otherwise it is passed to + * the superclass for processing. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes this event by dispatching it to any registered listeners. + * + * @param event The <code>ItemEvent</code> to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + */ +protected String +paramString() +{ + return ("label=" + label + ",state=" + state + ",group=" + group + + "," + super.paramString()); +} + +/** + * Gets the AccessibleContext associated with this <code>Checkbox</code>. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + { + AccessibleAWTCheckbox ac = new AccessibleAWTCheckbox(); + accessibleContext = ac; + addItemListener(ac); + } + return accessibleContext; +} + + /** + * Generate a unique name for this checkbox. + * + * @return A unique name for this checkbox. + */ + String generateName() + { + return "checkbox" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_checkbox_number++; + } +} diff --git a/libjava/classpath/java/awt/CheckboxGroup.java b/libjava/classpath/java/awt/CheckboxGroup.java new file mode 100644 index 00000000000..31b573e654d --- /dev/null +++ b/libjava/classpath/java/awt/CheckboxGroup.java @@ -0,0 +1,173 @@ +/* CheckboxGroup.java -- A grouping class for checkboxes. + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class if for combining checkboxes into groups so that only + * one checkbox in the group can be selected at any one time. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class CheckboxGroup implements java.io.Serializable +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = 3729780091441768983L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The currently selected checkbox. + */ +private Checkbox selectedCheckbox; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>CheckboxGroup</code>. + */ +public +CheckboxGroup() +{ +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the currently selected checkbox, or <code>null</code> if none + * of the checkboxes in this group are selected. + * + * @return The selected checkbox. + */ +public Checkbox +getSelectedCheckbox() +{ + return getCurrent (); +} + +/*************************************************************************/ + +/** + * Returns the currently selected checkbox, or <code>null</code> if none + * of the checkboxes in this group are selected. + * + * @return The selected checkbox. + * + * @deprecated This method is deprecated in favor of + * <code>getSelectedCheckbox()</code>. + */ +public Checkbox +getCurrent() +{ + return(selectedCheckbox); +} + +/*************************************************************************/ + +/** + * This method sets the specified checkbox to be the selected on in this + * group, and unsets all others. + * + * @param selectedCheckbox The new selected checkbox. + */ +public void +setSelectedCheckbox(Checkbox selectedCheckbox) +{ + setCurrent (selectedCheckbox); +} + +/*************************************************************************/ + +/** + * This method sets the specified checkbox to be the selected on in this + * group, and unsets all others. + * + * @param selectedCheckbox The new selected checkbox. + * + * @deprecated This method is deprecated in favor of + * <code>setSelectedCheckbox()</code>. + */ +public void +setCurrent(Checkbox selectedCheckbox) +{ + if (this.selectedCheckbox != null) + { + if (this.selectedCheckbox.getCheckboxGroup() != this) + return; + + this.selectedCheckbox.setState(false); + } + + this.selectedCheckbox = selectedCheckbox; + if (selectedCheckbox != null) + selectedCheckbox.setState(true); +} + +/*************************************************************************/ + +/** + * Returns a string representation of this checkbox group. + * + * @return A string representation of this checkbox group. + */ +public String +toString() +{ + return(getClass().getName() + "[selectedCheckbox=" + selectedCheckbox + "]"); +} + +} // class CheckboxGroup + diff --git a/libjava/classpath/java/awt/CheckboxMenuItem.java b/libjava/classpath/java/awt/CheckboxMenuItem.java new file mode 100644 index 00000000000..5e446b84c8b --- /dev/null +++ b/libjava/classpath/java/awt/CheckboxMenuItem.java @@ -0,0 +1,355 @@ +/* CheckboxMenuItem.java -- A menu option with a checkbox on it. + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.CheckboxMenuItemPeer; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a menu item that has a checkbox on it indicating + * the selected state of some option. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class CheckboxMenuItem extends MenuItem + implements ItemSelectable, Accessible +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = 6190621106981774043L; + +/* + * Instance Variables + */ + +/** + * @serial The state of the checkbox, with <code>true</code> being on and + * <code>false</code> being off. + */ +private boolean state; + +// List of registered ItemListeners +private transient ItemListener item_listeners; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>CheckboxMenuItem</code> with no + * label and an initial state of off. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem() +{ + this("", false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>CheckboxMenuItem</code> with the + * specified label and an initial state of off. + * + * @param label The label of the menu item. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem(String label) +{ + this(label, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>CheckboxMenuItem</code> with the + * specified label and initial state. + * + * @param label The label of the menu item. + * @param state The initial state of the menu item, where <code>true</code> + * is on, and <code>false</code> is off. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem(String label, boolean state) +{ + super(label); + this.state = state; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the state of this menu item. + * + * @return The state of this menu item. + */ +public boolean +getState() +{ + return(state); +} + +/*************************************************************************/ + +/** + * Sets the state of this menu item. + * + * @param state The initial state of the menu item, where <code>true</code> + * is on, and <code>false</code> is off. + */ +public synchronized void +setState(boolean state) +{ + this.state = state; + if (peer != null) + { + CheckboxMenuItemPeer cp = (CheckboxMenuItemPeer) peer; + cp.setState (state); + } +} + +/*************************************************************************/ + +/** + * Returns an array of length 1 with the menu item label for this object + * if the state is on. Otherwise <code>null</code> is returned. + * + * @return An array with this menu item's label if it has a state of on, + * or <code>null</code> otherwise. + */ +public Object[] +getSelectedObjects() +{ + if (state == false) + return(null); + + Object[] obj = new Object[1]; + obj[0] = getLabel(); + + return(obj); +} + +/*************************************************************************/ + +/** + * Create's this object's native peer + */ +public synchronized void +addNotify() +{ + if (peer == null) + peer = getToolkit().createCheckboxMenuItem(this); + + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Adds the specified listener to the list of registered item listeners + * for this object. + * + * @param listener The listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); + + enableEvents(AWTEvent.ITEM_EVENT_MASK); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of registered item + * listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes the specified event by calling <code>processItemEvent()</code> + * if it is an instance of <code>ItemEvent</code> or calling the superclass + * method otherwise. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes the specified event by dispatching it to any registered listeners. + * + * @param event The event to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e instanceof ItemEvent) + { + synchronized (this) + { + state = (((ItemEvent) e).getStateChange() == ItemEvent.SELECTED); + } + } + + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +public String +paramString() +{ + return ("label=" + getLabel() + ",state=" + state + + "," + super.paramString()); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this <code>CheckboxMenuItem</code>. FooListeners are registered using + * the addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns an aray of all item listeners currently registered to this + * <code>CheckBoxMenuItem</code>. + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + + protected class AccessibleAWTCheckboxMenuItem extends AccessibleAWTMenuItem + implements AccessibleAction, AccessibleValue + { + // I think the base class provides the necessary implementation + } + + /** + * Gets the AccessibleContext associated with this <code>CheckboxMenuItem</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTCheckboxMenuItem(); + return accessibleContext; + } + +} // class CheckboxMenuItem + diff --git a/libjava/classpath/java/awt/Choice.java b/libjava/classpath/java/awt/Choice.java new file mode 100644 index 00000000000..5075ea92d83 --- /dev/null +++ b/libjava/classpath/java/awt/Choice.java @@ -0,0 +1,638 @@ +/* Choice.java -- Java choice button widget. + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.ChoicePeer; +import java.io.Serializable; +import java.util.EventListener; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implements a drop down choice list. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Choice extends Component + implements ItemSelectable, Serializable, Accessible +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = -4075310674757313071L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial A list of items for the choice box, which can be <code>null</code>. + * This is package-private to avoid an accessor method. + */ +Vector pItems = new Vector(); + +/** + * @serial The index of the selected item in the choice box. + */ +private int selectedIndex = -1; + +// Listener chain +private ItemListener item_listeners; + +/** + * This class provides accessibility support for the + * combo box. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected class AccessibleAWTChoice + extends AccessibleAWTComponent + implements AccessibleAction + { + + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = 7175603582428509322L; + + /** + * Default constructor which simply calls the + * super class for generic component accessibility + * handling. + */ + public AccessibleAWTChoice() + { + super(); + } + + /** + * Returns an implementation of the <code>AccessibleAction</code> + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible action as well as + * the context. + * + * @return the accessible action associated with this context. + * @see javax.accessibility.AccessibleAction + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns the role of this accessible object. + * + * @return the instance of <code>AccessibleRole</code>, + * which describes this object. + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.COMBO_BOX; + } + + /** + * Returns the number of actions associated with this accessible + * object. In this case, it is the number of choices available. + * + * @return the number of choices available. + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + return pItems.size(); + } + + /** + * Returns a description of the action with the supplied id. + * In this case, it is the text used in displaying the particular + * choice on-screen. + * + * @param i the id of the choice whose description should be + * retrieved. + * @return the <code>String</code> used to describe the choice. + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + return (String) pItems.get(i); + } + + /** + * Executes the action with the specified id. In this case, + * calling this method provides the same behaviour as would + * choosing a choice from the list in a visual manner. + * + * @param i the id of the choice to select. + * @return true if a valid choice was specified. + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i < 0 || i >= pItems.size()) + return false; + + Choice.this.processItemEvent(new ItemEvent(Choice.this, + ItemEvent.ITEM_STATE_CHANGED, + this, ItemEvent.SELECTED)); + return true; + } + } + +/*************************************************************************/ + +/* + * Constructors + */ + + /** + * Initializes a new instance of <code>Choice</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ + public Choice() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + } + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the number of items in the list. + * + * @return The number of items in the list. + */ +public int +getItemCount() +{ + return countItems (); +} + +/*************************************************************************/ + +/** + * Returns the number of items in the list. + * + * @return The number of items in the list. + * + * @deprecated This method is deprecated in favor of <code>getItemCount</code>. + */ +public int +countItems() +{ + return(pItems.size()); +} + +/*************************************************************************/ + +/** + * Returns the item at the specified index in the list. + * + * @param index The index into the list to return the item from. + * + * @exception ArrayIndexOutOfBoundsException If the index is invalid. + */ +public String +getItem(int index) +{ + return((String)pItems.elementAt(index)); +} + +/*************************************************************************/ + +/** + * Adds the specified item to this choice box. + * + * @param item The item to add. + * + * @exception NullPointerException If the item's value is null + * + * @since 1.1 + */ +public synchronized void +add(String item) +{ + if (item == null) + throw new NullPointerException ("item must be non-null"); + + pItems.addElement(item); + + int i = pItems.size () - 1; + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.add (item, i); + } + else if (selectedIndex == -1) + select(0); +} + +/*************************************************************************/ + +/** + * Adds the specified item to this choice box. + * + * This method is oboslete since Java 2 platform 1.1. Please use @see add + * instead. + * + * @param item The item to add. + * + * @exception NullPointerException If the item's value is equal to null + */ +public synchronized void +addItem(String item) +{ + add(item); +} + +/*************************************************************************/ + +/** Inserts an item into this Choice. Existing items are shifted + * upwards. If the new item is the only item, then it is selected. + * If the currently selected item is shifted, then the first item is + * selected. If the currently selected item is not shifted, then it + * remains selected. + * + * @param item The item to add. + * @param index The index at which the item should be inserted. + * + * @exception IllegalArgumentException If index is less than 0 + */ +public synchronized void +insert(String item, int index) +{ + if (index < 0) + throw new IllegalArgumentException ("index may not be less then 0"); + + if (index > getItemCount ()) + index = getItemCount (); + + pItems.insertElementAt(item, index); + + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.add (item, index); + } + else if (selectedIndex == -1 || selectedIndex >= index) + select(0); +} + +/*************************************************************************/ + +/** + * Removes the specified item from the choice box. + * + * @param item The item to remove. + * + * @exception IllegalArgumentException If the specified item doesn't exist. + */ +public synchronized void +remove(String item) +{ + int index = pItems.indexOf(item); + if (index == -1) + throw new IllegalArgumentException ("item \"" + + item + "\" not found in Choice"); + remove(index); +} + +/*************************************************************************/ + +/** + * Removes the item at the specified index from the choice box. + * + * @param index The index of the item to remove. + * + * @exception IndexOutOfBoundsException If the index is not valid. + */ +public synchronized void +remove(int index) +{ + if ((index < 0) || (index > getItemCount())) + throw new IllegalArgumentException("Bad index: " + index); + + pItems.removeElementAt(index); + + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.remove (index); + } + else + { + if (getItemCount() == 0) + selectedIndex = -1; + else if (index == selectedIndex) + select(0); + } + + if (selectedIndex > index) + --selectedIndex; +} + +/*************************************************************************/ + +/** + * Removes all of the objects from this choice box. + */ +public synchronized void +removeAll() +{ + if (getItemCount() <= 0) + return; + + pItems.removeAllElements (); + + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.removeAll (); + } + + selectedIndex = -1; +} + +/*************************************************************************/ + +/** + * Returns the currently selected item, or null if no item is + * selected. + * + * @return The currently selected item. + */ +public synchronized String +getSelectedItem() +{ + return (selectedIndex == -1 + ? null + : ((String)pItems.elementAt(selectedIndex))); +} + +/*************************************************************************/ + +/** + * Returns an array with one row containing the selected item. + * + * @return An array containing the selected item. + */ +public synchronized Object[] +getSelectedObjects() +{ + if (selectedIndex == -1) + return null; + + Object[] objs = new Object[1]; + objs[0] = pItems.elementAt(selectedIndex); + + return(objs); +} + +/*************************************************************************/ + +/** + * Returns the index of the selected item. + * + * @return The index of the selected item. + */ +public int +getSelectedIndex() +{ + return(selectedIndex); +} + +/*************************************************************************/ + +/** + * Forces the item at the specified index to be selected. + * + * @param index The index of the row to make selected. + * + * @exception IllegalArgumentException If the specified index is invalid. + */ +public synchronized void +select(int index) +{ + if ((index < 0) || (index > getItemCount())) + throw new IllegalArgumentException("Bad index: " + index); + + this.selectedIndex = index; + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.select (index); + } +} + +/*************************************************************************/ + +/** + * Forces the named item to be selected. + * + * @param item The item to be selected. + * + * @exception IllegalArgumentException If the specified item does not exist. + */ +public synchronized void +select(String item) +{ + int index = pItems.indexOf(item); + if (index >= 0) + select(index); +} + +/*************************************************************************/ + +/** + * Creates the native peer for this object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createChoice (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Adds the specified listener to the list of registered listeners for + * this object. + * + * @param listener The listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of registered listeners for + * this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes this event by invoking <code>processItemEvent()</code> if the + * event is an instance of <code>ItemEvent</code>, otherwise the event + * is passed to the superclass. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes item event by dispatching to any registered listeners. + * + * @param event The event to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return ("selectedIndex=" + selectedIndex + "," + super.paramString()); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Choice. FooListeners are registered using the addFooListener + * method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns all registered item listeners. + * + * @since 1.4 + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + /** + * Gets the AccessibleContext associated with this <code>Choice</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTChoice(); + return accessibleContext; + } +} // class Choice diff --git a/libjava/classpath/java/awt/Color.java b/libjava/classpath/java/awt/Color.java new file mode 100644 index 00000000000..4ad46d0c07c --- /dev/null +++ b/libjava/classpath/java/awt/Color.java @@ -0,0 +1,1008 @@ +/* Color.java -- represents a color in Java + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.io.Serializable; + +/** + * This class represents a color value in the AWT system. It uses the sRGB + * (standard Red-Green-Blue) system, along with an alpha value ranging from + * transparent (0.0f or 0) and opaque (1.0f or 255). The color is not + * pre-multiplied by the alpha value an any of the accessor methods. Further + * information about sRGB can be found at + * <a href="http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html"> + * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html</a>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ColorSpace + * @see AlphaComposite + * @since 1.0 + * @status updated to 1.4 + */ +public class Color implements Paint, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 118526816881161077L; + + /** Constant for the color white: R=255, G=255, B=255. */ + public static final Color white = new Color(0xffffff, false); + + /** + * Constant for the color white: R=255, G=255, B=255. + * + * @since 1.4 + */ + public static final Color WHITE = white; + + /** Constant for the color light gray: R=192, G=192, B=192. */ + public static final Color lightGray = new Color(0xc0c0c0, false); + + /** + * Constant for the color light gray: R=192, G=192, B=192. + * + * @since 1.4 + */ + public static final Color LIGHT_GRAY = lightGray; + + /** Constant for the color gray: R=128, G=128, B=128. */ + public static final Color gray = new Color(0x808080, false); + + /** + * Constant for the color gray: R=128, G=128, B=128. + * + * @since 1.4 + */ + public static final Color GRAY = gray; + + /** Constant for the color dark gray: R=64, G=64, B=64. */ + public static final Color darkGray = new Color(0x404040, false); + + /** + * Constant for the color dark gray: R=64, G=64, B=64. + * + * @since 1.4 + */ + public static final Color DARK_GRAY = darkGray; + + /** Constant for the color black: R=0, G=0, B=0. */ + public static final Color black = new Color(0x000000, false); + + /** + * Constant for the color black: R=0, G=0, B=0. + * + * @since 1.4 + */ + public static final Color BLACK = black; + + /** Constant for the color red: R=255, G=0, B=0. */ + public static final Color red = new Color(0xff0000, false); + + /** + * Constant for the color red: R=255, G=0, B=0. + * + * @since 1.4 + */ + public static final Color RED = red; + + /** Constant for the color pink: R=255, G=175, B=175. */ + public static final Color pink = new Color(0xffafaf, false); + + /** + * Constant for the color pink: R=255, G=175, B=175. + * + * @since 1.4 + */ + public static final Color PINK = pink; + + /** Constant for the color orange: R=255, G=200, B=0. */ + public static final Color orange = new Color(0xffc800, false); + + /** + * Constant for the color orange: R=255, G=200, B=0. + * + * @since 1.4 + */ + public static final Color ORANGE = orange; + + /** Constant for the color yellow: R=255, G=255, B=0. */ + public static final Color yellow = new Color(0xffff00, false); + + /** + * Constant for the color yellow: R=255, G=255, B=0. + * + * @since 1.4 + */ + public static final Color YELLOW = yellow; + + /** Constant for the color green: R=0, G=255, B=0. */ + public static final Color green = new Color(0x00ff00, false); + + /** + * Constant for the color green: R=0, G=255, B=0. + * + * @since 1.4 + */ + public static final Color GREEN = green; + + /** Constant for the color magenta: R=255, G=0, B=255. */ + public static final Color magenta = new Color(0xff00ff, false); + + /** + * Constant for the color magenta: R=255, G=0, B=255. + * + * @since 1.4 + */ + public static final Color MAGENTA = magenta; + + /** Constant for the color cyan: R=0, G=255, B=255. */ + public static final Color cyan = new Color(0x00ffff, false); + + /** + * Constant for the color cyan: R=0, G=255, B=255. + * + * @since 1.4 + */ + public static final Color CYAN = cyan; + + /** Constant for the color blue: R=0, G=0, B=255. */ + public static final Color blue = new Color(0x0000ff, false); + + /** + * Constant for the color blue: R=0, G=0, B=255. + * + * @since 1.4 + */ + public static final Color BLUE = blue; + + /** Internal mask for red. */ + private static final int RED_MASK = 255 << 16; + + /** Internal mask for green. */ + private static final int GREEN_MASK = 255 << 8; + + /** Internal mask for blue. */ + private static final int BLUE_MASK = 255; + + /** Internal mask for alpha. Package visible for use in subclass. */ + static final int ALPHA_MASK = 255 << 24; + + /** Amount to scale a color by when brightening or darkening. */ + private static final float BRIGHT_SCALE = 0.7f; + + /** + * The color value, in sRGB. Note that the actual color may be more + * precise if frgbvalue or fvalue is non-null. This class stores alpha, red, + * green, and blue, each 0-255, packed in an int. However, the subclass + * SystemColor stores an index into an array. Therefore, for serial + * compatibility (and because of poor design on Sun's part), this value + * cannot be used directly; instead you must use <code>getRGB()</code>. + * + * @see #getRGB() + * @serial the value of the color, whether an RGB literal or array index + */ + final int value; + + /** + * The color value, in sRGB. This may be null if the color was constructed + * with ints; and it does not include alpha. This stores red, green, and + * blue, in the range 0.0f - 1.0f. + * + * @see #getRGBColorComponents(float[]) + * @see #getRGBComponents(float[]) + * @serial the rgb components of the value + * @since 1.2 + */ + private float[] frgbvalue; + + /** + * The color value, in the native ColorSpace components. This may be null + * if the color was constructed with ints or in the sRGB color space; and + * it does not include alpha. + * + * @see #getRGBColorComponents(float[]) + * @see #getRGBComponents(float[]) + * @serial the original color space components of the color + * @since 1.2 + */ + private float[] fvalue; + + /** + * The alpha value. This is in the range 0.0f - 1.0f, but is invalid if + * deserialized as 0.0 when frgbvalue is null. + * + * @see #getRGBComponents(float[]) + * @see #getComponents(float[]) + * @serial the alpha component of this color + * @since 1.2 + */ + private final float falpha; + + /** + * The ColorSpace. Null means the default sRGB space. + * + * @see #getColor(String) + * @see #getColorSpace() + * @see #getColorComponents(float[]) + * @serial the color space for this color + * @since 1.2 + */ + private final ColorSpace cs; + + /** + * The paint context for this solid color. Package visible for use in + * subclass. + */ + transient ColorPaintContext context; + + /** + * Initializes a new instance of <code>Color</code> using the specified + * red, green, and blue values, which must be given as integers in the + * range of 0-255. Alpha will default to 255 (opaque). When drawing to + * screen, the actual color may be adjusted to the best match of hardware + * capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @throws IllegalArgumentException if the values are out of range 0-255 + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(int, int, int, int) + */ + public Color(int red, int green, int blue) + { + this(red, green, blue, 255); + } + + /** + * Initializes a new instance of <code>Color</code> using the specified + * red, green, blue, and alpha values, which must be given as integers in + * the range of 0-255. When drawing to screen, the actual color may be + * adjusted to the best match of hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @param alpha the alpha value of the color + * @throws IllegalArgumentException if the values are out of range 0-255 + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(int red, int green, int blue, int alpha) + { + if ((red & 255) != red || (green & 255) != green || (blue & 255) != blue + || (alpha & 255) != alpha) + throw new IllegalArgumentException("Bad RGB values" + +" red=0x"+Integer.toHexString(red) + +" green=0x"+Integer.toHexString(green) + +" blue=0x"+Integer.toHexString(blue) + +" alpha=0x"+Integer.toHexString(alpha) ); + + value = (alpha << 24) | (red << 16) | (green << 8) | blue; + falpha = 1; + cs = null; + } + + /** + * Initializes a new instance of <code>Color</code> using the specified + * RGB value. The blue value is in bits 0-7, green in bits 8-15, and + * red in bits 16-23. The other bits are ignored. The alpha value is set + * to 255 (opaque). When drawing to screen, the actual color may be + * adjusted to the best match of hardware capabilities. + * + * @param value the RGB value + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(int, boolean) + */ + public Color(int value) + { + this(value, false); + } + + /** + * Initializes a new instance of <code>Color</code> using the specified + * RGB value. The blue value is in bits 0-7, green in bits 8-15, and + * red in bits 16-23. The alpha value is in bits 24-31, unless hasalpha + * is false, in which case alpha is set to 255. When drawing to screen, the + * actual color may be adjusted to the best match of hardware capabilities. + * + * @param value the RGB value + * @param hasalpha true if value includes the alpha + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(int value, boolean hasalpha) + { + // Note: SystemColor calls this constructor, setting falpha to 0; but + // code in getRGBComponents correctly reports falpha as 1.0 to the user + // for all instances of SystemColor since frgbvalue is left null here. + if (hasalpha) + falpha = ((value & ALPHA_MASK) >> 24) / 255f; + else + { + value |= ALPHA_MASK; + falpha = 1; + } + this.value = value; + cs = null; + } + + /** + * Initializes a new instance of <code>Color</code> using the specified + * RGB values. These must be in the range of 0.0-1.0. Alpha is assigned + * the value of 1.0 (opaque). When drawing to screen, the actual color may + * be adjusted to the best match of hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @throws IllegalArgumentException tf the values are out of range 0.0f-1.0f + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(float, float, float, float) + */ + public Color(float red, float green, float blue) + { + this(red, green, blue, 1.0f); + } + + /** + * Initializes a new instance of <code>Color</code> using the specified + * RGB and alpha values. These must be in the range of 0.0-1.0. When drawing + * to screen, the actual color may be adjusted to the best match of + * hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @param alpha the alpha value of the color + * @throws IllegalArgumentException tf the values are out of range 0.0f-1.0f + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(float red, float green, float blue, float alpha) + { + value = convert(red, green, blue, alpha); + frgbvalue = new float[] {red, green, blue}; + falpha = alpha; + cs = null; + } + + /** + * Creates a color in the given ColorSpace with the specified alpha. The + * array must be non-null and have enough elements for the color space + * (for example, RGB requires 3 elements, CMYK requires 4). When drawing + * to screen, the actual color may be adjusted to the best match of + * hardware capabilities. + * + * @param space the color space of components + * @param components the color components, except alpha + * @param alpha the alpha value of the color + * @throws NullPointerException if cpsace or components is null + * @throws ArrayIndexOutOfBoundsException if components is too small + * @throws IllegalArgumentException if alpha or any component is out of range + * @see #getComponents(float[]) + * @see #getColorComponents(float[]) + */ + public Color(ColorSpace space, float[] components, float alpha) + { + frgbvalue = space.toRGB(components); + fvalue = components; + falpha = alpha; + cs = space; + value = convert(frgbvalue[0], frgbvalue[1], frgbvalue[2], alpha); + } + + /** + * Returns the red value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the red value for this color + * @see #getRGB() + */ + public int getRed() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & RED_MASK) >> 16; + } + + /** + * Returns the green value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the green value for this color + * @see #getRGB() + */ + public int getGreen() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & GREEN_MASK) >> 8; + } + + /** + * Returns the blue value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the blue value for this color + * @see #getRGB() + */ + public int getBlue() + { + // Do not inline getRGB() to value, because of SystemColor. + return getRGB() & BLUE_MASK; + } + + /** + * Returns the alpha value for this color, as an integer in the range 0-255. + * + * @return the alpha value for this color + * @see #getRGB() + */ + public int getAlpha() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & ALPHA_MASK) >>> 24; + } + + /** + * Returns the RGB value for this color, in the sRGB color space. The blue + * value will be in bits 0-7, green in 8-15, red in 16-23, and alpha value in + * 24-31. + * + * @return the RGB value for this color + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + */ + public int getRGB() + { + return value; + } + + /** + * Returns a brighter version of this color. This is done by increasing the + * RGB values by an arbitrary scale factor. The new color is opaque (an + * alpha of 255). Note that this method and the <code>darker()</code> + * method are not necessarily inverses. + * + * @return a brighter version of this color + * @see #darker() + */ + public Color brighter() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + int red = (value & RED_MASK) >> 16; + int green = (value & GREEN_MASK) >> 8; + int blue = value & BLUE_MASK; + // We have to special case 0-2 because they won't scale by division. + red = red < 3 ? 3 : (int) Math.min(255, red / BRIGHT_SCALE); + green = green < 3 ? 3 : (int) Math.min(255, green / BRIGHT_SCALE); + blue = blue < 3 ? 3 : (int) Math.min(255, blue / BRIGHT_SCALE); + return new Color(red, green, blue, 255); + } + + /** + * Returns a darker version of this color. This is done by decreasing the + * RGB values by an arbitrary scale factor. The new color is opaque (an + * alpha of 255). Note that this method and the <code>brighter()</code> + * method are not necessarily inverses. + * + * @return a darker version of this color + * @see #brighter() + */ + public Color darker() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + return new Color((int) (((value & RED_MASK) >> 16) * BRIGHT_SCALE), + (int) (((value & GREEN_MASK) >> 8) * BRIGHT_SCALE), + (int) ((value & BLUE_MASK) * BRIGHT_SCALE), 255); + } + + /** + * Returns a hash value for this color. This is simply the color in 8-bit + * precision, in the format 0xAARRGGBB (alpha, red, green, blue). + * + * @return a hash value for this color + */ + public int hashCode() + { + return value; + } + + /** + * Tests this object for equality against the specified object. This will + * be true if and only if the specified object is an instance of + * <code>Color</code> and has the same 8-bit integer red, green, and blue + * values as this object. Note that two colors may be slightly different + * as float values, but round to the same integer values. Also note that + * this does not accurately compare SystemColors, since that class does + * not store its internal data in RGB format like regular colors. + * + * @param obj the object to compare to + * @return true if the specified object is semantically equal to this one + */ + public boolean equals(Object obj) + { + return obj instanceof Color && ((Color) obj).value == value; + } + + /** + * Returns a string representation of this object. Subclasses may return + * any desired format, except for null, but this implementation returns + * <code>getClass().getName() + "[r=" + getRed() + ",g=" + getGreen() + * + ",b=" + getBlue() + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "[r=" + ((value & RED_MASK) >> 16) + + ",g=" + ((value & GREEN_MASK) >> 8) + ",b=" + (value & BLUE_MASK) + + ']'; + } + + /** + * Converts the specified string to a number, using Integer.decode, and + * creates a new instance of <code>Color</code> from the value. The alpha + * value will be 255 (opaque). + * + * @param str the numeric color string + * @return a new instance of <code>Color</code> for the string + * @throws NumberFormatException if the string cannot be parsed + * @throws NullPointerException if the string is null + * @see Integer#decode(String) + * @see #Color(int) + * @since 1.1 + */ + public static Color decode(String str) + { + return new Color(Integer.decode(str).intValue(), false); + } + + /** + * Returns a new instance of <code>Color</code> from the value of the + * system property named by the specified string. If the property does not + * exist, or cannot be parsed, then <code>null</code> will be returned. + * + * @param prop the system property to retrieve + * @throws SecurityException if getting the property is denied + * @see #getColor(String, Color) + * @see Integer#getInteger(String) + */ + public static Color getColor(String prop) + { + return getColor(prop, null); + } + + /** + * Returns a new instance of <code>Color</code> from the value of the + * system property named by the specified string. If the property does + * not exist, or cannot be parsed, then the default color value will be + * returned. + * + * @param prop the system property to retrieve + * @param defcolor the default color + * @throws SecurityException if getting the property is denied + * @see Integer#getInteger(String) + */ + public static Color getColor(String prop, Color defcolor) + { + Integer val = Integer.getInteger(prop, null); + return val == null ? defcolor + : new Color(val.intValue(), false); + } + + /** + * Returns a new instance of <code>Color</code> from the value of the + * system property named by the specified string. If the property does + * not exist, or cannot be parsed, then the default RGB value will be + * used to create a return value. + * + * @param prop the system property to retrieve + * @param defrgb the default RGB value + * @throws SecurityException if getting the property is denied + * @see #getColor(String, Color) + * @see Integer#getInteger(String, int) + */ + public static Color getColor(String prop, int defrgb) + { + Color c = getColor(prop, null); + return c == null ? new Color(defrgb, false) : c; + } + + /** + * Converts from the HSB (hue, saturation, brightness) color model to the + * RGB (red, green, blue) color model. The hue may be any floating point; + * it's fractional portion is used to select the angle in the HSB model. + * The saturation and brightness must be between 0 and 1. The result is + * suitable for creating an RGB color with the one-argument constructor. + * + * @param hue the hue of the HSB value + * @param saturation the saturation of the HSB value + * @param brightness the brightness of the HSB value + * @return the RGB value + * @see #getRGB() + * @see #Color(int) + * @see ColorModel#getRGBdefault() + */ + public static int HSBtoRGB(float hue, float saturation, float brightness) + { + if (saturation == 0) + return convert(brightness, brightness, brightness, 0); + if (saturation < 0 || saturation > 1 || brightness < 0 || brightness > 1) + throw new IllegalArgumentException(); + hue = hue - (float) Math.floor(hue); + int i = (int) (6 * hue); + float f = 6 * hue - i; + float p = brightness * (1 - saturation); + float q = brightness * (1 - saturation * f); + float t = brightness * (1 - saturation * (1 - f)); + switch (i) + { + case 0: + return convert(brightness, t, p, 0); + case 1: + return convert(q, brightness, p, 0); + case 2: + return convert(p, brightness, t, 0); + case 3: + return convert(p, q, brightness, 0); + case 4: + return convert(t, p, brightness, 0); + case 5: + return convert(brightness, p, q, 0); + default: + throw new InternalError("impossible"); + } + } + + /** + * Converts from the RGB (red, green, blue) color model to the HSB (hue, + * saturation, brightness) color model. If the array is null, a new one + * is created, otherwise it is recycled. The results will be in the range + * 0.0-1.0 if the inputs are in the range 0-255. + * + * @param red the red part of the RGB value + * @param green the green part of the RGB value + * @param blue the blue part of the RGB value + * @param array an array for the result (at least 3 elements), or null + * @return the array containing HSB value + * @throws ArrayIndexOutOfBoundsException of array is too small + * @see #getRGB() + * @see #Color(int) + * @see ColorModel#getRGBdefault() + */ + public static float[] RGBtoHSB(int red, int green, int blue, float array[]) + { + if (array == null) + array = new float[3]; + // Calculate brightness. + int min; + int max; + if (red < green) + { + min = red; + max = green; + } + else + { + min = green; + max = red; + } + if (blue > max) + max = blue; + else if (blue < min) + min = blue; + array[2] = max / 255f; + // Calculate saturation. + if (max == 0) + array[1] = 0; + else + array[1] = (max - min) / max; + // Calculate hue. + if (array[1] == 0) + array[0] = 0; + else + { + float delta = (max - min) * 6; + if (red == max) + array[0] = (green - blue) / delta; + else if (green == max) + array[0] = 1f / 3 + (blue - red) / delta; + else + array[0] = 2f / 3 + (red - green) / delta; + if (array[0] < 0) + array[0]++; + } + return array; + } + + /** + * Returns a new instance of <code>Color</code> based on the specified + * HSB values. The hue may be any floating point; it's fractional portion + * is used to select the angle in the HSB model. The saturation and + * brightness must be between 0 and 1. + * + * @param hue the hue of the HSB value + * @param saturation the saturation of the HSB value + * @param brightness the brightness of the HSB value + * @return the new <code>Color</code> object + */ + public static Color getHSBColor(float hue, float saturation, + float brightness) + { + return new Color(HSBtoRGB(hue, saturation, brightness), false); + } + + /** + * Returns a float array with the red, green, and blue components, and the + * alpha value, in the default sRGB space, with values in the range 0.0-1.0. + * If the array is null, a new one is created, otherwise it is recycled. + * + * @param array the array to put results into (at least 4 elements), or null + * @return the RGB components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getRGBComponents(float[] array) + { + if (array == null) + array = new float[4]; + getRGBColorComponents(array); + // Stupid serialization issues require this check. + array[3] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array with the red, green, and blue components, in the + * default sRGB space, with values in the range 0.0-1.0. If the array is + * null, a new one is created, otherwise it is recycled. + * + * @param array the array to put results into (at least 3 elements), or null + * @return the RGB components + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getRGBColorComponents(float[] array) + { + if (array == null) + array = new float[3]; + else if (array == frgbvalue) + return array; // Optimization for getColorComponents(float[]). + if (frgbvalue == null) + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + frgbvalue = new float[] { ((value & RED_MASK) >> 16) / 255f, + ((value & GREEN_MASK) >> 8) / 255f, + (value & BLUE_MASK) / 255f }; + } + array[0] = frgbvalue[0]; + array[1] = frgbvalue[1]; + array[2] = frgbvalue[2]; + return array; + } + + /** + * Returns a float array containing the color and alpha components of this + * color in the ColorSpace it was created with (the constructors which do + * not take a ColorSpace parameter use a default sRGB ColorSpace). If the + * array is null, a new one is created, otherwise it is recycled, and must + * have at least one more position than components used in the color space. + * + * @param array the array to put results into, or null + * @return the original color space components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getComponents(float[] array) + { + int numComponents = cs == null ? 3 : cs.getNumComponents(); + if (array == null) + array = new float[1 + numComponents]; + getColorComponents(array); + // Stupid serialization issues require this check. + array[numComponents] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array containing the color components of this color in + * the ColorSpace it was created with (the constructors which do not take + * a ColorSpace parameter use a default sRGB ColorSpace). If the array is + * null, a new one is created, otherwise it is recycled, and must have at + * least as many positions as used in the color space. + * + * @param array the array to put results into, or null + * @return the original color space components + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getColorComponents(float[] array) + { + int numComponents = cs == null ? 3 : cs.getNumComponents(); + if (array == null) + array = new float[numComponents]; + if (fvalue == null) // If fvalue is null, cs should be null too. + fvalue = getRGBColorComponents(frgbvalue); + System.arraycopy(fvalue, 0, array, 0, numComponents); + return array; + } + + /** + * Returns a float array containing the color and alpha components of this + * color in the given ColorSpace. If the array is null, a new one is + * created, otherwise it is recycled, and must have at least one more + * position than components used in the color space. + * + * @param space the color space to translate to + * @param array the array to put results into, or null + * @return the color space components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + * @throws NullPointerException if space is null + */ + public float[] getComponents(ColorSpace space, float[] array) + { + int numComponents = space.getNumComponents(); + if (array == null) + array = new float[1 + numComponents]; + getColorComponents(space, array); + // Stupid serialization issues require this check. + array[numComponents] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array containing the color components of this color in + * the given ColorSpace. If the array is null, a new one is created, + * otherwise it is recycled, and must have at least as many positions as + * used in the color space. + * + * @param space the color space to translate to + * @return the color space components + * @throws ArrayIndexOutOfBoundsException if array is too small + * @throws NullPointerException if space is null + */ + public float[] getColorComponents(ColorSpace space, float[] array) + { + float[] components = space.fromRGB(getRGBColorComponents(frgbvalue)); + if (array == null) + return components; + System.arraycopy(components, 0, array, 0, components.length); + return array; + } + + /** + * Returns the color space of this color. Except for the constructor which + * takes a ColorSpace argument, this will be an implementation of + * ColorSpace.CS_sRGB. + * + * @return the color space + */ + public ColorSpace getColorSpace() + { + return cs == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : cs; + } + + /** + * Returns a paint context, used for filling areas of a raster scan with + * this color. Since the color is constant across the entire rectangle, and + * since it is always in sRGB space, this implementation returns the same + * object, regardless of the parameters. Subclasses, however, may have a + * mutable result. + * + * @param cm the requested color model + * @param deviceBounds the bounding box in device coordinates, ignored + * @param userBounds the bounding box in user coordinates, ignored + * @param xform the bounds transformation, ignored + * @param hints any rendering hints, ignored + * @return a context for painting this solid color + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + if (context == null || !context.getColorModel().equals(cm)) + context = new ColorPaintContext(cm,value); + return context; + } + + /** + * Returns the transparency level of this color. + * + * @return one of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT} + */ + public int getTransparency() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int alpha = getRGB() & ALPHA_MASK; + return alpha == (255 << 24) ? OPAQUE : alpha == 0 ? BITMASK : TRANSLUCENT; + } + + /** + * Converts float values to integer value. + * + * @param red the red value + * @param green the green value + * @param blue the blue value + * @param alpha the alpha value + * @return the integer value made of 8-bit sections + * @throws IllegalArgumentException if parameters are out of range 0.0-1.0 + */ + private static int convert(float red, float green, float blue, float alpha) + { + if (red < 0 || red > 1 || green < 0 || green > 1 || blue < 0 || blue > 1 + || alpha < 0 || alpha > 1) + throw new IllegalArgumentException("Bad RGB values"); + int redval = Math.round(255 * red); + int greenval = Math.round(255 * green); + int blueval = Math.round(255 * blue); + int alphaval = Math.round(255 * alpha); + return (alphaval << 24) | (redval << 16) | (greenval << 8) | blueval; + } +} // class Color diff --git a/libjava/classpath/java/awt/ColorPaintContext.java b/libjava/classpath/java/awt/ColorPaintContext.java new file mode 100644 index 00000000000..759ba9d8734 --- /dev/null +++ b/libjava/classpath/java/awt/ColorPaintContext.java @@ -0,0 +1,195 @@ +/* ColorPaintContext.java -- context for painting solid colors + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * This class provides a paint context which will fill a rectanglar region of + * a raster scan with the given color. However, it is not yet completely + * implemented. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class ColorPaintContext implements PaintContext +{ + /** + * The color to fill any raster with. Package visible for use in + * SystemColor. + */ + final int color; + final ColorModel colorModel; + + private ColorRaster cachedRaster; + + + /** + * Create the context for a given color. + * + * @param c The solid color to use. + */ + ColorPaintContext(int colorRGB) + { + this(ColorModel.getRGBdefault(), colorRGB); + } + + /** + * Create the context for a given color. + * + * @param cm The color model of this context. + * @param c The solid color to use. + */ + ColorPaintContext(ColorModel cm,int colorRGB) + { + color = colorRGB; + colorModel = cm; + } + + /** + * Release the resources allocated for the paint. As the color is constant, + * there aren't any resources. + */ + public void dispose() + { + } + + /** + * Return the color model of this context. + * + * @return the context color model + */ + public ColorModel getColorModel() + { + return colorModel; + } + + /** + * Return a raster containing the colors for the graphics operation. + * + * @param x the x-coordinate, in device space + * @param y the y-coordinate, in device space + * @param width the width, in device space + * @param height the height, in device space + * @return a raster for the given area and color + */ + public Raster getRaster(int x, int y, int width, int height) + { + if( cachedRaster == null + || cachedRaster.getWidth() < width + || cachedRaster.getHeight() < height) + { + cachedRaster = new ColorRaster(colorModel, 0, 0, width, height, color); + } + return cachedRaster.createChild(0 ,0 ,width ,height ,x ,y , null); + } + + /** + * A ColorRaster is a raster that is completely filled with one color. The + * data layout is taken from the color model given to the constructor. + */ + private class ColorRaster extends Raster + { + + /** + * Create a raster that is compaltible with the given color model and + * filled with the given color. + * @param cm The color model for this raster. + * @param x The smallest horizontal corrdinate in the raster. + * @param y The smallest vertical coordinate in the raster. + * @param width The width of the raster. + * @param height The height of the raster. + * @param rgbPixel The RGB value of the color for this raster. + */ + ColorRaster(ColorModel cm,int x, int y, int width, int height, int rgbPixel) + { + super(cm.createCompatibleSampleModel(width,height),new Point(x,y)); + Object pixel = cm.getDataElements(rgbPixel,null); + getSampleModel().setDataElements(0, 0, + width, height, + multiplyData(pixel,null,width*height), + dataBuffer); + } + + + + private Object multiplyData(Object src, Object dest, int factor) + { + Object from; + int srcLength = 0; + if (src instanceof byte[]) + { + srcLength = ((byte[])src).length; + + if (dest == null) dest = new byte[factor * srcLength]; + } + else if (src instanceof short[]) + { + srcLength = ((short[])src).length; + if (dest == null) dest = new short[factor * srcLength]; + } + else if (src instanceof int[]) + { + srcLength = ((int[]) src).length; + if (dest == null) dest = new int[factor * srcLength]; + } + else + { + throw new ClassCastException("Unknown data buffer type"); + } + + System.arraycopy(src,0,dest,0,srcLength); + + int count = 1; + while(count*2 < factor) + { + System.arraycopy(dest, 0, dest, count * srcLength, count*srcLength); + count *= 2; + } + + if(factor > count) + System.arraycopy(dest,0, dest, count * srcLength, + (factor - count) * srcLength ); + + return dest; + } + + } + +} // class ColorPaintContext diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java new file mode 100644 index 00000000000..4491904d0a4 --- /dev/null +++ b/libjava/classpath/java/awt/Component.java @@ -0,0 +1,6017 @@ +/* Component.java -- a graphics component + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.im.InputContext; +import java.awt.im.InputMethodRequests; +import java.awt.image.BufferStrategy; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * The root of all evil. All graphical representations are subclasses of this + * giant class, which is designed for screen display and user interaction. + * This class can be extended directly to build a lightweight component (one + * not associated with a native window); lightweight components must reside + * inside a heavyweight window. + * + * <p>This class is Serializable, which has some big implications. A user can + * save the state of all graphical components in one VM, and reload them in + * another. Note that this class will only save Serializable listeners, and + * ignore the rest, without causing any serialization exceptions. However, by + * making a listener serializable, and adding it to another element, you link + * in that entire element to the state of this component. To get around this, + * use the idiom shown in the example below - make listeners non-serializable + * in inner classes, rather than using this object itself as the listener, if + * external objects do not need to save the state of this object. + * + * <pre> + * import java.awt.*; + * import java.awt.event.*; + * import java.io.Serializable; + * class MyApp implements Serializable + * { + * BigObjectThatShouldNotBeSerializedWithAButton bigOne; + * // Serializing aButton will not suck in an instance of MyApp, with its + * // accompanying field bigOne. + * Button aButton = new Button(); + * class MyActionListener implements ActionListener + * { + * public void actionPerformed(ActionEvent e) + * { + * System.out.println("Hello There"); + * } + * } + * MyApp() + * { + * aButton.addActionListener(new MyActionListener()); + * } + * } + * </pre> + * + * <p>Status: Incomplete. The event dispatch mechanism is implemented. All + * other methods defined in the J2SE 1.3 API javadoc exist, but are mostly + * incomplete or only stubs; except for methods relating to the Drag and + * Drop, Input Method, and Accessibility frameworks: These methods are + * present but commented out. + * + * @author original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status still missing 1.4 support + */ +public abstract class Component + implements ImageObserver, MenuContainer, Serializable +{ + // Word to the wise - this file is huge. Search for '\f' (^L) for logical + // sectioning by fields, public API, private API, and nested classes. + + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7644114512714619750L; + + /** + * Constant returned by the <code>getAlignmentY</code> method to indicate + * that the component wishes to be aligned to the top relative to + * other components. + * + * @see #getAlignmentY() + */ + public static final float TOP_ALIGNMENT = 0; + + /** + * Constant returned by the <code>getAlignmentY</code> and + * <code>getAlignmentX</code> methods to indicate + * that the component wishes to be aligned to the center relative to + * other components. + * + * @see #getAlignmentX() + * @see #getAlignmentY() + */ + public static final float CENTER_ALIGNMENT = 0.5f; + + /** + * Constant returned by the <code>getAlignmentY</code> method to indicate + * that the component wishes to be aligned to the bottom relative to + * other components. + * + * @see #getAlignmentY() + */ + public static final float BOTTOM_ALIGNMENT = 1; + + /** + * Constant returned by the <code>getAlignmentX</code> method to indicate + * that the component wishes to be aligned to the right relative to + * other components. + * + * @see #getAlignmentX() + */ + public static final float RIGHT_ALIGNMENT = 1; + + /** + * Constant returned by the <code>getAlignmentX</code> method to indicate + * that the component wishes to be aligned to the left relative to + * other components. + * + * @see #getAlignmentX() + */ + public static final float LEFT_ALIGNMENT = 0; + + /** + * Make the treelock a String so that it can easily be identified + * in debug dumps. We clone the String in order to avoid a conflict in + * the unlikely event that some other package uses exactly the same string + * as a lock object. + */ + static final Object treeLock = new String("AWT_TREE_LOCK"); + + // Serialized fields from the serialization spec. + + /** + * The x position of the component in the parent's coordinate system. + * + * @see #getLocation() + * @serial the x position + */ + int x; + + /** + * The y position of the component in the parent's coordinate system. + * + * @see #getLocation() + * @serial the y position + */ + int y; + + /** + * The component width. + * + * @see #getSize() + * @serial the width + */ + int width; + + /** + * The component height. + * + * @see #getSize() + * @serial the height + */ + int height; + + /** + * The foreground color for the component. This may be null. + * + * @see #getForeground() + * @see #setForeground(Color) + * @serial the foreground color + */ + Color foreground; + + /** + * The background color for the component. This may be null. + * + * @see #getBackground() + * @see #setBackground(Color) + * @serial the background color + */ + Color background; + + /** + * The default font used in the component. This may be null. + * + * @see #getFont() + * @see #setFont(Font) + * @serial the font + */ + Font font; + + /** + * The font in use by the peer, or null if there is no peer. + * + * @serial the peer's font + */ + Font peerFont; + + /** + * The cursor displayed when the pointer is over this component. This may + * be null. + * + * @see #getCursor() + * @see #setCursor(Cursor) + */ + Cursor cursor; + + /** + * The locale for the component. + * + * @see #getLocale() + * @see #setLocale(Locale) + */ + Locale locale = Locale.getDefault (); + + /** + * True if the object should ignore repaint events (usually because it is + * not showing). + * + * @see #getIgnoreRepaint() + * @see #setIgnoreRepaint(boolean) + * @serial true to ignore repaints + * @since 1.4 + */ + boolean ignoreRepaint; + + /** + * True when the object is visible (although it is only showing if all + * ancestors are likewise visible). For component, this defaults to true. + * + * @see #isVisible() + * @see #setVisible(boolean) + * @serial true if visible + */ + boolean visible = true; + + /** + * True if the object is enabled, meaning it can interact with the user. + * For component, this defaults to true. + * + * @see #isEnabled() + * @see #setEnabled(boolean) + * @serial true if enabled + */ + boolean enabled = true; + + /** + * True if the object is valid. This is set to false any time a size + * adjustment means the component need to be layed out again. + * + * @see #isValid() + * @see #validate() + * @see #invalidate() + * @serial true if layout is valid + */ + boolean valid; + + /** + * The DropTarget for drag-and-drop operations. + * + * @see #getDropTarget() + * @see #setDropTarget(DropTarget) + * @serial the drop target, or null + * @since 1.2 + */ + DropTarget dropTarget; + + /** + * The list of popup menus for this component. + * + * @see #add(PopupMenu) + * @serial the list of popups + */ + Vector popups; + + /** + * The component's name. May be null, in which case a default name is + * generated on the first use. + * + * @see #getName() + * @see #setName(String) + * @serial the name + */ + String name; + + /** + * True once the user has set the name. Note that the user may set the name + * to null. + * + * @see #name + * @see #getName() + * @see #setName(String) + * @serial true if the name has been explicitly set + */ + boolean nameExplicitlySet; + + /** + * Indicates if the object can be focused. Defaults to true for components. + * + * @see #isFocusable() + * @see #setFocusable(boolean) + * @since 1.4 + */ + boolean focusable = true; + + /** + * Tracks whether this component's {@link #isFocusTraversable} + * method has been overridden. + * + * @since 1.4 + */ + int isFocusTraversableOverridden; + + /** + * The focus traversal keys, if not inherited from the parent or + * default keyboard focus manager. These sets will contain only + * AWTKeyStrokes that represent press and release events to use as + * focus control. + * + * @see #getFocusTraversalKeys(int) + * @see #setFocusTraversalKeys(int, Set) + * @since 1.4 + */ + Set[] focusTraversalKeys; + + /** + * True if focus traversal keys are enabled. This defaults to true for + * Component. If this is true, keystrokes in focusTraversalKeys are trapped + * and processed automatically rather than being passed on to the component. + * + * @see #getFocusTraversalKeysEnabled() + * @see #setFocusTraversalKeysEnabled(boolean) + * @since 1.4 + */ + boolean focusTraversalKeysEnabled = true; + + /** + * Cached information on the minimum size. Should have been transient. + * + * @serial ignore + */ + Dimension minSize; + + /** + * Cached information on the preferred size. Should have been transient. + * + * @serial ignore + */ + Dimension prefSize; + + /** + * Set to true if an event is to be handled by this component, false if + * it is to be passed up the hierarcy. + * + * @see #dispatchEvent(AWTEvent) + * @serial true to process event locally + */ + boolean newEventsOnly; + + /** + * Set by subclasses to enable event handling of particular events, and + * left alone when modifying listeners. For component, this defaults to + * enabling only input methods. + * + * @see #enableInputMethods(boolean) + * @see AWTEvent + * @serial the mask of events to process + */ + long eventMask = AWTEvent.INPUT_ENABLED_EVENT_MASK; + + /** + * Describes all registered PropertyChangeListeners. + * + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #firePropertyChange(String, Object, Object) + * @serial the property change listeners + * @since 1.2 + */ + PropertyChangeSupport changeSupport; + + /** + * True if the component has been packed (layed out). + * + * @serial true if this is packed + */ + boolean isPacked; + + /** + * The serialization version for this class. Currently at version 4. + * + * XXX How do we handle prior versions? + * + * @serial the serialization version + */ + int componentSerializedDataVersion = 4; + + /** + * The accessible context associated with this component. This is only set + * by subclasses. + * + * @see #getAccessibleContext() + * @serial the accessibility context + * @since 1.2 + */ + AccessibleContext accessibleContext; + + + // Guess what - listeners are special cased in serialization. See + // readObject and writeObject. + + /** Component listener chain. */ + transient ComponentListener componentListener; + + /** Focus listener chain. */ + transient FocusListener focusListener; + + /** Key listener chain. */ + transient KeyListener keyListener; + + /** Mouse listener chain. */ + transient MouseListener mouseListener; + + /** Mouse motion listener chain. */ + transient MouseMotionListener mouseMotionListener; + + /** + * Mouse wheel listener chain. + * + * @since 1.4 + */ + transient MouseWheelListener mouseWheelListener; + + /** + * Input method listener chain. + * + * @since 1.2 + */ + transient InputMethodListener inputMethodListener; + + /** + * Hierarcy listener chain. + * + * @since 1.3 + */ + transient HierarchyListener hierarchyListener; + + /** + * Hierarcy bounds listener chain. + * + * @since 1.3 + */ + transient HierarchyBoundsListener hierarchyBoundsListener; + + // Anything else is non-serializable, and should be declared "transient". + + /** The parent. */ + transient Container parent; + + /** The associated native peer. */ + transient ComponentPeer peer; + + /** The preferred component orientation. */ + transient ComponentOrientation orientation = ComponentOrientation.UNKNOWN; + + /** + * The associated graphics configuration. + * + * @since 1.4 + */ + transient GraphicsConfiguration graphicsConfig; + + /** + * The buffer strategy for repainting. + * + * @since 1.4 + */ + transient BufferStrategy bufferStrategy; + + /** + * true if requestFocus was called on this component when its + * top-level ancestor was not focusable. + */ + private transient FocusEvent pendingFocusRequest = null; + + /** + * The system properties that affect image updating. + */ + private static transient boolean incrementalDraw; + private static transient Long redrawRate; + + static + { + incrementalDraw = Boolean.getBoolean ("awt.image.incrementalDraw"); + redrawRate = Long.getLong ("awt.image.redrawrate"); + } + + // Public and protected API. + + /** + * Default constructor for subclasses. When Component is extended directly, + * it forms a lightweight component that must be hosted in an opaque native + * container higher in the tree. + */ + protected Component() + { + } + + /** + * Returns the name of this component. + * + * @return the name of this component + * @see #setName(String) + * @since 1.1 + */ + public String getName() + { + if (name == null && ! nameExplicitlySet) + name = generateName(); + return name; + } + + /** + * Sets the name of this component to the specified name. + * + * @param name the new name of this component + * @see #getName() + * @since 1.1 + */ + public void setName(String name) + { + nameExplicitlySet = true; + this.name = name; + } + + /** + * Returns the parent of this component. + * + * @return the parent of this component + */ + public Container getParent() + { + return parent; + } + + /** + * Returns the native windowing system peer for this component. Only the + * platform specific implementation code should call this method. + * + * @return the peer for this component + * @deprecated user programs should not directly manipulate peers; use + * {@link #isDisplayable()} instead + */ + // Classpath's Gtk peers rely on this. + public ComponentPeer getPeer() + { + return peer; + } + + /** + * Set the associated drag-and-drop target, which receives events when this + * is enabled. + * + * @param dt the new drop target + * @see #isEnabled() + */ + public void setDropTarget(DropTarget dt) + { + this.dropTarget = dt; + } + + /** + * Gets the associated drag-and-drop target, if there is one. + * + * @return the drop target + */ + public DropTarget getDropTarget() + { + return dropTarget; + } + + /** + * Returns the graphics configuration of this component, if there is one. + * If it has not been set, it is inherited from the parent. + * + * @return the graphics configuration, or null + * @since 1.3 + */ + public GraphicsConfiguration getGraphicsConfiguration() + { + return getGraphicsConfigurationImpl(); + } + + /** + * Returns the object used for synchronization locks on this component + * when performing tree and layout functions. + * + * @return the synchronization lock for this component + */ + public final Object getTreeLock() + { + return treeLock; + } + + /** + * Returns the toolkit in use for this component. The toolkit is associated + * with the frame this component belongs to. + * + * @return the toolkit for this component + */ + public Toolkit getToolkit() + { + if (peer != null) + { + Toolkit tk = peer.getToolkit(); + if (tk != null) + return tk; + } + // Get toolkit for lightweight component. + if (parent != null) + return parent.getToolkit(); + return Toolkit.getDefaultToolkit(); + } + + /** + * Tests whether or not this component is valid. A invalid component needs + * to have its layout redone. + * + * @return true if this component is valid + * @see #validate() + * @see #invalidate() + */ + public boolean isValid() + { + return valid; + } + + /** + * Tests if the component is displayable. It must be connected to a native + * screen resource, and all its ancestors must be displayable. A containment + * hierarchy is made displayable when a window is packed or made visible. + * + * @return true if the component is displayable + * @see Container#add(Component) + * @see Container#remove(Component) + * @see Window#pack() + * @see Window#show() + * @see Window#dispose() + * @since 1.2 + */ + public boolean isDisplayable() + { + if (parent != null) + return parent.isDisplayable(); + return false; + } + + /** + * Tests whether or not this component is visible. Except for top-level + * frames, components are initially visible. + * + * @return true if the component is visible + * @see #setVisible(boolean) + */ + public boolean isVisible() + { + return visible; + } + + /** + * Tests whether or not this component is actually being shown on + * the screen. This will be true if and only if it this component is + * visible and its parent components are all visible. + * + * @return true if the component is showing on the screen + * @see #setVisible(boolean) + */ + public boolean isShowing() + { + if (! visible || peer == null) + return false; + + return parent == null ? true : parent.isShowing(); + } + + /** + * Tests whether or not this component is enabled. Components are enabled + * by default, and must be enabled to receive user input or generate events. + * + * @return true if the component is enabled + * @see #setEnabled(boolean) + */ + public boolean isEnabled() + { + return enabled; + } + + /** + * Enables or disables this component. The component must be enabled to + * receive events (except that lightweight components always receive mouse + * events). + * + * @param enabled true to enable this component + * + * @see #isEnabled() + * @see #isLightweight() + * + * @since 1.1 + */ + public void setEnabled(boolean enabled) + { + enable(enabled); + } + + /** + * Enables this component. + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void enable() + { + this.enabled = true; + if (peer != null) + peer.setEnabled (true); + } + + /** + * Enables or disables this component. + * + * @param enabled true to enable this component + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void enable(boolean enabled) + { + if (enabled) + enable(); + else + disable(); + } + + /** + * Disables this component. + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void disable() + { + this.enabled = false; + if (peer != null) + peer.setEnabled (false); + } + + /** + * Checks if this image is painted to an offscreen image buffer that is + * later copied to screen (double buffering reduces flicker). This version + * returns false, so subclasses must override it if they provide double + * buffering. + * + * @return true if this is double buffered; defaults to false + */ + public boolean isDoubleBuffered() + { + return false; + } + + /** + * Enables or disables input method support for this component. By default, + * components have this enabled. Input methods are given the opportunity + * to process key events before this component and its listeners. + * + * @param enable true to enable input method processing + * @see #processKeyEvent(KeyEvent) + * @since 1.2 + */ + public void enableInputMethods(boolean enable) + { + if (enable) + eventMask |= AWTEvent.INPUT_ENABLED_EVENT_MASK; + else + eventMask &= ~AWTEvent.INPUT_ENABLED_EVENT_MASK; + } + + /** + * Makes this component visible or invisible. Note that it wtill might + * not show the component, if a parent is invisible. + * + * @param visible true to make this component visible + * + * @see #isVisible() + * + * @since 1.1 + */ + public void setVisible(boolean visible) + { + // Inspection by subclassing shows that Sun's implementation calls + // show(boolean) which then calls show() or hide(). It is the show() + // method that is overriden in subclasses like Window. + show(visible); + } + + /** + * Makes this component visible on the screen. + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void show() + { + // We must set visible before showing the peer. Otherwise the + // peer could post paint events before visible is true, in which + // case lightweight components are not initially painted -- + // Container.paint first calls isShowing () before painting itself + // and its children. + if(!isVisible()) + { + this.visible = true; + if (peer != null) + peer.setVisible(true); + invalidate(); + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + /** + * Makes this component visible or invisible. + * + * @param visible true to make this component visible + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void show(boolean visible) + { + if (visible) + show(); + else + hide(); + } + + /** + * Hides this component so that it is no longer shown on the screen. + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void hide() + { + if (isVisible()) + { + if (peer != null) + peer.setVisible(false); + this.visible = false; + invalidate(); + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + /** + * Returns this component's foreground color. If not set, this is inherited + * from the parent. + * + * @return this component's foreground color, or null + * @see #setForeground(Color) + */ + public Color getForeground() + { + if (foreground != null) + return foreground; + return parent == null ? SystemColor.windowText : parent.getForeground(); + } + + /** + * Sets this component's foreground color to the specified color. This is a + * bound property. + * + * @param c the new foreground color + * @see #getForeground() + */ + public void setForeground(Color c) + { + firePropertyChange("foreground", foreground, c); + if (peer != null) + peer.setForeground(c); + foreground = c; + } + + /** + * Tests if the foreground was explicitly set, or just inherited from the + * parent. + * + * @return true if the foreground has been set + * @since 1.4 + */ + public boolean isForegroundSet() + { + return foreground != null; + } + + /** + * Returns this component's background color. If not set, this is inherited + * from the parent. + * + * @return the background color of the component, or null + * @see #setBackground(Color) + */ + public Color getBackground() + { + if (background != null) + return background; + return parent == null ? SystemColor.window : parent.getBackground(); + } + + /** + * Sets this component's background color to the specified color. The parts + * of the component affected by the background color may by system dependent. + * This is a bound property. + * + * @param c the new background color + * @see #getBackground() + */ + public void setBackground(Color c) + { + // return if the background is already set to that color. + if (background != null && c != null) + if (background.equals(c)) + return; + // If c is null, inherit from closest ancestor whose bg is set. + if (c == null && parent != null) + c = parent.getBackground(); + firePropertyChange("background", background, c); + if (peer != null && c != null) + peer.setBackground(c); + background = c; + } + + /** + * Tests if the background was explicitly set, or just inherited from the + * parent. + * + * @return true if the background has been set + * @since 1.4 + */ + public boolean isBackgroundSet() + { + return background != null; + } + + /** + * Returns the font in use for this component. If not set, this is inherited + * from the parent. + * + * @return the font for this component + * @see #setFont(Font) + */ + public Font getFont() + { + if (font != null) + return font; + + if (parent != null) + return parent.getFont (); + else + return new Font ("Dialog", Font.PLAIN, 12); + } + + /** + * Sets the font for this component to the specified font. This is a bound + * property. + * + * @param newFont the new font for this component + * + * @see #getFont() + */ + public void setFont(Font newFont) + { + if (font == newFont) + return; + + Font oldFont = font; + font = newFont; + if (peer != null) + peer.setFont(font); + firePropertyChange("font", oldFont, newFont); + invalidate(); + } + + /** + * Tests if the font was explicitly set, or just inherited from the parent. + * + * @return true if the font has been set + * @since 1.4 + */ + public boolean isFontSet() + { + return font != null; + } + + /** + * Returns the locale for this component. If this component does not + * have a locale, the locale of the parent component is returned. + * + * @return the locale for this component + * @throws IllegalComponentStateException if it has no locale or parent + * @see #setLocale(Locale) + * @since 1.1 + */ + public Locale getLocale() + { + if (locale != null) + return locale; + if (parent == null) + throw new IllegalComponentStateException + ("Component has no parent: can't determine Locale"); + return parent.getLocale(); + } + + /** + * Sets the locale for this component to the specified locale. This is a + * bound property. + * + * @param newLocale the new locale for this component + */ + public void setLocale(Locale newLocale) + { + if (locale == newLocale) + return; + + Locale oldLocale = locale; + locale = newLocale; + firePropertyChange("locale", oldLocale, newLocale); + // New writing/layout direction or more/less room for localized labels. + invalidate(); + } + + /** + * Returns the color model of the device this componet is displayed on. + * + * @return this object's color model + * @see Toolkit#getColorModel() + */ + public ColorModel getColorModel() + { + GraphicsConfiguration config = getGraphicsConfiguration(); + return config != null ? config.getColorModel() + : getToolkit().getColorModel(); + } + + /** + * Returns the location of this component's top left corner relative to + * its parent component. This may be outdated, so for synchronous behavior, + * you should use a component listner. + * + * @return the location of this component + * @see #setLocation(int, int) + * @see #getLocationOnScreen() + * @since 1.1 + */ + public Point getLocation() + { + return location (); + } + + /** + * Returns the location of this component's top left corner in screen + * coordinates. + * + * @return the location of this component in screen coordinates + * @throws IllegalComponentStateException if the component is not showing + */ + public Point getLocationOnScreen() + { + if (! isShowing()) + throw new IllegalComponentStateException("component " + + getClass().getName() + + " not showing"); + // We know peer != null here. + return peer.getLocationOnScreen(); + } + + /** + * Returns the location of this component's top left corner relative to + * its parent component. + * + * @return the location of this component + * @deprecated use {@link #getLocation()} instead + */ + public Point location() + { + return new Point (x, y); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param x the new X coordinate of this component + * @param y the new Y coordinate of this component + * @see #getLocation() + * @see #setBounds(int, int, int, int) + */ + public void setLocation(int x, int y) + { + move (x, y); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param x the new X coordinate of this component + * @param y the new Y coordinate of this component + * @deprecated use {@link #setLocation(int, int)} instead + */ + public void move(int x, int y) + { + setBounds(x, y, this.width, this.height); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param p new coordinates for this component + * @throws NullPointerException if p is null + * @see #getLocation() + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setLocation(Point p) + { + setLocation(p.x, p.y); + } + + /** + * Returns the size of this object. + * + * @return the size of this object + * @see #setSize(int, int) + * @since 1.1 + */ + public Dimension getSize() + { + return size (); + } + + /** + * Returns the size of this object. + * + * @return the size of this object + * @deprecated use {@link #getSize()} instead + */ + public Dimension size() + { + return new Dimension (width, height); + } + + /** + * Sets the size of this component to the specified width and height. + * + * @param width the new width of this component + * @param height the new height of this component + * @see #getSize() + * @see #setBounds(int, int, int, int) + */ + public void setSize(int width, int height) + { + resize (width, height); + } + + /** + * Sets the size of this component to the specified value. + * + * @param width the new width of the component + * @param height the new height of the component + * @deprecated use {@link #setSize(int, int)} instead + */ + public void resize(int width, int height) + { + setBounds(this.x, this.y, width, height); + } + + /** + * Sets the size of this component to the specified value. + * + * @param d the new size of this component + * @throws NullPointerException if d is null + * @see #setSize(int, int) + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setSize(Dimension d) + { + resize (d); + } + + /** + * Sets the size of this component to the specified value. + * + * @param d the new size of this component + * @throws NullPointerException if d is null + * @deprecated use {@link #setSize(Dimension)} instead + */ + public void resize(Dimension d) + { + resize (d.width, d.height); + } + + /** + * Returns a bounding rectangle for this component. Note that the + * returned rectange is relative to this component's parent, not to + * the screen. + * + * @return the bounding rectangle for this component + * @see #setBounds(int, int, int, int) + * @see #getLocation() + * @see #getSize() + */ + public Rectangle getBounds() + { + return bounds (); + } + + /** + * Returns a bounding rectangle for this component. Note that the + * returned rectange is relative to this component's parent, not to + * the screen. + * + * @return the bounding rectangle for this component + * @deprecated use {@link #getBounds()} instead + */ + public Rectangle bounds() + { + return new Rectangle (x, y, width, height); + } + + /** + * Sets the bounding rectangle for this component to the specified values. + * Note that these coordinates are relative to the parent, not to the screen. + * + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @see #getBounds() + * @see #setLocation(int, int) + * @see #setLocation(Point) + * @see #setSize(int, int) + * @see #setSize(Dimension) + * @since 1.1 + */ + public void setBounds(int x, int y, int w, int h) + { + reshape (x, y, w, h); + } + + /** + * Sets the bounding rectangle for this component to the specified values. + * Note that these coordinates are relative to the parent, not to the screen. + * + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @deprecated use {@link #setBounds(int, int, int, int)} instead + */ + public void reshape(int x, int y, int width, int height) + { + int oldx = this.x; + int oldy = this.y; + int oldwidth = this.width; + int oldheight = this.height; + + if (this.x == x && this.y == y + && this.width == width && this.height == height) + return; + invalidate (); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + if (peer != null) + peer.setBounds (x, y, width, height); + + // Erase old bounds and repaint new bounds for lightweights. + if (isLightweight() && isShowing ()) + { + boolean shouldRepaintParent = false; + boolean shouldRepaintSelf = false; + + if (parent != null) + { + Rectangle parentBounds = parent.getBounds(); + Rectangle oldBounds = new Rectangle(parent.getX() + oldx, + parent.getY() + oldy, + oldwidth, oldheight); + Rectangle newBounds = new Rectangle(parent.getX() + x, + parent.getY() + y, + width, height); + shouldRepaintParent = parentBounds.intersects(oldBounds); + shouldRepaintSelf = parentBounds.intersects(newBounds); + } + + if (shouldRepaintParent && parent != null) + parent.repaint(oldx, oldy, oldwidth, oldheight); + if (shouldRepaintSelf) + repaint(); + } + + // Only post event if this component is visible and has changed size. + if (isShowing () + && (oldx != x || oldy != y)) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_MOVED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + if (isShowing () + && (oldwidth != width || oldheight != height)) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_RESIZED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + /** + * Sets the bounding rectangle for this component to the specified + * rectangle. Note that these coordinates are relative to the parent, not + * to the screen. + * + * @param r the new bounding rectangle + * @throws NullPointerException if r is null + * @see #getBounds() + * @see #setLocation(Point) + * @see #setSize(Dimension) + * @since 1.1 + */ + public void setBounds(Rectangle r) + { + setBounds (r.x, r.y, r.width, r.height); + } + + /** + * Gets the x coordinate of the upper left corner. This is more efficient + * than getBounds().x or getLocation().x. + * + * @return the current x coordinate + * @since 1.2 + */ + public int getX() + { + return x; + } + + /** + * Gets the y coordinate of the upper left corner. This is more efficient + * than getBounds().y or getLocation().y. + * + * @return the current y coordinate + * @since 1.2 + */ + public int getY() + { + return y; + } + + /** + * Gets the width of the component. This is more efficient than + * getBounds().width or getSize().width. + * + * @return the current width + * @since 1.2 + */ + public int getWidth() + { + return width; + } + + /** + * Gets the height of the component. This is more efficient than + * getBounds().height or getSize().height. + * + * @return the current width + * @since 1.2 + */ + public int getHeight() + { + return height; + } + + /** + * Returns the bounds of this component. This allows reuse of an existing + * rectangle, if r is non-null. + * + * @param r the rectangle to use, or null + * @return the bounds + */ + public Rectangle getBounds(Rectangle r) + { + if (r == null) + r = new Rectangle(); + r.x = x; + r.y = y; + r.width = width; + r.height = height; + return r; + } + + /** + * Returns the size of this component. This allows reuse of an existing + * dimension, if d is non-null. + * + * @param d the dimension to use, or null + * @return the size + */ + public Dimension getSize(Dimension d) + { + if (d == null) + d = new Dimension(); + d.width = width; + d.height = height; + return d; + } + + /** + * Returns the location of this component. This allows reuse of an existing + * point, if p is non-null. + * + * @param p the point to use, or null + * @return the location + */ + public Point getLocation(Point p) + { + if (p == null) + p = new Point(); + p.x = x; + p.y = y; + return p; + } + + /** + * Tests if this component is opaque. All "heavyweight" (natively-drawn) + * components are opaque. A component is opaque if it draws all pixels in + * the bounds; a lightweight component is partially transparent if it lets + * pixels underneath show through. Subclasses that guarantee that all pixels + * will be drawn should override this. + * + * @return true if this is opaque + * @see #isLightweight() + * @since 1.2 + */ + public boolean isOpaque() + { + return ! isLightweight(); + } + + /** + * Return whether the component is lightweight. That means the component has + * no native peer, but is displayable. This applies to subclasses of + * Component not in this package, such as javax.swing. + * + * @return true if the component has a lightweight peer + * @see #isDisplayable() + * @since 1.2 + */ + public boolean isLightweight() + { + return peer instanceof LightweightPeer; + } + + /** + * Returns the component's preferred size. + * + * @return the component's preferred size + * @see #getMinimumSize() + * @see LayoutManager + */ + public Dimension getPreferredSize() + { + return preferredSize(); + } + + /** + * Returns the component's preferred size. + * + * @return the component's preferred size + * @deprecated use {@link #getPreferredSize()} instead + */ + public Dimension preferredSize() + { + if (prefSize == null) + if (peer == null) + return new Dimension(width, height); + else + prefSize = peer.getPreferredSize(); + return prefSize; + } + + /** + * Returns the component's minimum size. + * + * @return the component's minimum size + * @see #getPreferredSize() + * @see LayoutManager + */ + public Dimension getMinimumSize() + { + return minimumSize(); + } + + /** + * Returns the component's minimum size. + * + * @return the component's minimum size + * @deprecated use {@link #getMinimumSize()} instead + */ + public Dimension minimumSize() + { + if (minSize == null) + minSize = (peer != null ? peer.getMinimumSize() + : new Dimension(width, height)); + return minSize; + } + + /** + * Returns the component's maximum size. + * + * @return the component's maximum size + * @see #getMinimumSize() + * @see #getPreferredSize() + * @see LayoutManager + */ + public Dimension getMaximumSize() + { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Returns the preferred horizontal alignment of this component. The value + * returned will be between {@link #LEFT_ALIGNMENT} and + * {@link #RIGHT_ALIGNMENT}, inclusive. + * + * @return the preferred horizontal alignment of this component + */ + public float getAlignmentX() + { + return CENTER_ALIGNMENT; + } + + /** + * Returns the preferred vertical alignment of this component. The value + * returned will be between {@link #TOP_ALIGNMENT} and + * {@link #BOTTOM_ALIGNMENT}, inclusive. + * + * @return the preferred vertical alignment of this component + */ + public float getAlignmentY() + { + return CENTER_ALIGNMENT; + } + + /** + * Calls the layout manager to re-layout the component. This is called + * during validation of a container in most cases. + * + * @see #validate() + * @see LayoutManager + */ + public void doLayout() + { + layout (); + } + + /** + * Calls the layout manager to re-layout the component. This is called + * during validation of a container in most cases. + * + * @deprecated use {@link #doLayout()} instead + */ + public void layout() + { + // Nothing to do unless we're a container. + } + + /** + * Called to ensure that the layout for this component is valid. This is + * usually called on containers. + * + * @see #invalidate() + * @see #doLayout() + * @see LayoutManager + * @see Container#validate() + */ + public void validate() + { + valid = true; + } + + /** + * Invalidates this component and all of its parent components. This will + * cause them to have their layout redone. This is called frequently, so + * make it fast. + */ + public void invalidate() + { + valid = false; + prefSize = null; + minSize = null; + if (parent != null && parent.valid) + parent.invalidate(); + } + + /** + * Returns a graphics object for this component. Returns <code>null</code> + * if this component is not currently displayed on the screen. + * + * @return a graphics object for this component + * @see #paint(Graphics) + */ + public Graphics getGraphics() + { + if (peer != null) + { + Graphics gfx = peer.getGraphics(); + if (gfx != null) + return gfx; + // create graphics for lightweight: + Container parent = getParent(); + if (parent != null) + { + gfx = parent.getGraphics(); + Rectangle bounds = getBounds(); + gfx.setClip(bounds); + gfx.translate(bounds.x, bounds.y); + return gfx; + } + } + return null; + } + + /** + * Returns the font metrics for the specified font in this component. + * + * @param font the font to retrieve metrics for + * @return the font metrics for the specified font + * @throws NullPointerException if font is null + * @see #getFont() + * @see Toolkit#getFontMetrics(Font) + */ + public FontMetrics getFontMetrics(Font font) + { + return peer == null ? getToolkit().getFontMetrics(font) + : peer.getFontMetrics(font); + } + + /** + * Sets the cursor for this component to the specified cursor. The cursor + * is displayed when the point is contained by the component, and the + * component is visible, displayable, and enabled. This is inherited by + * subcomponents unless they set their own cursor. + * + * @param cursor the new cursor for this component + * @see #isEnabled() + * @see #isShowing() + * @see #getCursor() + * @see #contains(int, int) + * @see Toolkit#createCustomCursor(Image, Point, String) + */ + public void setCursor(Cursor cursor) + { + this.cursor = cursor; + if (peer != null) + peer.setCursor(cursor); + } + + /** + * Returns the cursor for this component. If not set, this is inherited + * from the parent, or from Cursor.getDefaultCursor(). + * + * @return the cursor for this component + */ + public Cursor getCursor() + { + if (cursor != null) + return cursor; + return parent != null ? parent.getCursor() : Cursor.getDefaultCursor(); + } + + /** + * Tests if the cursor was explicitly set, or just inherited from the parent. + * + * @return true if the cursor has been set + * @since 1.4 + */ + public boolean isCursorSet() + { + return cursor != null; + } + + /** + * Paints this component on the screen. The clipping region in the graphics + * context will indicate the region that requires painting. This is called + * whenever the component first shows, or needs to be repaired because + * something was temporarily drawn on top. It is not necessary for + * subclasses to call <code>super.paint(g)</code>. Components with no area + * are not painted. + * + * @param g the graphics context for this paint job + * @see #update(Graphics) + */ + public void paint(Graphics g) + { + // Paint the heavyweight peer + if (!isLightweight() && peer != null) + peer.paint(g); + } + + /** + * Updates this component. This is called in response to + * <code>repaint</code>. This method fills the component with the + * background color, then sets the foreground color of the specified + * graphics context to the foreground color of this component and calls + * the <code>paint()</code> method. The coordinates of the graphics are + * relative to this component. Subclasses should call either + * <code>super.update(g)</code> or <code>paint(g)</code>. + * + * @param g the graphics context for this update + * + * @see #paint(Graphics) + * @see #repaint() + */ + public void update(Graphics g) + { + if (!isLightweight()) + { + Rectangle clip = g.getClipBounds(); + if (clip == null) + g.clearRect(0, 0, width, height); + else + g.clearRect(clip.x, clip.y, clip.width, clip.height); + } + + paint(g); + } + + /** + * Paints this entire component, including any sub-components. + * + * @param g the graphics context for this paint job + * + * @see #paint(Graphics) + */ + public void paintAll(Graphics g) + { + if (! visible) + return; + paint(g); + } + + /** + * Repaint this entire component. The <code>update()</code> method + * on this component will be called as soon as possible. + * + * @see #update(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint() + { + repaint(0, 0, 0, width, height); + } + + /** + * Repaint this entire component. The <code>update()</code> method on this + * component will be called in approximate the specified number of + * milliseconds. + * + * @param tm milliseconds before this component should be repainted + * @see #paint(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint(long tm) + { + repaint(tm, 0, 0, width, height); + } + + /** + * Repaints the specified rectangular region within this component. The + * <code>update</code> method on this component will be called as soon as + * possible. The coordinates are relative to this component. + * + * @param x the X coordinate of the upper left of the region to repaint + * @param y the Y coordinate of the upper left of the region to repaint + * @param w the width of the region to repaint + * @param h the height of the region to repaint + * @see #update(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint(int x, int y, int w, int h) + { + repaint(0, x, y, w, h); + } + + /** + * Repaints the specified rectangular region within this component. The + * <code>update</code> method on this component will be called in + * approximately the specified number of milliseconds. The coordinates + * are relative to this component. + * + * @param tm milliseconds before this component should be repainted + * @param x the X coordinate of the upper left of the region to repaint + * @param y the Y coordinate of the upper left of the region to repaint + * @param width the width of the region to repaint + * @param height the height of the region to repaint + * @see #update(Graphics) + */ + public void repaint(long tm, int x, int y, int width, int height) + { + // Handle lightweight repainting by forwarding to native parent + if (isLightweight() && parent != null) + { + if (parent != null) + parent.repaint(tm, x + getX(), y + getY(), width, height); + } + else if (peer != null) + peer.repaint(tm, x, y, width, height); + } + + /** + * Prints this component. This method is provided so that printing can be + * done in a different manner from painting. However, the implementation + * in this class simply calls the <code>paint()</code> method. + * + * @param g the graphics context of the print device + * + * @see #paint(Graphics) + */ + public void print(Graphics g) + { + paint(g); + } + + /** + * Prints this component, including all sub-components. This method is + * provided so that printing can be done in a different manner from + * painting. However, the implementation in this class simply calls the + * <code>paintAll()</code> method. + * + * @param g the graphics context of the print device + * + * @see #paintAll(Graphics) + */ + public void printAll(Graphics g) + { + paintAll(g); + } + + /** + * Called when an image has changed so that this component is repainted. + * This incrementally draws an image as more bits are available, when + * possible. Incremental drawing is enabled if the system property + * <code>awt.image.incrementalDraw</code> is not present or is true, in which + * case the redraw rate is set to 100ms or the value of the system property + * <code>awt.image.redrawrate</code>. + * + * <p>The coordinate system used depends on the particular flags. + * + * @param img the image that has been updated + * @param flags tlags as specified in <code>ImageObserver</code> + * @param x the X coordinate + * @param y the Y coordinate + * @param w the width + * @param h the height + * @return false if the image is completely loaded, loading has been + * aborted, or an error has occurred. true if more updates are + * required. + * @see ImageObserver + * @see Graphics#drawImage(Image, int, int, Color, ImageObserver) + * @see Graphics#drawImage(Image, int, int, ImageObserver) + * @see Graphics#drawImage(Image, int, int, int, int, Color, ImageObserver) + * @see Graphics#drawImage(Image, int, int, int, int, ImageObserver) + * @see ImageObserver#imageUpdate(Image, int, int, int, int, int) + */ + public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) + { + if ((flags & (FRAMEBITS | ALLBITS)) != 0) + repaint (); + else if ((flags & SOMEBITS) != 0) + { + if (incrementalDraw) + { + if (redrawRate != null) + { + long tm = redrawRate.longValue(); + if (tm < 0) + tm = 0; + repaint (tm); + } + else + repaint (100); + } + } + return (flags & (ALLBITS | ABORT | ERROR)) == 0; + } + + /** + * Creates an image from the specified producer. + * + * @param producer the image procedure to create the image from + * @return the resulting image + */ + public Image createImage(ImageProducer producer) + { + // Sun allows producer to be null. + if (peer != null) + return peer.createImage(producer); + else + return getToolkit().createImage(producer); + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. + * + * @param width the width of the image + * @param height the height of the image + * @return the requested image, or null if it is not supported + */ + public Image createImage (int width, int height) + { + Image returnValue = null; + if (!GraphicsEnvironment.isHeadless ()) + { + if (isLightweight () && parent != null) + returnValue = parent.createImage (width, height); + else if (peer != null) + returnValue = peer.createImage (width, height); + } + return returnValue; + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. + * + * @param width the width of the image + * @param height the height of the image + * @return the requested image, or null if it is not supported + * @since 1.4 + */ + public VolatileImage createVolatileImage(int width, int height) + { + if (GraphicsEnvironment.isHeadless()) + return null; + GraphicsConfiguration config = getGraphicsConfiguration(); + return config == null ? null + : config.createCompatibleVolatileImage(width, height); + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. The image + * will support the specified capabilities. + * + * @param width the width of the image + * @param height the height of the image + * @param caps the requested capabilities + * @return the requested image, or null if it is not supported + * @throws AWTException if a buffer with the capabilities cannot be created + * @since 1.4 + */ + public VolatileImage createVolatileImage(int width, int height, + ImageCapabilities caps) + throws AWTException + { + if (GraphicsEnvironment.isHeadless()) + return null; + GraphicsConfiguration config = getGraphicsConfiguration(); + return config == null ? null + : config.createCompatibleVolatileImage(width, height, caps); + } + + /** + * Prepares the specified image for rendering on this component. + * + * @param image the image to prepare for rendering + * @param observer the observer to notify of image preparation status + * @return true if the image is already fully prepared + * @throws NullPointerException if image is null + */ + public boolean prepareImage(Image image, ImageObserver observer) + { + return prepareImage(image, image.getWidth(observer), + image.getHeight(observer), observer); + } + + /** + * Prepares the specified image for rendering on this component at the + * specified scaled width and height + * + * @param image the image to prepare for rendering + * @param width the scaled width of the image + * @param height the scaled height of the image + * @param observer the observer to notify of image preparation status + * @return true if the image is already fully prepared + */ + public boolean prepareImage(Image image, int width, int height, + ImageObserver observer) + { + if (peer != null) + return peer.prepareImage(image, width, height, observer); + else + return getToolkit().prepareImage(image, width, height, observer); + } + + /** + * Returns the status of the loading of the specified image. The value + * returned will be those flags defined in <code>ImageObserver</code>. + * + * @param image the image to check on + * @param observer the observer to notify of image loading progress + * @return the image observer flags indicating the status of the load + * @see #prepareImage(Image, int, int, ImageObserver) + * @see Toolkit#checkImage(Image, int, int, ImageObserver) + * @throws NullPointerException if image is null + */ + public int checkImage(Image image, ImageObserver observer) + { + return checkImage(image, -1, -1, observer); + } + + /** + * Returns the status of the loading of the specified image. The value + * returned will be those flags defined in <code>ImageObserver</code>. + * + * @param image the image to check on + * @param width the scaled image width + * @param height the scaled image height + * @param observer the observer to notify of image loading progress + * @return the image observer flags indicating the status of the load + * @see #prepareImage(Image, int, int, ImageObserver) + * @see Toolkit#checkImage(Image, int, int, ImageObserver) + */ + public int checkImage(Image image, int width, int height, + ImageObserver observer) + { + if (peer != null) + return peer.checkImage(image, width, height, observer); + return getToolkit().checkImage(image, width, height, observer); + } + + /** + * Sets whether paint messages delivered by the operating system should be + * ignored. This does not affect messages from AWT, except for those + * triggered by OS messages. Setting this to true can allow faster + * performance in full-screen mode or page-flipping. + * + * @param ignoreRepaint the new setting for ignoring repaint events + * @see #getIgnoreRepaint() + * @see BufferStrategy + * @see GraphicsDevice#setFullScreenWindow(Window) + * @since 1.4 + */ + public void setIgnoreRepaint(boolean ignoreRepaint) + { + this.ignoreRepaint = ignoreRepaint; + } + + /** + * Test whether paint events from the operating system are ignored. + * + * @return the status of ignoring paint events + * @see #setIgnoreRepaint(boolean) + * @since 1.4 + */ + public boolean getIgnoreRepaint() + { + return ignoreRepaint; + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is within this component + * @see #getComponentAt(int, int) + */ + public boolean contains(int x, int y) + { + return inside (x, y); + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is within this component + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return x >= 0 && y >= 0 && x < width && y < height; + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param p the point to test + * @return true if the point is within this component + * @throws NullPointerException if p is null + * @see #getComponentAt(Point) + * @since 1.1 + */ + public boolean contains(Point p) + { + return contains (p.x, p.y); + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or <code>null</code> + * if neither of the first two occupies the specified location. + * + * @param x the X coordinate to search for components at + * @param y the Y coordinate to search for components at + * @return the component at the specified location, or null + * @see #contains(int, int) + */ + public Component getComponentAt(int x, int y) + { + return locate (x, y); + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or <code>null</code> + * if neither of the first two occupies the specified location. + * + * @param x the X coordinate to search for components at + * @param y the Y coordinate to search for components at + * @return the component at the specified location, or null + * @deprecated use {@link #getComponentAt(int, int)} instead + */ + public Component locate(int x, int y) + { + return contains (x, y) ? this : null; + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or <code>null</code> + * if neither of the first two occupies the specified location. + * + * @param p the point to search for components at + * @return the component at the specified location, or null + * @throws NullPointerException if p is null + * @see #contains(Point) + * @since 1.1 + */ + public Component getComponentAt(Point p) + { + return getComponentAt (p.x, p.y); + } + + /** + * AWT 1.0 event delivery. + * + * Deliver an AWT 1.0 event to this Component. This method simply + * calls {@link #postEvent}. + * + * @param e the event to deliver + * @deprecated use {@link #dispatchEvent (AWTEvent)} instead + */ + public void deliverEvent (Event e) + { + postEvent (e); + } + + /** + * Forwards AWT events to processEvent() if:<ul> + * <li>Events have been enabled for this type of event via + * <code>enableEvents()</code></li>, + * <li>There is at least one registered listener for this type of event</li> + * </ul> + * + * @param e the event to dispatch + */ + public final void dispatchEvent(AWTEvent e) + { + // Some subclasses in the AWT package need to override this behavior, + // hence the use of dispatchEventImpl(). + dispatchEventImpl(e); + if (peer != null && ! e.consumed) + peer.handleEvent(e); + } + + /** + * AWT 1.0 event handler. + * + * This method simply calls handleEvent and returns the result. + * + * @param e the event to handle + * @return true if the event was handled, false otherwise + * @deprecated use {@link #dispatchEvent(AWTEvent)} instead + */ + public boolean postEvent (Event e) + { + boolean handled = handleEvent (e); + + if (!handled && getParent() != null) + // FIXME: need to translate event coordinates to parent's + // coordinate space. + handled = getParent ().postEvent (e); + + return handled; + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see ComponentEvent + * @see #removeComponentListener(ComponentListener) + * @see #getComponentListeners() + * @since 1.1 + */ + public synchronized void addComponentListener(ComponentListener listener) + { + componentListener = AWTEventMulticaster.add(componentListener, listener); + if (componentListener != null) + enableEvents(AWTEvent.COMPONENT_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see ComponentEvent + * @see #addComponentListener(ComponentListener) + * @see #getComponentListeners() + * @since 1.1 + */ + public synchronized void removeComponentListener(ComponentListener listener) + { + componentListener = AWTEventMulticaster.remove(componentListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addComponentListener(ComponentListener) + * @see #removeComponentListener(ComponentListener) + * @since 1.4 + */ + public synchronized ComponentListener[] getComponentListeners() + { + return (ComponentListener[]) + AWTEventMulticaster.getListeners(componentListener, + ComponentListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see FocusEvent + * @see #removeFocusListener(FocusListener) + * @see #getFocusListeners() + * @since 1.1 + */ + public synchronized void addFocusListener(FocusListener listener) + { + focusListener = AWTEventMulticaster.add(focusListener, listener); + if (focusListener != null) + enableEvents(AWTEvent.FOCUS_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see FocusEvent + * @see #addFocusListener(FocusListener) + * @see #getFocusListeners() + * @since 1.1 + */ + public synchronized void removeFocusListener(FocusListener listener) + { + focusListener = AWTEventMulticaster.remove(focusListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addFocusListener(FocusListener) + * @see #removeFocusListener(FocusListener) + * @since 1.4 + */ + public synchronized FocusListener[] getFocusListeners() + { + return (FocusListener[]) + AWTEventMulticaster.getListeners(focusListener, FocusListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see HierarchyEvent + * @see #removeHierarchyListener(HierarchyListener) + * @see #getHierarchyListeners() + * @since 1.3 + */ + public synchronized void addHierarchyListener(HierarchyListener listener) + { + hierarchyListener = AWTEventMulticaster.add(hierarchyListener, listener); + if (hierarchyListener != null) + enableEvents(AWTEvent.HIERARCHY_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see HierarchyEvent + * @see #addHierarchyListener(HierarchyListener) + * @see #getHierarchyListeners() + * @since 1.3 + */ + public synchronized void removeHierarchyListener(HierarchyListener listener) + { + hierarchyListener = AWTEventMulticaster.remove(hierarchyListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addHierarchyListener(HierarchyListener) + * @see #removeHierarchyListener(HierarchyListener) + * @since 1.4 + */ + public synchronized HierarchyListener[] getHierarchyListeners() + { + return (HierarchyListener[]) + AWTEventMulticaster.getListeners(hierarchyListener, + HierarchyListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see HierarchyEvent + * @see #removeHierarchyBoundsListener(HierarchyBoundsListener) + * @see #getHierarchyBoundsListeners() + * @since 1.3 + */ + public synchronized void + addHierarchyBoundsListener(HierarchyBoundsListener listener) + { + hierarchyBoundsListener = + AWTEventMulticaster.add(hierarchyBoundsListener, listener); + if (hierarchyBoundsListener != null) + enableEvents(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see HierarchyEvent + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #getHierarchyBoundsListeners() + * @since 1.3 + */ + public synchronized void + removeHierarchyBoundsListener(HierarchyBoundsListener listener) + { + hierarchyBoundsListener = + AWTEventMulticaster.remove(hierarchyBoundsListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #removeHierarchyBoundsListener(HierarchyBoundsListener) + * @since 1.4 + */ + public synchronized HierarchyBoundsListener[] getHierarchyBoundsListeners() + { + return (HierarchyBoundsListener[]) + AWTEventMulticaster.getListeners(hierarchyBoundsListener, + HierarchyBoundsListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see KeyEvent + * @see #removeKeyListener(KeyListener) + * @see #getKeyListeners() + * @since 1.1 + */ + public synchronized void addKeyListener(KeyListener listener) + { + keyListener = AWTEventMulticaster.add(keyListener, listener); + if (keyListener != null) + enableEvents(AWTEvent.KEY_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see KeyEvent + * @see #addKeyListener(KeyListener) + * @see #getKeyListeners() + * @since 1.1 + */ + public synchronized void removeKeyListener(KeyListener listener) + { + keyListener = AWTEventMulticaster.remove(keyListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addKeyListener(KeyListener) + * @see #removeKeyListener(KeyListener) + * @since 1.4 + */ + public synchronized KeyListener[] getKeyListeners() + { + return (KeyListener[]) + AWTEventMulticaster.getListeners(keyListener, KeyListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see #removeMouseListener(MouseListener) + * @see #getMouseListeners() + * @since 1.1 + */ + public synchronized void addMouseListener(MouseListener listener) + { + mouseListener = AWTEventMulticaster.add(mouseListener, listener); + if (mouseListener != null) + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see #addMouseListener(MouseListener) + * @see #getMouseListeners() + * @since 1.1 + */ + public synchronized void removeMouseListener(MouseListener listener) + { + mouseListener = AWTEventMulticaster.remove(mouseListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseListener(MouseListener) + * @see #removeMouseListener(MouseListener) + * @since 1.4 + */ + public synchronized MouseListener[] getMouseListeners() + { + return (MouseListener[]) + AWTEventMulticaster.getListeners(mouseListener, MouseListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see #removeMouseMotionListener(MouseMotionListener) + * @see #getMouseMotionListeners() + * @since 1.1 + */ + public synchronized void addMouseMotionListener(MouseMotionListener listener) + { + mouseMotionListener = AWTEventMulticaster.add(mouseMotionListener, listener); + if (mouseMotionListener != null) + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see #addMouseMotionListener(MouseMotionListener) + * @see #getMouseMotionListeners() + * @since 1.1 + */ + public synchronized void removeMouseMotionListener(MouseMotionListener listener) + { + mouseMotionListener = AWTEventMulticaster.remove(mouseMotionListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseMotionListener(MouseMotionListener) + * @see #removeMouseMotionListener(MouseMotionListener) + * @since 1.4 + */ + public synchronized MouseMotionListener[] getMouseMotionListeners() + { + return (MouseMotionListener[]) + AWTEventMulticaster.getListeners(mouseMotionListener, + MouseMotionListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see MouseWheelEvent + * @see #removeMouseWheelListener(MouseWheelListener) + * @see #getMouseWheelListeners() + * @since 1.4 + */ + public synchronized void addMouseWheelListener(MouseWheelListener listener) + { + mouseWheelListener = AWTEventMulticaster.add(mouseWheelListener, listener); + if (mouseWheelListener != null) + enableEvents(AWTEvent.MOUSE_WHEEL_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see MouseWheelEvent + * @see #addMouseWheelListener(MouseWheelListener) + * @see #getMouseWheelListeners() + * @since 1.4 + */ + public synchronized void removeMouseWheelListener(MouseWheelListener listener) + { + mouseWheelListener = AWTEventMulticaster.remove(mouseWheelListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseWheelListener(MouseWheelListener) + * @see #removeMouseWheelListener(MouseWheelListener) + * @since 1.4 + */ + public synchronized MouseWheelListener[] getMouseWheelListeners() + { + return (MouseWheelListener[]) + AWTEventMulticaster.getListeners(mouseWheelListener, + MouseWheelListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see InputMethodEvent + * @see #removeInputMethodListener(InputMethodListener) + * @see #getInputMethodListeners() + * @see #getInputMethodRequests() + * @since 1.2 + */ + public synchronized void addInputMethodListener(InputMethodListener listener) + { + inputMethodListener = AWTEventMulticaster.add(inputMethodListener, listener); + if (inputMethodListener != null) + enableEvents(AWTEvent.INPUT_METHOD_EVENT_MASK); + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see InputMethodEvent + * @see #addInputMethodListener(InputMethodListener) + * @see #getInputMethodRequests() + * @since 1.2 + */ + public synchronized void removeInputMethodListener(InputMethodListener listener) + { + inputMethodListener = AWTEventMulticaster.remove(inputMethodListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addInputMethodListener(InputMethodListener) + * @see #removeInputMethodListener(InputMethodListener) + * @since 1.4 + */ + public synchronized InputMethodListener[] getInputMethodListeners() + { + return (InputMethodListener[]) + AWTEventMulticaster.getListeners(inputMethodListener, + InputMethodListener.class); + } + + /** + * Returns all registered EventListers of the given listenerType. + * + * @param listenerType the class of listeners to filter + * @return an array of registered listeners + * @see #getComponentListeners() + * @see #getFocusListeners() + * @see #getHierarchyListeners() + * @see #getHierarchyBoundsListeners() + * @see #getKeyListeners() + * @see #getMouseListeners() + * @see #getMouseMotionListeners() + * @see #getMouseWheelListeners() + * @see #getInputMethodListeners() + * @see #getPropertyChangeListeners() + * @since 1.3 + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == ComponentListener.class) + return getComponentListeners(); + if (listenerType == FocusListener.class) + return getFocusListeners(); + if (listenerType == HierarchyListener.class) + return getHierarchyListeners(); + if (listenerType == HierarchyBoundsListener.class) + return getHierarchyBoundsListeners(); + if (listenerType == KeyListener.class) + return getKeyListeners(); + if (listenerType == MouseListener.class) + return getMouseListeners(); + if (listenerType == MouseMotionListener.class) + return getMouseMotionListeners(); + if (listenerType == MouseWheelListener.class) + return getMouseWheelListeners(); + if (listenerType == InputMethodListener.class) + return getInputMethodListeners(); + if (listenerType == PropertyChangeListener.class) + return getPropertyChangeListeners(); + return (EventListener[]) Array.newInstance(listenerType, 0); + } + + /** + * Returns the input method request handler, for subclasses which support + * on-the-spot text input. By default, input methods are handled by AWT, + * and this returns null. + * + * @return the input method handler, null by default + * @since 1.2 + */ + public InputMethodRequests getInputMethodRequests() + { + return null; + } + + /** + * Gets the input context of this component, which is inherited from the + * parent unless this is overridden. + * + * @return the text input context + * @since 1.2 + */ + public InputContext getInputContext() + { + return parent == null ? null : parent.getInputContext(); + } + + /** + * Enables the specified events. The events to enable are specified + * by OR-ing together the desired masks from <code>AWTEvent</code>. + * + * <p>Events are enabled by default when a listener is attached to the + * component for that event type. This method can be used by subclasses + * to ensure the delivery of a specified event regardless of whether + * or not a listener is attached. + * + * @param eventsToEnable the desired events to enable + * @see #processEvent(AWTEvent) + * @see #disableEvents(long) + * @see AWTEvent + * @since 1.1 + */ + protected final void enableEvents(long eventsToEnable) + { + eventMask |= eventsToEnable; + // TODO: Unlike Sun's implementation, I think we should try and + // enable/disable events at the peer (gtk/X) level. This will avoid + // clogging the event pipeline with useless mousemove events that + // we arn't interested in, etc. This will involve extending the peer + // interface, but thats okay because the peer interfaces have been + // deprecated for a long time, and no longer feature in the + // API specification at all. + if (isLightweight() && parent != null) + parent.enableEvents(eventsToEnable); + else if (peer != null) + peer.setEventMask(eventMask); + } + + /** + * Disables the specified events. The events to disable are specified + * by OR-ing together the desired masks from <code>AWTEvent</code>. + * + * @param eventsToDisable the desired events to disable + * @see #enableEvents(long) + * @since 1.1 + */ + protected final void disableEvents(long eventsToDisable) + { + eventMask &= ~eventsToDisable; + // forward new event mask to peer? + } + + /** + * This is called by the EventQueue if two events with the same event id + * and owner component are queued. Returns a new combined event, or null if + * no combining is done. The coelesced events are currently mouse moves + * (intermediate ones are discarded) and paint events (a merged paint is + * created in place of the two events). + * + * @param existingEvent the event on the queue + * @param newEvent the new event that might be entered on the queue + * @return null if both events are kept, or the replacement coelesced event + */ + protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) + { + switch (existingEvent.id) + { + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_DRAGGED: + // Just drop the old (intermediate) event and return the new one. + return newEvent; + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + return coalescePaintEvents((PaintEvent) existingEvent, + (PaintEvent) newEvent); + default: + return null; + } + } + + /** + * Processes the specified event. In this class, this method simply + * calls one of the more specific event handlers. + * + * @param e the event to process + * @throws NullPointerException if e is null + * @see #processComponentEvent(ComponentEvent) + * @see #processFocusEvent(FocusEvent) + * @see #processKeyEvent(KeyEvent) + * @see #processMouseEvent(MouseEvent) + * @see #processMouseMotionEvent(MouseEvent) + * @see #processInputMethodEvent(InputMethodEvent) + * @see #processHierarchyEvent(HierarchyEvent) + * @see #processMouseWheelEvent(MouseWheelEvent) + * @since 1.1 + */ + protected void processEvent(AWTEvent e) + { + /* Note: the order of these if statements are + important. Subclasses must be checked first. Eg. MouseEvent + must be checked before ComponentEvent, since a MouseEvent + object is also an instance of a ComponentEvent. */ + + if (e instanceof FocusEvent) + processFocusEvent((FocusEvent) e); + else if (e instanceof MouseWheelEvent) + processMouseWheelEvent((MouseWheelEvent) e); + else if (e instanceof MouseEvent) + { + if (e.id == MouseEvent.MOUSE_MOVED + || e.id == MouseEvent.MOUSE_DRAGGED) + processMouseMotionEvent((MouseEvent) e); + else + processMouseEvent((MouseEvent) e); + } + else if (e instanceof KeyEvent) + processKeyEvent((KeyEvent) e); + else if (e instanceof InputMethodEvent) + processInputMethodEvent((InputMethodEvent) e); + else if (e instanceof ComponentEvent) + processComponentEvent((ComponentEvent) e); + else if (e instanceof HierarchyEvent) + { + if (e.id == HierarchyEvent.HIERARCHY_CHANGED) + processHierarchyEvent((HierarchyEvent) e); + else + processHierarchyBoundsEvent((HierarchyEvent) e); + } + } + + /** + * Called when a component event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the <code>ComponentEvent</code> to process + * @throws NullPointerException if e is null + * @see ComponentListener + * @see #addComponentListener(ComponentListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processComponentEvent(ComponentEvent e) + { + if (componentListener == null) + return; + switch (e.id) + { + case ComponentEvent.COMPONENT_HIDDEN: + componentListener.componentHidden(e); + break; + case ComponentEvent.COMPONENT_MOVED: + componentListener.componentMoved(e); + break; + case ComponentEvent.COMPONENT_RESIZED: + componentListener.componentResized(e); + break; + case ComponentEvent.COMPONENT_SHOWN: + componentListener.componentShown(e); + break; + } + } + + /** + * Called when a focus event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the <code>FocusEvent</code> to process + * @throws NullPointerException if e is null + * @see FocusListener + * @see #addFocusListener(FocusListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processFocusEvent(FocusEvent e) + { + if (focusListener == null) + return; + + switch (e.id) + { + case FocusEvent.FOCUS_GAINED: + focusListener.focusGained(e); + break; + case FocusEvent.FOCUS_LOST: + focusListener.focusLost(e); + break; + } + } + + /** + * Called when a key event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the <code>KeyEvent</code> to process + * @throws NullPointerException if e is null + * @see KeyListener + * @see #addKeyListener(KeyListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processKeyEvent(KeyEvent e) + { + if (keyListener == null) + return; + switch (e.id) + { + case KeyEvent.KEY_PRESSED: + keyListener.keyPressed(e); + break; + case KeyEvent.KEY_RELEASED: + keyListener.keyReleased(e); + break; + case KeyEvent.KEY_TYPED: + keyListener.keyTyped(e); + break; + } + } + + /** + * Called when a regular mouse event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the <code>MouseEvent</code> to process + * @throws NullPointerException if e is null + * @see MouseListener + * @see #addMouseListener(MouseListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processMouseEvent(MouseEvent e) + { + if (mouseListener == null) + return; + switch (e.id) + { + case MouseEvent.MOUSE_CLICKED: + mouseListener.mouseClicked(e); + break; + case MouseEvent.MOUSE_ENTERED: + mouseListener.mouseEntered(e); + break; + case MouseEvent.MOUSE_EXITED: + mouseListener.mouseExited(e); + break; + case MouseEvent.MOUSE_PRESSED: + mouseListener.mousePressed(e); + break; + case MouseEvent.MOUSE_RELEASED: + mouseListener.mouseReleased(e); + break; + } + e.consume(); + } + + /** + * Called when a mouse motion event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the <code>MouseMotionEvent</code> to process + * @throws NullPointerException if e is null + * @see MouseMotionListener + * @see #addMouseMotionListener(MouseMotionListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processMouseMotionEvent(MouseEvent e) + { + if (mouseMotionListener == null) + return; + switch (e.id) + { + case MouseEvent.MOUSE_DRAGGED: + mouseMotionListener.mouseDragged(e); + break; + case MouseEvent.MOUSE_MOVED: + mouseMotionListener.mouseMoved(e); + break; + } + e.consume(); + } + + /** + * Called when a mouse wheel event is dispatched and component events are + * enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the <code>MouseWheelEvent</code> to process + * @throws NullPointerException if e is null + * @see MouseWheelListener + * @see #addMouseWheelListener(MouseWheelListener) + * @see #enableEvents(long) + * @since 1.4 + */ + protected void processMouseWheelEvent(MouseWheelEvent e) + { + if (mouseWheelListener != null + && e.id == MouseEvent.MOUSE_WHEEL) + { + mouseWheelListener.mouseWheelMoved(e); + e.consume(); + } + } + + /** + * Called when an input method event is dispatched and component events are + * enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the <code>InputMethodEvent</code> to process + * @throws NullPointerException if e is null + * @see InputMethodListener + * @see #addInputMethodListener(InputMethodListener) + * @see #enableEvents(long) + * @since 1.2 + */ + protected void processInputMethodEvent(InputMethodEvent e) + { + if (inputMethodListener == null) + return; + switch (e.id) + { + case InputMethodEvent.CARET_POSITION_CHANGED: + inputMethodListener.caretPositionChanged(e); + break; + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + inputMethodListener.inputMethodTextChanged(e); + break; + } + } + + /** + * Called when a hierarchy change event is dispatched and component events + * are enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the <code>HierarchyEvent</code> to process + * @throws NullPointerException if e is null + * @see HierarchyListener + * @see #addHierarchyListener(HierarchyListener) + * @see #enableEvents(long) + * @since 1.3 + */ + protected void processHierarchyEvent(HierarchyEvent e) + { + if (hierarchyListener == null) + return; + if (e.id == HierarchyEvent.HIERARCHY_CHANGED) + hierarchyListener.hierarchyChanged(e); + } + + /** + * Called when a hierarchy bounds event is dispatched and component events + * are enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the <code>HierarchyEvent</code> to process + * @throws NullPointerException if e is null + * @see HierarchyBoundsListener + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #enableEvents(long) + * @since 1.3 + */ + protected void processHierarchyBoundsEvent(HierarchyEvent e) + { + if (hierarchyBoundsListener == null) + return; + switch (e.id) + { + case HierarchyEvent.ANCESTOR_MOVED: + hierarchyBoundsListener.ancestorMoved(e); + break; + case HierarchyEvent.ANCESTOR_RESIZED: + hierarchyBoundsListener.ancestorResized(e); + break; + } + } + + /** + * AWT 1.0 event handler. + * + * This method calls one of the event-specific handler methods. For + * example for key events, either {@link #keyDown(Event,int)} + * or {@link #keyUp(Event,int)} is called. A derived + * component can override one of these event-specific methods if it + * only needs to handle certain event types. Otherwise it can + * override handleEvent itself and handle any event. + * + * @param evt the event to handle + * @return true if the event was handled, false otherwise + * @deprecated use {@link #processEvent(AWTEvent)} instead + */ + public boolean handleEvent (Event evt) + { + switch (evt.id) + { + // Handle key events. + case Event.KEY_ACTION: + case Event.KEY_PRESS: + return keyDown (evt, evt.key); + case Event.KEY_ACTION_RELEASE: + case Event.KEY_RELEASE: + return keyUp (evt, evt.key); + + // Handle mouse events. + case Event.MOUSE_DOWN: + return mouseDown (evt, evt.x, evt.y); + case Event.MOUSE_UP: + return mouseUp (evt, evt.x, evt.y); + case Event.MOUSE_MOVE: + return mouseMove (evt, evt.x, evt.y); + case Event.MOUSE_DRAG: + return mouseDrag (evt, evt.x, evt.y); + case Event.MOUSE_ENTER: + return mouseEnter (evt, evt.x, evt.y); + case Event.MOUSE_EXIT: + return mouseExit (evt, evt.x, evt.y); + + // Handle focus events. + case Event.GOT_FOCUS: + return gotFocus (evt, evt.arg); + case Event.LOST_FOCUS: + return lostFocus (evt, evt.arg); + + // Handle action event. + case Event.ACTION_EVENT: + return action (evt, evt.arg); + } + // Unknown event. + return false; + } + + /** + * AWT 1.0 MOUSE_DOWN event handler. This method is meant to be + * overridden by components providing their own MOUSE_DOWN handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseDown(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_DRAG event handler. This method is meant to be + * overridden by components providing their own MOUSE_DRAG handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseMotionEvent(MouseEvent)} instead + */ + public boolean mouseDrag(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_UP event handler. This method is meant to be + * overridden by components providing their own MOUSE_UP handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseUp(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_MOVE event handler. This method is meant to be + * overridden by components providing their own MOUSE_MOVE handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseMotionEvent(MouseEvent)} instead + */ + public boolean mouseMove(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_ENTER event handler. This method is meant to be + * overridden by components providing their own MOUSE_ENTER handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseEnter(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_EXIT event handler. This method is meant to be + * overridden by components providing their own MOUSE_EXIT handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseExit(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 KEY_PRESS and KEY_ACTION event handler. This method is + * meant to be overridden by components providing their own key + * press handler. The default implementation simply returns false. + * + * @param evt the event to handle + * @param key the key pressed, ignored + * @return false + * @deprecated use {@link #processKeyEvent(KeyEvent)} instead + */ + public boolean keyDown(Event evt, int key) + { + return false; + } + + /** + * AWT 1.0 KEY_RELEASE and KEY_ACTION_RELEASE event handler. This + * method is meant to be overridden by components providing their + * own key release handler. The default implementation simply + * returns false. + * + * @param evt the event to handle + * @param key the key pressed, ignored + * @return false + * @deprecated use {@link #processKeyEvent(KeyEvent)} instead + */ + public boolean keyUp(Event evt, int key) + { + return false; + } + + /** + * AWT 1.0 ACTION_EVENT event handler. This method is meant to be + * overridden by components providing their own action event + * handler. The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the object acted on, ignored + * @return false + * @deprecated in classes which support actions, use + * <code>processActionEvent(ActionEvent)</code> instead + */ + public boolean action(Event evt, Object what) + { + return false; + } + + /** + * Called to inform this component it has been added to a container. + * A native peer - if any - is created at this time. This method is + * called automatically by the AWT system and should not be called by + * user level code. + * + * @see #isDisplayable() + * @see #removeNotify() + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createComponent(this); + /* Now that all the children has gotten their peers, we should + have the event mask needed for this component and its + lightweight subcomponents. */ + peer.setEventMask(eventMask); + /* We do not invalidate here, but rather leave that job up to + the peer. For efficiency, the peer can choose not to + invalidate if it is happy with the current dimensions, + etc. */ + } + + /** + * Called to inform this component is has been removed from its + * container. Its native peer - if any - is destroyed at this time. + * This method is called automatically by the AWT system and should + * not be called by user level code. + * + * @see #isDisplayable() + * @see #addNotify() + */ + public void removeNotify() + { + // We null our peer field before disposing of it, such that if we're + // not the event dispatch thread and the dispatch thread is awoken by + // the dispose call, there will be no race checking the peer's null + // status. + + ComponentPeer tmp = peer; + peer = null; + if (tmp != null) + tmp.dispose(); + } + + /** + * AWT 1.0 GOT_FOCUS event handler. This method is meant to be + * overridden by components providing their own GOT_FOCUS handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the Object focused, ignored + * @return false + * @deprecated use {@link #processFocusEvent(FocusEvent)} instead + */ + public boolean gotFocus(Event evt, Object what) + { + return false; + } + + /** + * AWT 1.0 LOST_FOCUS event handler. This method is meant to be + * overridden by components providing their own LOST_FOCUS handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the Object focused, ignored + * @return false + * @deprecated use {@link #processFocusEvent(FocusEvent)} instead + */ + public boolean lostFocus(Event evt, Object what) + { + return false; + } + + /** + * Tests whether or not this component is in the group that can be + * traversed using the keyboard traversal mechanism (such as the TAB key). + * + * @return true if the component is traversed via the TAB key + * @see #setFocusable(boolean) + * @since 1.1 + * @deprecated use {@link #isFocusable()} instead + */ + public boolean isFocusTraversable() + { + return enabled && visible && (peer == null || isLightweight() || peer.isFocusTraversable()); + } + + /** + * Tests if this component can receive focus. + * + * @return true if this component can receive focus + * @since 1.4 + */ + public boolean isFocusable() + { + return focusable; + } + + /** + * Specify whether this component can receive focus. This method also + * sets the {@link #isFocusTraversableOverridden} field to 1, which + * appears to be the undocumented way {@link + * DefaultFocusTraversalPolicy#accept(Component)} determines whether to + * respect the {@link #isFocusable()} method of the component. + * + * @param focusable the new focusable status + * @since 1.4 + */ + public void setFocusable(boolean focusable) + { + firePropertyChange("focusable", this.focusable, focusable); + this.focusable = focusable; + this.isFocusTraversableOverridden = 1; + } + + /** + * Sets the focus traversal keys for one of the three focus + * traversal directions supported by Components: + * {@link KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS}, + * {@link KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS}, or + * {@link KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS}. Normally, the + * default values should match the operating system's native + * choices. To disable a given traversal, use + * <code>Collections.EMPTY_SET</code>. The event dispatcher will + * consume PRESSED, RELEASED, and TYPED events for the specified + * key, although focus can only transfer on PRESSED or RELEASED. + * + * <p>The defaults are: + * <table> + * <th><td>Identifier</td><td>Meaning</td><td>Default</td></th> + * <tr><td>KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS</td> + * <td>Normal forward traversal</td> + * <td>TAB on KEY_PRESSED, Ctrl-TAB on KEY_PRESSED</td></tr> + * <tr><td>KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS</td> + * <td>Normal backward traversal</td> + * <td>Shift-TAB on KEY_PRESSED, Ctrl-Shift-TAB on KEY_PRESSED</td></tr> + * <tr><td>KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS</td> + * <td>Go up a traversal cycle</td><td>None</td></tr> + * </table> + * + * If keystrokes is null, this component's focus traversal key set + * is inherited from one of its ancestors. If none of its ancestors + * has its own set of focus traversal keys, the focus traversal keys + * are set to the defaults retrieved from the current + * KeyboardFocusManager. If not null, the set must contain only + * AWTKeyStrokes that are not already focus keys and are not + * KEY_TYPED events. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, or + * UP_CYCLE_TRAVERSAL_KEYS + * @param keystrokes a set of keys, or null + * @throws IllegalArgumentException if id or keystrokes is invalid + * @see #getFocusTraversalKeys(int) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * @since 1.4 + */ + public void setFocusTraversalKeys(int id, Set keystrokes) + { + if (keystrokes == null) + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + + Set sa; + Set sb; + String name; + switch (id) + { + case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "forwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "backwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + name = "upCycleFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + + if (focusTraversalKeys == null) + focusTraversalKeys = new Set[3]; + + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); + + focusTraversalKeys[id] = keystrokes; + } + + /** + * Returns the set of keys for a given focus traversal action, as + * defined in <code>setFocusTraversalKeys</code>. If not set, this + * is inherited from the parent component, which may have gotten it + * from the KeyboardFocusManager. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS + * + * @return set of traversal keys + * + * @throws IllegalArgumentException if id is invalid + * + * @see #setFocusTraversalKeys (int, Set) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * + * @since 1.4 + */ + public Set getFocusTraversalKeys (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException(); + + Set s = null; + + if (focusTraversalKeys != null) + s = focusTraversalKeys[id]; + + if (s == null && parent != null) + s = parent.getFocusTraversalKeys (id); + + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getDefaultFocusTraversalKeys(id)) : s; + } + + /** + * Tests whether the focus traversal keys for a given action are explicitly + * set or inherited. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS + * @return true if that set is explicitly specified + * @throws IllegalArgumentException if id is invalid + * @see #getFocusTraversalKeys (int) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * @since 1.4 + */ + public boolean areFocusTraversalKeysSet (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + return focusTraversalKeys != null && focusTraversalKeys[id] != null; + } + + /** + * Enable or disable focus traversal keys on this Component. If + * they are, then the keyboard focus manager consumes and acts on + * key press and release events that trigger focus traversal, and + * discards the corresponding key typed events. If focus traversal + * keys are disabled, then all key events that would otherwise + * trigger focus traversal are sent to this Component. + * + * @param focusTraversalKeysEnabled the new value of the flag + * @see #getFocusTraversalKeysEnabled () + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) + * @since 1.4 + */ + public void setFocusTraversalKeysEnabled (boolean focusTraversalKeysEnabled) + { + firePropertyChange ("focusTraversalKeysEnabled", + this.focusTraversalKeysEnabled, + focusTraversalKeysEnabled); + this.focusTraversalKeysEnabled = focusTraversalKeysEnabled; + } + + /** + * Check whether or not focus traversal keys are enabled on this + * Component. If they are, then the keyboard focus manager consumes + * and acts on key press and release events that trigger focus + * traversal, and discards the corresponding key typed events. If + * focus traversal keys are disabled, then all key events that would + * otherwise trigger focus traversal are sent to this Component. + * + * @return true if focus traversal keys are enabled + * @see #setFocusTraversalKeysEnabled (boolean) + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) + * @since 1.4 + */ + public boolean getFocusTraversalKeysEnabled () + { + return focusTraversalKeysEnabled; + } + + /** + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and showing and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. + * + * The behaviour of this method is platform-dependent. + * {@link #requestFocusInWindow()} should be used instead. + * + * @see #requestFocusInWindow () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + */ + public void requestFocus () + { + if (isDisplayable () + && isShowing () + && isFocusable ()) + { + synchronized (getTreeLock ()) + { + // Find this Component's top-level ancestor. + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + if (toplevel.isFocusableWindow ()) + { + if (peer != null && !isLightweight()) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + synchronized (eq) + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); + if (currentFocusOwner != null) + { + eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, + false, this)); + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false, + currentFocusOwner)); + } + else + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false)); + } + } + } + else + pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED); + } + } + } + + /** + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and showing and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. + * + * The behaviour of this method is platform-dependent. + * {@link #requestFocusInWindow()} should be used instead. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. + * + * @param temporary true if the focus request is temporary + * @return true if the request has a chance of success + * @see #requestFocusInWindow () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + protected boolean requestFocus (boolean temporary) + { + if (isDisplayable () + && isShowing () + && isFocusable ()) + { + synchronized (getTreeLock ()) + { + // Find this Component's top-level ancestor. + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + if (toplevel.isFocusableWindow ()) + { + if (peer != null && !isLightweight()) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + synchronized (eq) + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); + if (currentFocusOwner != null) + { + eq.postEvent (new FocusEvent(currentFocusOwner, + FocusEvent.FOCUS_LOST, + temporary, this)); + eq.postEvent (new FocusEvent(this, + FocusEvent.FOCUS_GAINED, + temporary, + currentFocusOwner)); + } + else + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + } + } + } + else + // FIXME: need to add a focus listener to our top-level + // ancestor, so that we can post this event when it becomes + // the focused window. + pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary); + } + } + // Always return true. + return true; + } + + /** + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * <code>FOCUS_GAINED</code> event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, showing, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. + * + * @return true if the request has a chance of success + * @see #requestFocus () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + public boolean requestFocusInWindow () + { + return requestFocusInWindow (false); + } + + /** + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * <code>FOCUS_GAINED</code> event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, showing, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. + * + * @param temporary true if the focus request is temporary + * @return true if the request has a chance of success + * @see #requestFocus () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + protected boolean requestFocusInWindow (boolean temporary) + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Window focusedWindow = manager.getFocusedWindow (); + + if (isDisplayable () + && isShowing () + && isFocusable ()) + { + if (focusedWindow != null) + { + synchronized (getTreeLock ()) + { + Container parent = getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + Window toplevel = (Window) parent; + + // Check if top-level ancestor is currently focused window. + if (focusedWindow == toplevel) + { + if (peer != null + && !isLightweight() + && !(this instanceof Window)) + // This call will cause a FOCUS_GAINED event to be + // posted to the system event queue if the native + // windowing system grants the focus request. + peer.requestFocus (); + else + { + // Either our peer hasn't been created yet or we're a + // lightweight component. In either case we want to + // post a FOCUS_GAINED event. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + synchronized (eq) + { + Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); + if (currentFocusOwner != null) + { + eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, + temporary, this)); + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary, + currentFocusOwner)); + } + else + eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + } + } + } + else + return false; + } + } + + return true; + } + return false; + } + + /** + * Transfers focus to the next component in the focus traversal + * order, as though this were the current focus owner. + * + * @see #requestFocus() + * @since 1.1 + */ + public void transferFocus () + { + nextFocus (); + } + + /** + * Returns the root container that owns the focus cycle where this + * component resides. A focus cycle root is in two cycles, one as + * the ancestor, and one as the focusable element; this call always + * returns the ancestor. + * + * @return the ancestor container that owns the focus cycle + * @since 1.4 + */ + public Container getFocusCycleRootAncestor () + { + if (this instanceof Window + && ((Container) this).isFocusCycleRoot ()) + return (Container) this; + + Container parent = getParent (); + + while (parent != null + && !parent.isFocusCycleRoot ()) + parent = parent.getParent (); + + return parent; + } + + /** + * Tests if the container is the ancestor of the focus cycle that + * this component belongs to. + * + * @param c the container to test + * @return true if c is the focus cycle root + * @since 1.4 + */ + public boolean isFocusCycleRoot (Container c) + { + return c == getFocusCycleRootAncestor (); + } + + /** + * AWT 1.0 focus event processor. Transfers focus to the next + * component in the focus traversal order, as though this were the + * current focus owner. + * + * @deprecated use {@link #transferFocus ()} instead + */ + public void nextFocus () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.focusNextComponent (this); + } + + /** + * Transfers focus to the previous component in the focus traversal + * order, as though this were the current focus owner. + * + * @see #requestFocus () + * @since 1.4 + */ + public void transferFocusBackward () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.focusPreviousComponent (this); + } + + /** + * Transfers focus to the focus cycle root of this component. + * However, if this is a Window, the default focus owner in the + * window in the current focus cycle is focused instead. + * + * @see #requestFocus() + * @see #isFocusCycleRoot(Container) + * @since 1.4 + */ + public void transferFocusUpCycle () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.upFocusCycle (this); + } + + /** + * Tests if this component is the focus owner. Use {@link + * #isFocusOwner ()} instead. + * + * @return true if this component owns focus + * @since 1.2 + */ + public boolean hasFocus () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Component focusOwner = manager.getFocusOwner (); + + return this == focusOwner; + } + + /** + * Tests if this component is the focus owner. + * + * @return true if this component owns focus + * @since 1.4 + */ + public boolean isFocusOwner() + { + return hasFocus (); + } + + /** + * Adds the specified popup menu to this component. + * + * @param popup the popup menu to be added + * + * @see #remove(MenuComponent) + * + * @since 1.1 + */ + public synchronized void add(PopupMenu popup) + { + if (popups == null) + popups = new Vector(); + popups.add(popup); + + if (popup.parent != null) + popup.parent.remove(popup); + popup.parent = this; + if (peer != null) + popup.addNotify(); + } + + /** + * Removes the specified popup menu from this component. + * + * @param popup the popup menu to remove + * @see #add(PopupMenu) + * @since 1.1 + */ + public synchronized void remove(MenuComponent popup) + { + if (popups != null) + popups.remove(popup); + } + + /** + * Returns a debugging string representing this component. The string may + * be empty but not null. + * + * @return a string representing this component + */ + protected String paramString() + { + StringBuffer param = new StringBuffer(); + String name = getName(); + if (name != null) + param.append(name).append(","); + param.append(x).append(",").append(y).append(",").append(width) + .append("x").append(height); + if (! isValid()) + param.append(",invalid"); + if (! isVisible()) + param.append(",invisible"); + if (! isEnabled()) + param.append(",disabled"); + if (! isOpaque()) + param.append(",translucent"); + if (isDoubleBuffered()) + param.append(",doublebuffered"); + return param.toString(); + } + + /** + * Returns a string representation of this component. This is implemented + * as <code>getClass().getName() + '[' + paramString() + ']'</code>. + * + * @return a string representation of this component + */ + public String toString() + { + return getClass().getName() + '[' + paramString() + ']'; + } + + /** + * Prints a listing of this component to <code>System.out</code>. + * + * @see #list(PrintStream) + */ + public void list() + { + list(System.out, 0); + } + + /** + * Prints a listing of this component to the specified print stream. + * + * @param out the <code>PrintStream</code> to print to + */ + public void list(PrintStream out) + { + list(out, 0); + } + + /** + * Prints a listing of this component to the specified print stream, + * starting at the specified indentation point. + * + * @param out the <code>PrintStream</code> to print to + * @param indent the indentation point + */ + public void list(PrintStream out, int indent) + { + for (int i = 0; i < indent; ++i) + out.print(' '); + out.println(toString()); + } + + /** + * Prints a listing of this component to the specified print writer. + * + * @param out the <code>PrintWrinter</code> to print to + * @since 1.1 + */ + public void list(PrintWriter out) + { + list(out, 0); + } + + /** + * Prints a listing of this component to the specified print writer, + * starting at the specified indentation point. + * + * @param out the <code>PrintWriter</code> to print to + * @param indent the indentation point + * @since 1.1 + */ + public void list(PrintWriter out, int indent) + { + for (int i = 0; i < indent; ++i) + out.print(' '); + out.println(toString()); + } + + /** + * Adds the specified property listener to this component. This is harmless + * if the listener is null, but if the listener has already been registered, + * it will now be registered twice. The property listener ignores inherited + * properties. Recognized properties include:<br> + * <ul> + * <li>the font (<code>"font"</code>)</li> + * <li>the background color (<code>"background"</code>)</li> + * <li>the foreground color (<code>"foreground"</code>)</li> + * <li>the focusability (<code>"focusable"</code>)</li> + * <li>the focus key traversal enabled state + * (<code>"focusTraversalKeysEnabled"</code>)</li> + * <li>the set of forward traversal keys + * (<code>"forwardFocusTraversalKeys"</code>)</li> + * <li>the set of backward traversal keys + * (<code>"backwardFocusTraversalKeys"</code>)</li> + * <li>the set of up-cycle traversal keys + * (<code>"upCycleFocusTraversalKeys"</code>)</li> + * </ul> + * + * @param listener the new listener to add + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @since 1.1 + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + if (changeSupport == null) + changeSupport = new PropertyChangeSupport(this); + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Removes the specified property listener from the component. This is + * harmless if the listener was not previously registered. + * + * @param listener the listener to remove + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @since 1.1 + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + if (changeSupport != null) + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return changeSupport == null ? new PropertyChangeListener[0] + : changeSupport.getPropertyChangeListeners(); + } + + /** + * Adds the specified property listener to this component. This is harmless + * if the listener is null, but if the listener has already been registered, + * it will now be registered twice. The property listener ignores inherited + * properties. The listener is keyed to a single property. Recognized + * properties include:<br> + * <ul> + * <li>the font (<code>"font"</code>)</li> + * <li>the background color (<code>"background"</code>)</li> + * <li>the foreground color (<code>"foreground"</code>)</li> + * <li>the focusability (<code>"focusable"</code>)</li> + * <li>the focus key traversal enabled state + * (<code>"focusTraversalKeysEnabled"</code>)</li> + * <li>the set of forward traversal keys + * (<code>"forwardFocusTraversalKeys"</code>)</li> +p * <li>the set of backward traversal keys + * (<code>"backwardFocusTraversalKeys"</code>)</li> + * <li>the set of up-cycle traversal keys + * (<code>"upCycleFocusTraversalKeys"</code>)</li> + * </ul> + * + * @param propertyName the property name to filter on + * @param listener the new listener to add + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @see #addPropertyChangeListener(PropertyChangeListener) + * @since 1.1 + */ + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + if (changeSupport == null) + changeSupport = new PropertyChangeSupport(this); + changeSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Removes the specified property listener on a particular property from + * the component. This is harmless if the listener was not previously + * registered. + * + * @param propertyName the property name to filter on + * @param listener the listener to remove + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @since 1.1 + */ + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + if (changeSupport != null) + changeSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Returns an array of all specified listeners on the named property that + * are registered on this component. + * + * @return an array of listeners + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners(String property) + { + return changeSupport == null ? new PropertyChangeListener[0] + : changeSupport.getPropertyChangeListeners(property); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, Object oldValue, + Object newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, boolean oldValue, + boolean newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, int oldValue, + int newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Sets the text layout orientation of this component. New components default + * to UNKNOWN (which behaves like LEFT_TO_RIGHT). This method affects only + * the current component, while + * {@link #applyComponentOrientation(ComponentOrientation)} affects the + * entire hierarchy. + * + * @param o the new orientation + * @throws NullPointerException if o is null + * @see #getComponentOrientation() + */ + public void setComponentOrientation(ComponentOrientation o) + { + if (o == null) + throw new NullPointerException(); + ComponentOrientation oldOrientation = orientation; + orientation = o; + firePropertyChange("componentOrientation", oldOrientation, o); + } + + /** + * Determines the text layout orientation used by this component. + * + * @return the component orientation + * @see #setComponentOrientation(ComponentOrientation) + */ + public ComponentOrientation getComponentOrientation() + { + return orientation; + } + + /** + * Sets the text layout orientation of this component. New components default + * to UNKNOWN (which behaves like LEFT_TO_RIGHT). This method affects the + * entire hierarchy, while + * {@link #setComponentOrientation(ComponentOrientation)} affects only the + * current component. + * + * @param o the new orientation + * @throws NullPointerException if o is null + * @see #getComponentOrientation() + * @since 1.4 + */ + public void applyComponentOrientation(ComponentOrientation o) + { + setComponentOrientation(o); + } + + /** + * Returns the accessibility framework context of this class. Component is + * not accessible, so the default implementation returns null. Subclasses + * must override this behavior, and return an appropriate subclass of + * {@link AccessibleAWTComponent}. + * + * @return the accessibility context + */ + public AccessibleContext getAccessibleContext() + { + return null; + } + + + // Helper methods; some are package visible for use by subclasses. + + /** + * Subclasses should override this to return unique component names like + * "menuitem0". + * + * @return the generated name for this component + */ + String generateName() + { + // Component is abstract. + return null; + } + + /** + * Sets the peer for this component. + * + * @param peer the new peer + */ + final void setPeer(ComponentPeer peer) + { + this.peer = peer; + } + + /** + * Implementation method that allows classes such as Canvas and Window to + * override the graphics configuration without violating the published API. + * + * @return the graphics configuration + */ + GraphicsConfiguration getGraphicsConfigurationImpl() + { + if (peer != null) + { + GraphicsConfiguration config = peer.getGraphicsConfiguration(); + if (config != null) + return config; + } + + if (parent != null) + return parent.getGraphicsConfiguration(); + + return null; + } + + /** + * Translate an AWT 1.1 event ({@link AWTEvent}) into an AWT 1.0 + * event ({@link Event}). + * + * @param e an AWT 1.1 event to translate + * + * @return an AWT 1.0 event representing e + */ + static Event translateEvent (AWTEvent e) + { + Component target = (Component) e.getSource (); + Event translated = null; + + if (e instanceof InputEvent) + { + InputEvent ie = (InputEvent) e; + long when = ie.getWhen (); + + int oldID = 0; + int id = e.getID (); + + int oldMods = 0; + int mods = ie.getModifiersEx (); + + if ((mods & InputEvent.BUTTON2_DOWN_MASK) != 0) + oldMods |= Event.META_MASK; + else if ((mods & InputEvent.BUTTON3_DOWN_MASK) != 0) + oldMods |= Event.ALT_MASK; + + if ((mods & InputEvent.SHIFT_DOWN_MASK) != 0) + oldMods |= Event.SHIFT_MASK; + + if ((mods & InputEvent.CTRL_DOWN_MASK) != 0) + oldMods |= Event.CTRL_MASK; + + if ((mods & InputEvent.META_DOWN_MASK) != 0) + oldMods |= Event.META_MASK; + + if ((mods & InputEvent.ALT_DOWN_MASK) != 0) + oldMods |= Event.ALT_MASK; + + if (e instanceof MouseEvent) + { + if (id == MouseEvent.MOUSE_PRESSED) + oldID = Event.MOUSE_DOWN; + else if (id == MouseEvent.MOUSE_RELEASED) + oldID = Event.MOUSE_UP; + else if (id == MouseEvent.MOUSE_MOVED) + oldID = Event.MOUSE_MOVE; + else if (id == MouseEvent.MOUSE_DRAGGED) + oldID = Event.MOUSE_DRAG; + else if (id == MouseEvent.MOUSE_ENTERED) + oldID = Event.MOUSE_ENTER; + else if (id == MouseEvent.MOUSE_EXITED) + oldID = Event.MOUSE_EXIT; + else + // No analogous AWT 1.0 mouse event. + return null; + + MouseEvent me = (MouseEvent) e; + + translated = new Event (target, when, oldID, + me.getX (), me.getY (), 0, oldMods); + } + else if (e instanceof KeyEvent) + { + if (id == KeyEvent.KEY_PRESSED) + oldID = Event.KEY_PRESS; + else if (e.getID () == KeyEvent.KEY_RELEASED) + oldID = Event.KEY_RELEASE; + else + // No analogous AWT 1.0 key event. + return null; + + int oldKey = 0; + int newKey = ((KeyEvent) e).getKeyCode (); + switch (newKey) + { + case KeyEvent.VK_BACK_SPACE: + oldKey = Event.BACK_SPACE; + break; + case KeyEvent.VK_CAPS_LOCK: + oldKey = Event.CAPS_LOCK; + break; + case KeyEvent.VK_DELETE: + oldKey = Event.DELETE; + break; + case KeyEvent.VK_DOWN: + case KeyEvent.VK_KP_DOWN: + oldKey = Event.DOWN; + break; + case KeyEvent.VK_END: + oldKey = Event.END; + break; + case KeyEvent.VK_ENTER: + oldKey = Event.ENTER; + break; + case KeyEvent.VK_ESCAPE: + oldKey = Event.ESCAPE; + break; + case KeyEvent.VK_F1: + oldKey = Event.F1; + break; + case KeyEvent.VK_F10: + oldKey = Event.F10; + break; + case KeyEvent.VK_F11: + oldKey = Event.F11; + break; + case KeyEvent.VK_F12: + oldKey = Event.F12; + break; + case KeyEvent.VK_F2: + oldKey = Event.F2; + break; + case KeyEvent.VK_F3: + oldKey = Event.F3; + break; + case KeyEvent.VK_F4: + oldKey = Event.F4; + break; + case KeyEvent.VK_F5: + oldKey = Event.F5; + break; + case KeyEvent.VK_F6: + oldKey = Event.F6; + break; + case KeyEvent.VK_F7: + oldKey = Event.F7; + break; + case KeyEvent.VK_F8: + oldKey = Event.F8; + break; + case KeyEvent.VK_F9: + oldKey = Event.F9; + break; + case KeyEvent.VK_HOME: + oldKey = Event.HOME; + break; + case KeyEvent.VK_INSERT: + oldKey = Event.INSERT; + break; + case KeyEvent.VK_LEFT: + case KeyEvent.VK_KP_LEFT: + oldKey = Event.LEFT; + break; + case KeyEvent.VK_NUM_LOCK: + oldKey = Event.NUM_LOCK; + break; + case KeyEvent.VK_PAUSE: + oldKey = Event.PAUSE; + break; + case KeyEvent.VK_PAGE_DOWN: + oldKey = Event.PGDN; + break; + case KeyEvent.VK_PAGE_UP: + oldKey = Event.PGUP; + break; + case KeyEvent.VK_PRINTSCREEN: + oldKey = Event.PRINT_SCREEN; + break; + case KeyEvent.VK_RIGHT: + case KeyEvent.VK_KP_RIGHT: + oldKey = Event.RIGHT; + break; + case KeyEvent.VK_SCROLL_LOCK: + oldKey = Event.SCROLL_LOCK; + break; + case KeyEvent.VK_TAB: + oldKey = Event.TAB; + break; + case KeyEvent.VK_UP: + case KeyEvent.VK_KP_UP: + oldKey = Event.UP; + break; + default: + oldKey = newKey; + } + + translated = new Event (target, when, oldID, + 0, 0, oldKey, oldMods); + } + } + else if (e instanceof ActionEvent) + translated = new Event (target, Event.ACTION_EVENT, + ((ActionEvent) e).getActionCommand ()); + + return translated; + } + + /** + * Implementation of dispatchEvent. Allows trusted package classes + * to dispatch additional events first. This implementation first + * translates <code>e</code> to an AWT 1.0 event and sends the + * result to {@link #postEvent}. If the AWT 1.0 event is not + * handled, and events of type <code>e</code> are enabled for this + * component, e is passed on to {@link #processEvent}. + * + * @param e the event to dispatch + */ + + void dispatchEventImpl (AWTEvent e) + { + Event oldEvent = translateEvent (e); + + if (oldEvent != null) + postEvent (oldEvent); + + if (eventTypeEnabled (e.id)) + { + // the trick we use to communicate between dispatch and redispatch + // is to have KeyboardFocusManager.redispatch synchronize on the + // object itself. we then do not redispatch to KeyboardFocusManager + // if we are already holding the lock. + if (! Thread.holdsLock(e)) + { + switch (e.id) + { + case WindowEvent.WINDOW_GAINED_FOCUS: + case WindowEvent.WINDOW_LOST_FOCUS: + case KeyEvent.KEY_PRESSED: + case KeyEvent.KEY_RELEASED: + case KeyEvent.KEY_TYPED: + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + if (KeyboardFocusManager + .getCurrentKeyboardFocusManager() + .dispatchEvent(e)) + return; + case MouseEvent.MOUSE_PRESSED: + if (isLightweight()) + requestFocus(); + break; + } + } + processEvent (e); + } + } + + /** + * Tells whether or not an event type is enabled. + */ + boolean eventTypeEnabled (int type) + { + if (type > AWTEvent.RESERVED_ID_MAX) + return true; + + switch (type) + { + case ComponentEvent.COMPONENT_HIDDEN: + case ComponentEvent.COMPONENT_MOVED: + case ComponentEvent.COMPONENT_RESIZED: + case ComponentEvent.COMPONENT_SHOWN: + return (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0); + + case KeyEvent.KEY_PRESSED: + case KeyEvent.KEY_RELEASED: + case KeyEvent.KEY_TYPED: + return (keyListener != null + || (eventMask & AWTEvent.KEY_EVENT_MASK) != 0); + + case MouseEvent.MOUSE_CLICKED: + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_DRAGGED: + return (mouseListener != null + || mouseMotionListener != null + || (eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0); + + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + return (focusListener != null + || (eventMask & AWTEvent.FOCUS_EVENT_MASK) != 0); + + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + case InputMethodEvent.CARET_POSITION_CHANGED: + return (inputMethodListener != null + || (eventMask & AWTEvent.INPUT_METHOD_EVENT_MASK) != 0); + + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + return (eventMask & AWTEvent.PAINT_EVENT_MASK) != 0; + + default: + return false; + } + } + + /** + * Coalesce paint events. Current heuristic is: Merge if the union of + * areas is less than twice that of the sum of the areas. The X server + * tend to create a lot of paint events that are adjacent but not + * overlapping. + * + * <pre> + * +------+ + * | +-----+ ...will be merged + * | | | + * | | | + * +------+ | + * +-----+ + * + * +---------------+--+ + * | | | ...will not be merged + * +---------------+ | + * | | + * | | + * | | + * | | + * | | + * +--+ + * </pre> + * + * @param queuedEvent the first paint event + * @param newEvent the second paint event + * @return the combined paint event, or null + */ + private PaintEvent coalescePaintEvents(PaintEvent queuedEvent, + PaintEvent newEvent) + { + Rectangle r1 = queuedEvent.getUpdateRect(); + Rectangle r2 = newEvent.getUpdateRect(); + Rectangle union = r1.union(r2); + + int r1a = r1.width * r1.height; + int r2a = r2.width * r2.height; + int ua = union.width * union.height; + + if (ua > (r1a+r2a)*2) + return null; + /* The 2 factor should maybe be reconsidered. Perhaps 3/2 + would be better? */ + + newEvent.setUpdateRect(union); + return newEvent; + } + + /** + * This method is used to implement transferFocus(). CHILD is the child + * making the request. This is overridden by Container; when called for an + * ordinary component there is no child and so we always return null. + * + * FIXME: is this still needed, in light of focus traversal policies? + * + * @param child the component making the request + * @return the next component to focus on + */ + Component findNextFocusComponent(Component child) + { + return null; + } + + /** + * Deserializes this component. This regenerates all serializable listeners + * which were registered originally. + * + * @param s the stream to read from + * @throws ClassNotFoundException if deserialization fails + * @throws IOException if the stream fails + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + String key = (String) s.readObject(); + while (key != null) + { + Object listener = s.readObject(); + if ("componentL".equals(key)) + addComponentListener((ComponentListener) listener); + else if ("focusL".equals(key)) + addFocusListener((FocusListener) listener); + else if ("keyL".equals(key)) + addKeyListener((KeyListener) listener); + else if ("mouseL".equals(key)) + addMouseListener((MouseListener) listener); + else if ("mouseMotionL".equals(key)) + addMouseMotionListener((MouseMotionListener) listener); + else if ("inputMethodL".equals(key)) + addInputMethodListener((InputMethodListener) listener); + else if ("hierarchyL".equals(key)) + addHierarchyListener((HierarchyListener) listener); + else if ("hierarchyBoundsL".equals(key)) + addHierarchyBoundsListener((HierarchyBoundsListener) listener); + else if ("mouseWheelL".equals(key)) + addMouseWheelListener((MouseWheelListener) listener); + key = (String) s.readObject(); + } + } + + /** + * Serializes this component. This ignores all listeners which do not + * implement Serializable, but includes those that do. + * + * @param s the stream to write to + * @throws IOException if the stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + AWTEventMulticaster.save(s, "componentL", componentListener); + AWTEventMulticaster.save(s, "focusL", focusListener); + AWTEventMulticaster.save(s, "keyL", keyListener); + AWTEventMulticaster.save(s, "mouseL", mouseListener); + AWTEventMulticaster.save(s, "mouseMotionL", mouseMotionListener); + AWTEventMulticaster.save(s, "inputMethodL", inputMethodListener); + AWTEventMulticaster.save(s, "hierarchyL", hierarchyListener); + AWTEventMulticaster.save(s, "hierarchyBoundsL", hierarchyBoundsListener); + AWTEventMulticaster.save(s, "mouseWheelL", mouseWheelListener); + s.writeObject(null); + } + + + // Nested classes. + + /** + * This class provides accessibility support for subclasses of container. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected abstract class AccessibleAWTComponent extends AccessibleContext + implements Serializable, AccessibleComponent + { + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = 642321655757800191L; + + /** + * Converts show/hide events to PropertyChange events, and is registered + * as a component listener on this component. + * + * @serial the component handler + */ + protected ComponentListener accessibleAWTComponentHandler + = new AccessibleAWTComponentHandler(); + + /** + * Converts focus events to PropertyChange events, and is registered + * as a focus listener on this component. + * + * @serial the focus handler + */ + protected FocusListener accessibleAWTFocusHandler + = new AccessibleAWTFocusHandler(); + + /** + * The default constructor. + */ + protected AccessibleAWTComponent() + { + Component.this.addComponentListener(accessibleAWTComponentHandler); + Component.this.addFocusListener(accessibleAWTFocusHandler); + } + + /** + * Adds a global property change listener to the accessible component. + * + * @param l the listener to add + * @see #ACCESSIBLE_NAME_PROPERTY + * @see #ACCESSIBLE_DESCRIPTION_PROPERTY + * @see #ACCESSIBLE_STATE_PROPERTY + * @see #ACCESSIBLE_VALUE_PROPERTY + * @see #ACCESSIBLE_SELECTION_PROPERTY + * @see #ACCESSIBLE_TEXT_PROPERTY + * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + Component.this.addPropertyChangeListener(l); + super.addPropertyChangeListener(l); + } + + /** + * Removes a global property change listener from this accessible + * component. + * + * @param l the listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + Component.this.removePropertyChangeListener(l); + super.removePropertyChangeListener(l); + } + + /** + * Returns the accessible name of this component. It is almost always + * wrong to return getName(), since it is not localized. In fact, for + * things like buttons, this should be the text of the button, not the + * name of the object. The tooltip text might also be appropriate. + * + * @return the name + * @see #setAccessibleName(String) + */ + public String getAccessibleName() + { + return accessibleName == null ? getName() : accessibleName; + } + + /** + * Returns a brief description of this accessible context. This should + * be localized. + * + * @return a description of this component + * @see #setAccessibleDescription(String) + */ + public String getAccessibleDescription() + { + return accessibleDescription; + } + + /** + * Returns the role of this component. + * + * @return the accessible role + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.AWT_COMPONENT; + } + + /** + * Returns a state set describing this component's state. + * + * @return a new state set + * @see AccessibleState + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet s = new AccessibleStateSet(); + if (Component.this.isEnabled()) + s.add(AccessibleState.ENABLED); + if (isFocusable()) + s.add(AccessibleState.FOCUSABLE); + if (isFocusOwner()) + s.add(AccessibleState.FOCUSED); + if (isOpaque()) + s.add(AccessibleState.OPAQUE); + if (Component.this.isShowing()) + s.add(AccessibleState.SHOWING); + if (Component.this.isVisible()) + s.add(AccessibleState.VISIBLE); + return s; + } + + /** + * Returns the parent of this component, if it is accessible. + * + * @return the accessible parent + */ + public Accessible getAccessibleParent() + { + if (accessibleParent == null) + { + Container parent = getParent(); + accessibleParent = parent instanceof Accessible + ? (Accessible) parent : null; + } + return accessibleParent; + } + + /** + * Returns the index of this component in its accessible parent. + * + * @return the index, or -1 if the parent is not accessible + * @see #getAccessibleParent() + */ + public int getAccessibleIndexInParent() + { + if (getAccessibleParent() == null) + return -1; + AccessibleContext context + = ((Component) accessibleParent).getAccessibleContext(); + if (context == null) + return -1; + for (int i = context.getAccessibleChildrenCount(); --i >= 0; ) + if (context.getAccessibleChild(i) == Component.this) + return i; + return -1; + } + + /** + * Returns the number of children of this component which implement + * Accessible. Subclasses must override this if they can have children. + * + * @return the number of accessible children, default 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the ith accessible child. Subclasses must override this if + * they can have children. + * + * @return the ith accessible child, or null + * @see #getAccessibleChildrenCount() + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale of this component. + * + * @return the locale + * @throws IllegalComponentStateException if the locale is unknown + */ + public Locale getLocale() + { + return Component.this.getLocale(); + } + + /** + * Returns this, since it is an accessible component. + * + * @return the accessible component + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Gets the background color. + * + * @return the background color + * @see #setBackground(Color) + */ + public Color getBackground() + { + return Component.this.getBackground(); + } + + /** + * Sets the background color. + * + * @param c the background color + * @see #getBackground() + * @see #isOpaque() + */ + public void setBackground(Color c) + { + Component.this.setBackground(c); + } + + /** + * Gets the foreground color. + * + * @return the foreground color + * @see #setForeground(Color) + */ + public Color getForeground() + { + return Component.this.getForeground(); + } + + /** + * Sets the foreground color. + * + * @param c the foreground color + * @see #getForeground() + */ + public void setForeground(Color c) + { + Component.this.setForeground(c); + } + + /** + * Gets the cursor. + * + * @return the cursor + * @see #setCursor(Cursor) + */ + public Cursor getCursor() + { + return Component.this.getCursor(); + } + + /** + * Sets the cursor. + * + * @param cursor the cursor + * @see #getCursor() + */ + public void setCursor(Cursor cursor) + { + Component.this.setCursor(cursor); + } + + /** + * Gets the font. + * + * @return the font + * @see #setFont(Font) + */ + public Font getFont() + { + return Component.this.getFont(); + } + + /** + * Sets the font. + * + * @param f the font + * @see #getFont() + */ + public void setFont(Font f) + { + Component.this.setFont(f); + } + + /** + * Gets the font metrics for a font. + * + * @param f the font to look up + * @return its metrics + * @throws NullPointerException if f is null + * @see #getFont() + */ + public FontMetrics getFontMetrics(Font f) + { + return Component.this.getFontMetrics(f); + } + + /** + * Tests if the component is enabled. + * + * @return true if the component is enabled + * @see #setEnabled(boolean) + * @see #getAccessibleStateSet() + * @see AccessibleState#ENABLED + */ + public boolean isEnabled() + { + return Component.this.isEnabled(); + } + + /** + * Set whether the component is enabled. + * + * @param b the new enabled status + * @see #isEnabled() + */ + public void setEnabled(boolean b) + { + Component.this.setEnabled(b); + } + + /** + * Test whether the component is visible (not necesarily showing). + * + * @return true if it is visible + * @see #setVisible(boolean) + * @see #getAccessibleStateSet() + * @see AccessibleState#VISIBLE + */ + public boolean isVisible() + { + return Component.this.isVisible(); + } + + /** + * Sets the visibility of this component. + * + * @param b the desired visibility + * @see #isVisible() + */ + public void setVisible(boolean b) + { + Component.this.setVisible(b); + } + + /** + * Tests if the component is showing. + * + * @return true if this is showing + */ + public boolean isShowing() + { + return Component.this.isShowing(); + } + + /** + * Tests if the point is contained in this component. + * + * @param p the point to check + * @return true if it is contained + * @throws NullPointerException if p is null + */ + public boolean contains(Point p) + { + return Component.this.contains(p.x, p.y); + } + + /** + * Returns the location of this object on the screen, or null if it is + * not showing. + * + * @return the location relative to screen coordinates, if showing + * @see #getBounds() + * @see #getLocation() + */ + public Point getLocationOnScreen() + { + return Component.this.isShowing() ? Component.this.getLocationOnScreen() + : null; + } + + /** + * Returns the location of this object relative to its parent's coordinate + * system, or null if it is not showing. + * + * @return the location + * @see #getBounds() + * @see #getLocationOnScreen() + */ + public Point getLocation() + { + return Component.this.isShowing() ? Component.this.getLocation() : null; + } + + /** + * Sets the location of this relative to its parent's coordinate system. + * + * @param p the location + * @throws NullPointerException if p is null + * @see #getLocation() + */ + public void setLocation(Point p) + { + Component.this.setLocation(p.x, p.y); + } + + /** + * Gets the bounds of this component, or null if it is not on screen. + * + * @return the bounds + * @see #contains(Point) + * @see #setBounds(Rectangle) + */ + public Rectangle getBounds() + { + return Component.this.isShowing() ? Component.this.getBounds() : null; + } + + /** + * Sets the bounds of this component. + * + * @param r the bounds + * @throws NullPointerException if r is null + * @see #getBounds() + */ + public void setBounds(Rectangle r) + { + Component.this.setBounds(r.x, r.y, r.width, r.height); + } + + /** + * Gets the size of this component, or null if it is not showing. + * + * @return the size + * @see #setSize(Dimension) + */ + public Dimension getSize() + { + return Component.this.isShowing() ? Component.this.getSize() : null; + } + + /** + * Sets the size of this component. + * + * @param d the size + * @throws NullPointerException if d is null + * @see #getSize() + */ + public void setSize(Dimension d) + { + Component.this.setSize(d.width, d.height); + } + + /** + * Returns the Accessible child at a point relative to the coordinate + * system of this component, if one exists, or null. Since components + * have no children, subclasses must override this to get anything besides + * null. + * + * @param p the point to check + * @return the accessible child at that point + * @throws NullPointerException if p is null + */ + public Accessible getAccessibleAt(Point p) + { + return null; + } + + /** + * Tests whether this component can accept focus. + * + * @return true if this is focus traversable + * @see #getAccessibleStateSet () + * @see AccessibleState#FOCUSABLE + * @see AccessibleState#FOCUSED + */ + public boolean isFocusTraversable () + { + return Component.this.isFocusTraversable (); + } + + /** + * Requests focus for this component. + * + * @see #isFocusTraversable () + */ + public void requestFocus () + { + Component.this.requestFocus (); + } + + /** + * Adds a focus listener. + * + * @param l the listener to add + */ + public void addFocusListener(FocusListener l) + { + Component.this.addFocusListener(l); + } + + /** + * Removes a focus listener. + * + * @param l the listener to remove + */ + public void removeFocusListener(FocusListener l) + { + Component.this.removeFocusListener(l); + } + + /** + * Converts component changes into property changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected class AccessibleAWTComponentHandler implements ComponentListener + { + /** + * Default constructor. + */ + protected AccessibleAWTComponentHandler() + { + } + + /** + * Convert a component hidden to a property change. + * + * @param e the event to convert + */ + public void componentHidden(ComponentEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, AccessibleState.VISIBLE, null); + } + + /** + * Convert a component shown to a property change. + * + * @param e the event to convert + */ + public void componentShown(ComponentEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.VISIBLE); + } + + /** + * Moving a component does not affect properties. + * + * @param e ignored + */ + public void componentMoved(ComponentEvent e) + { + } + + /** + * Resizing a component does not affect properties. + * + * @param e ignored + */ + public void componentResized(ComponentEvent e) + { + } + } // class AccessibleAWTComponentHandler + + /** + * Converts focus changes into property changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected class AccessibleAWTFocusHandler implements FocusListener + { + /** + * Default constructor. + */ + protected AccessibleAWTFocusHandler() + { + } + + /** + * Convert a focus gained to a property change. + * + * @param e the event to convert + */ + public void focusGained(FocusEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.FOCUSED); + } + + /** + * Convert a focus lost to a property change. + * + * @param e the event to convert + */ + public void focusLost(FocusEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null); + } + } // class AccessibleAWTComponentHandler + } // class AccessibleAWTComponent + + /** + * This class provides support for blitting offscreen surfaces to a + * component. + * + * @see BufferStrategy + * + * @since 1.4 + */ + protected class BltBufferStrategy extends BufferStrategy + { + /** + * The capabilities of the image buffer. + */ + protected BufferCapabilities caps; + + /** + * The back buffers used in this strategy. + */ + protected VolatileImage[] backBuffers; + + /** + * Whether or not the image buffer resources are allocated and + * ready to be drawn into. + */ + protected boolean validatedContents; + + /** + * The width of the back buffers. + */ + protected int width; + + /** + * The height of the back buffers. + */ + protected int height; + + /** + * The front buffer. + */ + private VolatileImage frontBuffer; + + /** + * Creates a blitting buffer strategy. + * + * @param numBuffers the number of buffers, including the front + * buffer + * @param caps the capabilities of this strategy + */ + protected BltBufferStrategy(int numBuffers, BufferCapabilities caps) + { + this.caps = caps; + createBackBuffers(numBuffers - 1); + width = getWidth(); + height = getHeight(); + } + + /** + * Initializes the backBuffers field with an array of numBuffers + * VolatileImages. + * + * @param numBuffers the number of backbuffers to create + */ + protected void createBackBuffers(int numBuffers) + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + backBuffers = new VolatileImage[numBuffers]; + + for (int i = 0; i < numBuffers; i++) + backBuffers[i] = c.createCompatibleVolatileImage(width, height); + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return the capabilities of this buffer strategy + */ + public BufferCapabilities getCapabilities() + { + return caps; + } + + /** + * Retrieves a graphics object that can be used to draw into this + * strategy's image buffer. + * + * @return a graphics object + */ + public Graphics getDrawGraphics() + { + // Return the backmost buffer's graphics. + return backBuffers[0].getGraphics(); + } + + /** + * Bring the contents of the back buffer to the front buffer. + */ + public void show() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + // draw the front buffer. + getGraphics().drawImage(backBuffers[backBuffers.length - 1], + width, height, null); + + BufferCapabilities.FlipContents f = getCapabilities().getFlipContents(); + + // blit the back buffers. + for (int i = backBuffers.length - 1; i > 0 ; i--) + backBuffers[i] = backBuffers[i - 1]; + + // create new backmost buffer. + if (f == BufferCapabilities.FlipContents.UNDEFINED) + backBuffers[0] = c.createCompatibleVolatileImage(width, height); + + // create new backmost buffer and clear it to the background + // color. + if (f == BufferCapabilities.FlipContents.BACKGROUND) + { + backBuffers[0] = c.createCompatibleVolatileImage(width, height); + backBuffers[0].getGraphics().clearRect(0, 0, width, height); + } + + // FIXME: set the backmost buffer to the prior contents of the + // front buffer. How do we retrieve the contents of the front + // buffer? + // + // if (f == BufferCapabilities.FlipContents.PRIOR) + + // set the backmost buffer to a copy of the new front buffer. + if (f == BufferCapabilities.FlipContents.COPIED) + backBuffers[0] = backBuffers[backBuffers.length - 1]; + } + + /** + * Re-create the image buffer resources if they've been lost. + */ + protected void revalidate() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + for (int i = 0; i < backBuffers.length; i++) + { + int result = backBuffers[i].validate(c); + if (result == VolatileImage.IMAGE_INCOMPATIBLE) + backBuffers[i] = c.createCompatibleVolatileImage(width, height); + } + validatedContents = true; + } + + /** + * Returns whether or not the image buffer resources have been + * lost. + * + * @return true if the resources have been lost, false otherwise + */ + public boolean contentsLost() + { + for (int i = 0; i < backBuffers.length; i++) + { + if (backBuffers[i].contentsLost()) + { + validatedContents = false; + return true; + } + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return false; + } + + /** + * Returns whether or not the image buffer resources have been + * restored. + * + * @return true if the resources have been restored, false + * otherwise + */ + public boolean contentsRestored() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + boolean imageRestored = false; + + for (int i = 0; i < backBuffers.length; i++) + { + int result = backBuffers[i].validate(c); + if (result == VolatileImage.IMAGE_RESTORED) + imageRestored = true; + else if (result == VolatileImage.IMAGE_INCOMPATIBLE) + return false; + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return imageRestored; + } + } + + /** + * This class provides support for flipping component buffers. It + * can only be used on Canvases and Windows. + * + * @since 1.4 + */ + protected class FlipBufferStrategy extends BufferStrategy + { + /** + * The number of buffers. + */ + protected int numBuffers; + + /** + * The capabilities of this buffering strategy. + */ + protected BufferCapabilities caps; + + /** + * An Image reference to the drawing buffer. + */ + protected Image drawBuffer; + + /** + * A VolatileImage reference to the drawing buffer. + */ + protected VolatileImage drawVBuffer; + + /** + * Whether or not the image buffer resources are allocated and + * ready to be drawn into. + */ + protected boolean validatedContents; + + /** + * The width of the back buffer. + */ + private int width; + + /** + * The height of the back buffer. + */ + private int height; + + /** + * Creates a flipping buffer strategy. The only supported + * strategy for FlipBufferStrategy itself is a double-buffer page + * flipping strategy. It forms the basis for more complex derived + * strategies. + * + * @param numBuffers the number of buffers + * @param caps the capabilities of this buffering strategy + * + * @throws AWTException if the requested + * number-of-buffers/capabilities combination is not supported + */ + protected FlipBufferStrategy(int numBuffers, BufferCapabilities caps) + throws AWTException + { + this.caps = caps; + width = getWidth(); + height = getHeight(); + + if (numBuffers > 1) + createBuffers(numBuffers, caps); + else + { + drawVBuffer = peer.createVolatileImage(width, height); + drawBuffer = drawVBuffer; + } + } + + /** + * Creates a multi-buffer flipping strategy. The number of + * buffers must be greater than one and the buffer capabilities + * must specify page flipping. + * + * @param numBuffers the number of flipping buffers; must be + * greater than one + * @param caps the buffering capabilities; caps.isPageFlipping() + * must return true + * + * @throws IllegalArgumentException if numBuffers is not greater + * than one or if the page flipping capability is not requested + * + * @throws AWTException if the requested flipping strategy is not + * supported + */ + protected void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException + { + if (numBuffers <= 1) + throw new IllegalArgumentException("FlipBufferStrategy.createBuffers:" + + " numBuffers must be greater than" + + " one."); + + if (!caps.isPageFlipping()) + throw new IllegalArgumentException("FlipBufferStrategy.createBuffers:" + + " flipping must be a specified" + + " capability."); + + peer.createBuffers(numBuffers, caps); + } + + /** + * Return a direct reference to the back buffer image. + * + * @return a direct reference to the back buffer image. + */ + protected Image getBackBuffer() + { + return peer.getBackBuffer(); + } + + /** + * Perform a flip operation to transfer the contents of the back + * buffer to the front buffer. + */ + protected void flip(BufferCapabilities.FlipContents flipAction) + { + peer.flip(flipAction); + } + + /** + * Release the back buffer's resources. + */ + protected void destroyBuffers() + { + peer.destroyBuffers(); + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return the capabilities of this buffer strategy + */ + public BufferCapabilities getCapabilities() + { + return caps; + } + + /** + * Retrieves a graphics object that can be used to draw into this + * strategy's image buffer. + * + * @return a graphics object + */ + public Graphics getDrawGraphics() + { + return drawVBuffer.getGraphics(); + } + + /** + * Re-create the image buffer resources if they've been lost. + */ + protected void revalidate() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + if (drawVBuffer.validate(c) == VolatileImage.IMAGE_INCOMPATIBLE) + drawVBuffer = peer.createVolatileImage(width, height); + validatedContents = true; + } + + /** + * Returns whether or not the image buffer resources have been + * lost. + * + * @return true if the resources have been lost, false otherwise + */ + public boolean contentsLost() + { + if (drawVBuffer.contentsLost()) + { + validatedContents = false; + return true; + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return false; + } + + /** + * Returns whether or not the image buffer resources have been + * restored. + * + * @return true if the resources have been restored, false + * otherwise + */ + public boolean contentsRestored() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + int result = drawVBuffer.validate(c); + + boolean imageRestored = false; + + if (result == VolatileImage.IMAGE_RESTORED) + imageRestored = true; + else if (result == VolatileImage.IMAGE_INCOMPATIBLE) + return false; + + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return imageRestored; + } + + /** + * Bring the contents of the back buffer to the front buffer. + */ + public void show() + { + flip(caps.getFlipContents()); + } + } +} diff --git a/libjava/classpath/java/awt/ComponentOrientation.java b/libjava/classpath/java/awt/ComponentOrientation.java new file mode 100644 index 00000000000..69b14c7553f --- /dev/null +++ b/libjava/classpath/java/awt/ComponentOrientation.java @@ -0,0 +1,215 @@ +/* ComponentOrientation.java -- describes a component's orientation + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This class is used to differentiate different orientations for text layout. + * It controls whether text flows left-to-right or right-to-left, and whether + * lines are horizontal or vertical, as in this table:<br> + * <pre> + * LT RT TL TR + * A B C C B A A D G G D A + * D E F F E D B E H H E B + * G H I I H G C F I I F C + * </pre> + * <b>LT</b> languages are most common (left-to-right lines, top-to-bottom). + * This includes Western European languages, and optionally includes Japanese, + * Chinese, and Korean. <b>RT</b> languages (right-to-left lines, + * top-to-bottom) are mainly middle eastern, such as Hebrew and Arabic. + * <b>TR</b> languages flow top-to-bottom in a line, right-to-left, and are + * the basis of Japanese, Chinese, and Korean. Finally, <b>TL</b> languages + * flow top-to-bottom in a line, left-to-right, as in Mongolian. + * + * <p>This is a pretty poor excuse for a type-safe enum, since it is not + * guaranteed that orientation objects are unique (thanks to serialization), + * yet there is no equals() method. You would be wise to compare the output + * of isHorizontal() and isLeftToRight() rather than comparing objects with + * ==, especially since more constants may be added in the future. + * + * @author Bryce McKinlay (bryce@albatross.co.nz) + * @since 1.0 + * @status updated to 1.4 + */ +public final class ComponentOrientation implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4113291392143563828L; + + /** Constant for unknown orientation. */ + private static final int UNKNOWN_ID = 1; + + /** Constant for horizontal line orientation. */ + private static final int HORIZONTAL_ID = 2; + + /** Constant for left-to-right orientation. */ + private static final int LEFT_TO_RIGHT_ID = 4; + + /** + * Items run left to right, and lines flow top to bottom. Examples: English, + * French. + */ + public static final ComponentOrientation LEFT_TO_RIGHT + = new ComponentOrientation(HORIZONTAL_ID | LEFT_TO_RIGHT_ID); + + /** + * Items run right to left, and lines flow top to bottom. Examples: Arabic, + * Hebrew. + */ + public static final ComponentOrientation RIGHT_TO_LEFT + = new ComponentOrientation(HORIZONTAL_ID); + + /** + * The orientation is unknown for the locale. For backwards compatibility, + * this behaves like LEFT_TO_RIGHT in the instance methods. + */ + public static final ComponentOrientation UNKNOWN + = new ComponentOrientation(UNKNOWN_ID | HORIZONTAL_ID | LEFT_TO_RIGHT_ID); + + /** + * The orientation of this object; bitwise-or of unknown (1), horizontal (2), + * and left-to-right (4). + * + * @serial the orientation + */ + private final int orientation; + + /** + * Construct a given orientation. + * + * @param orientation the orientation + */ + private ComponentOrientation(int orientation) + { + this.orientation = orientation; + } + + /** + * Returns true if the lines are horizontal, in which case lines flow + * top-to-bottom. For example, English, Hebrew. Counterexamples: Japanese, + * Chinese, Korean, Mongolian. + * + * @return true if this orientation has horizontal lines + */ + public boolean isHorizontal() + { + return (orientation & HORIZONTAL_ID) != 0; + } + + /** + * If isHorizontal() returns true, then this determines whether items in + * the line flow left-to-right. If isHorizontal() returns false, items in + * a line flow top-to-bottom, and this determines if lines flow + * left-to-right. + * + * @return true if this orientation flows left-to-right + */ + public boolean isLeftToRight() + { + return (orientation & LEFT_TO_RIGHT_ID) != 0; + } + + /** + * Gets an orientation appropriate for the locale. + * + * @param locale the locale + * @return the orientation for that locale + * @throws NullPointerException if locale is null + */ + public static ComponentOrientation getOrientation(Locale locale) + { + // Based on iterating over all languages defined in JDK 1.4, this behavior + // matches Sun's. However, it makes me wonder if any non-horizontal + // orientations even exist, as it sure contradicts their documentation. + String language = locale.getLanguage(); + if ("ar".equals(language) || "fa".equals(language) || "iw".equals(language) + || "ur".equals(language)) + return RIGHT_TO_LEFT; + return LEFT_TO_RIGHT; + } + + /** + * Gets an orientation from a resource bundle. This tries the following: + * + * <ul> + * <li>Use the key "Orientation" to find an instance of ComponentOrientation + * in the bundle.</li> + * <li>Get the locale of the resource bundle, and get the orientation of + * that locale.</li> + * <li>Give up and get the orientation of the default locale.</li> + * </ul> + * + * @param bdl the bundle to use + * @return the orientation + * @throws NullPointerException if bdl is null + * @deprecated use {@link #getOrientation(Locale)} instead + */ + public static ComponentOrientation getOrientation(ResourceBundle bdl) + { + ComponentOrientation r; + try + { + r = (ComponentOrientation) bdl.getObject("Orientation"); + if (r != null) + return r; + } + catch (MissingResourceException ignored) + { + } + catch (ClassCastException ignored) + { + } + try + { + r = getOrientation(bdl.getLocale()); + if (r != null) + return r; + } + catch (Exception ignored) + { + } + return getOrientation(Locale.getDefault()); + } +} // class ComponentOrientation diff --git a/libjava/classpath/java/awt/Composite.java b/libjava/classpath/java/awt/Composite.java new file mode 100644 index 00000000000..ca3abe4d1ee --- /dev/null +++ b/libjava/classpath/java/awt/Composite.java @@ -0,0 +1,73 @@ +/* Composite.java -- graphics formed from composite layers + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; + +/** + * This interface is for graphics which are formed as composites of others. + * It combines {@link Graphics2D} shapes according to defined rules to form + * the new image. Implementations of this interface must be immutable, because + * they are not cloned when a Graphics2D container is cloned. + * + * <p>Since this can expose pixels to untrusted code, there is a security + * check on custom objects, <code>readDisplayPixels</code>, to prevent leaking + * restricted information graphically. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see AlphaComposite + * @see CompositeContext + * @see Graphics2D#setComposite(Composite) + * @since 1.2 + * @status updated to 1.4 + */ +public interface Composite +{ + /** + * Create a context state for performing the compositing operation. Several + * contexts may exist for this object, in a multi-threaded environment. + * + * @param srcColorModel the color model of the source + * @param dstColorModel the color model of the destination + * @param hints hints for choosing between rendering alternatives + */ + CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints); +} // interface Composite diff --git a/libjava/classpath/java/awt/CompositeContext.java b/libjava/classpath/java/awt/CompositeContext.java new file mode 100644 index 00000000000..018a27071dd --- /dev/null +++ b/libjava/classpath/java/awt/CompositeContext.java @@ -0,0 +1,71 @@ +/* Composite.java -- the context for compositing graphics layers + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * This interface provides an optimized environment for compositing graphics. + * Several such contexts may exist for a given <code>Composite</code> object. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Composite + * @since 1.2 + * @status updated to 1.4 + */ +public interface CompositeContext +{ + /** + * Release resources allocated for the compositing. + */ + void dispose(); + + /** + * Compose the two source images into the composite image. The destination + * can be the same as one of the two inputs, and the destination must be + * compatible with the ColorModel chosen in {@link Composite#createContext}. + * + * @param src the lower image source in compositing + * @param dstIn the upper image source in compositing + * @param dstOut the destination for the composite + * @see Composite + */ + void compose(Raster src, Raster dstIn, WritableRaster dstOut); +} // interface CompositeContext diff --git a/libjava/classpath/java/awt/Container.java b/libjava/classpath/java/awt/Container.java new file mode 100644 index 00000000000..303d13b2eb9 --- /dev/null +++ b/libjava/classpath/java/awt/Container.java @@ -0,0 +1,2026 @@ +/* Container.java -- parent container class in AWT + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ContainerPeer; +import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.accessibility.Accessible; +import javax.swing.SwingUtilities; + +/** + * A generic window toolkit object that acts as a container for other objects. + * Components are tracked in a list, and new elements are at the end of the + * list or bottom of the stacking order. + * + * @author original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.0 + * + * @status still missing 1.4 support + */ +public class Container extends Component +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4613797578919906343L; + + /* Serialized fields from the serialization spec. */ + int ncomponents; + Component[] component; + LayoutManager layoutMgr; + + LightweightDispatcher dispatcher; + + Dimension maxSize; + + /** + * @since 1.4 + */ + boolean focusCycleRoot; + + int containerSerializedDataVersion; + + /* Anything else is non-serializable, and should be declared "transient". */ + transient ContainerListener containerListener; + transient PropertyChangeSupport changeSupport; + + /** The focus traversal policy that determines how focus is + transferred between this Container and its children. */ + private FocusTraversalPolicy focusTraversalPolicy; + + /** + * The focus traversal keys, if not inherited from the parent or default + * keyboard manager. These sets will contain only AWTKeyStrokes that + * represent press and release events to use as focus control. + * + * @see #getFocusTraversalKeys(int) + * @see #setFocusTraversalKeys(int, Set) + * @since 1.4 + */ + transient Set[] focusTraversalKeys; + + /** + * Default constructor for subclasses. + */ + public Container() + { + } + + /** + * Returns the number of components in this container. + * + * @return The number of components in this container. + */ + public int getComponentCount() + { + return countComponents (); + } + + /** + * Returns the number of components in this container. + * + * @return The number of components in this container. + * + * @deprecated use {@link #getComponentCount()} instead + */ + public int countComponents() + { + return ncomponents; + } + + /** + * Returns the component at the specified index. + * + * @param n The index of the component to retrieve. + * + * @return The requested component. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid + */ + public Component getComponent(int n) + { + synchronized (getTreeLock ()) + { + if (n < 0 || n >= ncomponents) + throw new ArrayIndexOutOfBoundsException("no such component"); + + return component[n]; + } + } + + /** + * Returns an array of the components in this container. + * + * @return The components in this container. + */ + public Component[] getComponents() + { + synchronized (getTreeLock ()) + { + Component[] result = new Component[ncomponents]; + + if (ncomponents > 0) + System.arraycopy(component, 0, result, 0, ncomponents); + + return result; + } + } + + /** + * Swaps the components at position i and j, in the container. + */ + + protected void swapComponents (int i, int j) + { + synchronized (getTreeLock ()) + { + if (i < 0 + || i >= component.length + || j < 0 + || j >= component.length) + throw new ArrayIndexOutOfBoundsException (); + Component tmp = component[i]; + component[i] = component[j]; + component[j] = tmp; + } + } + + /** + * Returns the insets for this container, which is the space used for + * borders, the margin, etc. + * + * @return The insets for this container. + */ + public Insets getInsets() + { + return insets (); + } + + /** + * Returns the insets for this container, which is the space used for + * borders, the margin, etc. + * + * @return The insets for this container. + * @deprecated use {@link #getInsets()} instead + */ + public Insets insets() + { + if (peer == null) + return new Insets (0, 0, 0, 0); + + return ((ContainerPeer) peer).getInsets (); + } + + /** + * Adds the specified component to this container at the end of the + * component list. + * + * @param comp The component to add to the container. + * + * @return The same component that was added. + */ + public Component add(Component comp) + { + addImpl(comp, null, -1); + return comp; + } + + /** + * Adds the specified component to the container at the end of the + * component list. This method should not be used. Instead, use + * <code>add(Component, Object)</code>. + * + * @param name The name of the component to be added. + * @param comp The component to be added. + * + * @return The same component that was added. + * + * @see #add(Component,Object) + */ + public Component add(String name, Component comp) + { + addImpl(comp, name, -1); + return comp; + } + + /** + * Adds the specified component to this container at the specified index + * in the component list. + * + * @param comp The component to be added. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @return The same component that was added. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + public Component add(Component comp, int index) + { + addImpl(comp, null, index); + return comp; + } + + /** + * Adds the specified component to this container at the end of the + * component list. The layout manager will use the specified constraints + * when laying out this component. + * + * @param comp The component to be added to this container. + * @param constraints The layout constraints for this component. + */ + public void add(Component comp, Object constraints) + { + addImpl(comp, constraints, -1); + } + + /** + * Adds the specified component to this container at the specified index + * in the component list. The layout manager will use the specified + * constraints when layout out this component. + * + * @param comp The component to be added. + * @param constraints The layout constraints for this component. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + public void add(Component comp, Object constraints, int index) + { + addImpl(comp, constraints, index); + } + + /** + * This method is called by all the <code>add()</code> methods to perform + * the actual adding of the component. Subclasses who wish to perform + * their own processing when a component is added should override this + * method. Any subclass doing this must call the superclass version of + * this method in order to ensure proper functioning of the container. + * + * @param comp The component to be added. + * @param constraints The layout constraints for this component, or + * <code>null</code> if there are no constraints. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + protected void addImpl(Component comp, Object constraints, int index) + { + synchronized (getTreeLock ()) + { + if (index > ncomponents + || (index < 0 && index != -1) + || comp instanceof Window + || (comp instanceof Container + && ((Container) comp).isAncestorOf(this))) + throw new IllegalArgumentException(); + + // Reparent component, and make sure component is instantiated if + // we are. + if (comp.parent != null) + comp.parent.remove(comp); + comp.parent = this; + if (peer != null) + { + if (comp.isLightweight ()) + { + enableEvents (comp.eventMask); + if (!isLightweight ()) + enableEvents (AWTEvent.PAINT_EVENT_MASK); + } + } + + invalidate(); + + if (component == null) + component = new Component[4]; // FIXME, better initial size? + + // This isn't the most efficient implementation. We could do less + // copying when growing the array. It probably doesn't matter. + if (ncomponents >= component.length) + { + int nl = component.length * 2; + Component[] c = new Component[nl]; + System.arraycopy(component, 0, c, 0, ncomponents); + component = c; + } + + if (index == -1) + component[ncomponents++] = comp; + else + { + System.arraycopy(component, index, component, index + 1, + ncomponents - index); + component[index] = comp; + ++ncomponents; + } + + // Notify the layout manager. + if (layoutMgr != null) + { + if (layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + lm2.addLayoutComponent(comp, constraints); + } + else if (constraints instanceof String) + layoutMgr.addLayoutComponent((String) constraints, comp); + else + layoutMgr.addLayoutComponent(null, comp); + } + + if (isShowing ()) + { + // Post event to notify of adding the component. + ContainerEvent ce = new ContainerEvent(this, + ContainerEvent.COMPONENT_ADDED, + comp); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + } + + /** + * Removes the component at the specified index from this container. + * + * @param index The index of the component to remove. + */ + public void remove(int index) + { + synchronized (getTreeLock ()) + { + Component r = component[index]; + + r.removeNotify(); + + System.arraycopy(component, index + 1, component, index, + ncomponents - index - 1); + component[--ncomponents] = null; + + invalidate(); + + if (layoutMgr != null) + layoutMgr.removeLayoutComponent(r); + + r.parent = null; + + if (isShowing ()) + { + // Post event to notify of removing the component. + ContainerEvent ce = new ContainerEvent(this, + ContainerEvent.COMPONENT_REMOVED, + r); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + } + + /** + * Removes the specified component from this container. + * + * @param comp The component to remove from this container. + */ + public void remove(Component comp) + { + synchronized (getTreeLock ()) + { + for (int i = 0; i < ncomponents; ++i) + { + if (component[i] == comp) + { + remove(i); + break; + } + } + } + } + + /** + * Removes all components from this container. + */ + public void removeAll() + { + synchronized (getTreeLock ()) + { + while (ncomponents > 0) + remove(0); + } + } + + /** + * Returns the current layout manager for this container. + * + * @return The layout manager for this container. + */ + public LayoutManager getLayout() + { + return layoutMgr; + } + + /** + * Sets the layout manager for this container to the specified layout + * manager. + * + * @param mgr The new layout manager for this container. + */ + public void setLayout(LayoutManager mgr) + { + layoutMgr = mgr; + invalidate(); + } + + /** + * Layout the components in this container. + */ + public void doLayout() + { + layout (); + } + + /** + * Layout the components in this container. + * + * @deprecated use {@link #doLayout()} instead + */ + public void layout() + { + if (layoutMgr != null) + layoutMgr.layoutContainer (this); + } + + /** + * Invalidates this container to indicate that it (and all parent + * containers) need to be laid out. + */ + public void invalidate() + { + super.invalidate(); + } + + /** + * Re-lays out the components in this container. + */ + public void validate() + { + synchronized (getTreeLock ()) + { + if (! isValid() && peer != null) + { + validateTree(); + } + } + } + + /** + * Recursively invalidates the container tree. + */ + void invalidateTree() + { + for (int i = 0; i < ncomponents; i++) + { + Component comp = component[i]; + comp.invalidate(); + if (comp instanceof Container) + ((Container) comp).invalidateTree(); + } + } + + /** + * Recursively validates the container tree, recomputing any invalid + * layouts. + */ + protected void validateTree() + { + if (valid) + return; + + ContainerPeer cPeer = null; + if (peer != null && ! (peer instanceof LightweightPeer)) + { + cPeer = (ContainerPeer) peer; + cPeer.beginValidate(); + } + + for (int i = 0; i < ncomponents; ++i) + { + Component comp = component[i]; + + if (comp.getPeer () == null) + comp.addNotify(); + } + + doLayout (); + for (int i = 0; i < ncomponents; ++i) + { + Component comp = component[i]; + + if (! comp.isValid()) + { + if (comp instanceof Container) + { + ((Container) comp).validateTree(); + } + else + { + component[i].validate(); + } + } + } + + /* children will call invalidate() when they are layed out. It + is therefore important that valid is not set to true + until after the children have been layed out. */ + valid = true; + + if (cPeer != null) + cPeer.endValidate(); + } + + public void setFont(Font f) + { + super.setFont(f); + // FIXME: Although it might make more sense to invalidate only + // those children whose font == null, Sun invalidates all children. + // So we'll do the same. + invalidateTree(); + } + + /** + * Returns the preferred size of this container. + * + * @return The preferred size of this container. + */ + public Dimension getPreferredSize() + { + return preferredSize (); + } + + /** + * Returns the preferred size of this container. + * + * @return The preferred size of this container. + * + * @deprecated use {@link #getPreferredSize()} instead + */ + public Dimension preferredSize() + { + if (layoutMgr != null) + return layoutMgr.preferredLayoutSize (this); + else + return super.preferredSize (); + } + + /** + * Returns the minimum size of this container. + * + * @return The minimum size of this container. + */ + public Dimension getMinimumSize() + { + return minimumSize (); + } + + /** + * Returns the minimum size of this container. + * + * @return The minimum size of this container. + * + * @deprecated use {@link #getMinimumSize()} instead + */ + public Dimension minimumSize() + { + if (layoutMgr != null) + return layoutMgr.minimumLayoutSize (this); + else + return super.minimumSize (); + } + + /** + * Returns the maximum size of this container. + * + * @return The maximum size of this container. + */ + public Dimension getMaximumSize() + { + if (layoutMgr != null && layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + return lm2.maximumLayoutSize(this); + } + else + return super.getMaximumSize(); + } + + /** + * Returns the preferred alignment along the X axis. This is a value + * between 0 and 1 where 0 represents alignment flush left and + * 1 means alignment flush right, and 0.5 means centered. + * + * @return The preferred alignment along the X axis. + */ + public float getAlignmentX() + { + if (layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + return lm2.getLayoutAlignmentX(this); + } + else + return super.getAlignmentX(); + } + + /** + * Returns the preferred alignment along the Y axis. This is a value + * between 0 and 1 where 0 represents alignment flush top and + * 1 means alignment flush bottom, and 0.5 means centered. + * + * @return The preferred alignment along the Y axis. + */ + public float getAlignmentY() + { + if (layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + return lm2.getLayoutAlignmentY(this); + } + else + return super.getAlignmentY(); + } + + /** + * Paints this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g The graphics context for this paint job. + */ + public void paint(Graphics g) + { + if (!isShowing()) + return; + // Paint self first. + super.paint(g); + // Visit heavyweights as well, in case they were + // erased when we cleared the background for this container. + visitChildren(g, GfxPaintVisitor.INSTANCE, false); + } + + /** + * Updates this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g The graphics context for this update. + */ + public void update(Graphics g) + { + super.update(g); + } + + /** + * Prints this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g The graphics context for this print job. + */ + public void print(Graphics g) + { + super.print(g); + visitChildren(g, GfxPrintVisitor.INSTANCE, true); + } + + /** + * Paints all of the components in this container. + * + * @param g The graphics context for this paint job. + */ + public void paintComponents(Graphics g) + { + super.paint(g); + visitChildren(g, GfxPaintAllVisitor.INSTANCE, true); + } + + /** + * Prints all of the components in this container. + * + * @param g The graphics context for this print job. + */ + public void printComponents(Graphics g) + { + super.paint(g); + visitChildren(g, GfxPrintAllVisitor.INSTANCE, true); + } + + /** + * Adds the specified container listener to this object's list of + * container listeners. + * + * @param listener The listener to add. + */ + public synchronized void addContainerListener(ContainerListener listener) + { + containerListener = AWTEventMulticaster.add(containerListener, listener); + } + + /** + * Removes the specified container listener from this object's list of + * container listeners. + * + * @param listener The listener to remove. + */ + public synchronized void removeContainerListener(ContainerListener listener) + { + containerListener = AWTEventMulticaster.remove(containerListener, listener); + } + + /** + * @since 1.4 + */ + public synchronized ContainerListener[] getContainerListeners() + { + return (ContainerListener[]) + AWTEventMulticaster.getListeners(containerListener, + ContainerListener.class); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Container. FooListeners are registered using the addFooListener + * method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements @see java.util.EventListener. + * + * @since 1.3 + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == ContainerListener.class) + return getContainerListeners(); + return super.getListeners(listenerType); + } + + /** + * Processes the specified event. This method calls + * <code>processContainerEvent()</code> if this method is a + * <code>ContainerEvent</code>, otherwise it calls the superclass + * method. + * + * @param e The event to be processed. + */ + protected void processEvent(AWTEvent e) + { + if (e instanceof ContainerEvent) + processContainerEvent((ContainerEvent) e); + else + super.processEvent(e); + } + + /** + * Called when a container event occurs if container events are enabled. + * This method calls any registered listeners. + * + * @param e The event that occurred. + */ + protected void processContainerEvent(ContainerEvent e) + { + if (containerListener == null) + return; + switch (e.id) + { + case ContainerEvent.COMPONENT_ADDED: + containerListener.componentAdded(e); + break; + + case ContainerEvent.COMPONENT_REMOVED: + containerListener.componentRemoved(e); + break; + } + } + + /** + * AWT 1.0 event processor. + * + * @param e The event that occurred. + * + * @deprecated use {@link #dispatchEvent(AWTEvent)} instead + */ + public void deliverEvent(Event e) + { + if (!handleEvent (e)) + { + synchronized (getTreeLock ()) + { + Component parent = getParent (); + + if (parent != null) + parent.deliverEvent (e); + } + } + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case <code>null</code> is returned. + * + * @param x The X coordinate of the point. + * @param y The Y coordinate of the point. + * + * @return The component containing the specified point, or + * <code>null</code> if there is no such point. + */ + public Component getComponentAt(int x, int y) + { + return locate (x, y); + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case <code>null</code> is returned. + * + * @param x The x position of the point to return the component at. + * @param y The y position of the point to return the component at. + * + * @return The component containing the specified point, or <code>null</code> + * if there is no such point. + * + * @deprecated use {@link #getComponentAt(int, int)} instead + */ + public Component locate(int x, int y) + { + synchronized (getTreeLock ()) + { + if (!contains (x, y)) + return null; + for (int i = 0; i < ncomponents; ++i) + { + // Ignore invisible children... + if (!component[i].isVisible ()) + continue; + + int x2 = x - component[i].x; + int y2 = y - component[i].y; + if (component[i].contains (x2, y2)) + return component[i]; + } + return this; + } + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case <code>null</code> is returned. + * + * @param p The point to return the component at. + * @return The component containing the specified point, or <code>null</code> + * if there is no such point. + */ + public Component getComponentAt(Point p) + { + return getComponentAt (p.x, p.y); + } + + public Component findComponentAt(int x, int y) + { + synchronized (getTreeLock ()) + { + if (! contains(x, y)) + return null; + + for (int i = 0; i < ncomponents; ++i) + { + // Ignore invisible children... + if (!component[i].isVisible()) + continue; + + int x2 = x - component[i].x; + int y2 = y - component[i].y; + // We don't do the contains() check right away because + // findComponentAt would redundantly do it first thing. + if (component[i] instanceof Container) + { + Container k = (Container) component[i]; + Component r = k.findComponentAt(x2, y2); + if (r != null) + return r; + } + else if (component[i].contains(x2, y2)) + return component[i]; + } + + return this; + } + } + + public Component findComponentAt(Point p) + { + return findComponentAt(p.x, p.y); + } + + /** + * Called when this container is added to another container to inform it + * to create its peer. Peers for any child components will also be + * created. + */ + public void addNotify() + { + super.addNotify(); + addNotifyContainerChildren(); + } + + /** + * Called when this container is removed from its parent container to + * inform it to destroy its peer. This causes the peers of all child + * component to be destroyed as well. + */ + public void removeNotify() + { + synchronized (getTreeLock ()) + { + for (int i = 0; i < ncomponents; ++i) + component[i].removeNotify(); + super.removeNotify(); + } + } + + /** + * Tests whether or not the specified component is contained within + * this components subtree. + * + * @param comp The component to test. + * + * @return <code>true</code> if this container is an ancestor of the + * specified component, <code>false</code> otherwise. + */ + public boolean isAncestorOf(Component comp) + { + synchronized (getTreeLock ()) + { + while (true) + { + if (comp == null) + return false; + if (comp == this) + return true; + comp = comp.getParent(); + } + } + } + + /** + * Returns a string representing the state of this container for + * debugging purposes. + * + * @return A string representing the state of this container. + */ + protected String paramString() + { + if (layoutMgr == null) + return super.paramString(); + + StringBuffer sb = new StringBuffer(); + sb.append(super.paramString()); + sb.append(",layout="); + sb.append(layoutMgr.getClass().getName()); + return sb.toString(); + } + + /** + * Writes a listing of this container to the specified stream starting + * at the specified indentation point. + * + * @param out The <code>PrintStream</code> to write to. + * @param indent The indentation point. + */ + public void list(PrintStream out, int indent) + { + synchronized (getTreeLock ()) + { + super.list(out, indent); + for (int i = 0; i < ncomponents; ++i) + component[i].list(out, indent + 2); + } + } + + /** + * Writes a listing of this container to the specified stream starting + * at the specified indentation point. + * + * @param out The <code>PrintWriter</code> to write to. + * @param indent The indentation point. + */ + public void list(PrintWriter out, int indent) + { + synchronized (getTreeLock ()) + { + super.list(out, indent); + for (int i = 0; i < ncomponents; ++i) + component[i].list(out, indent + 2); + } + } + + /** + * Sets the focus traversal keys for a given traversal operation for this + * Container. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, + * or if keystrokes contains null, or if any Object in keystrokes is not an + * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, or if any + * keystroke already maps to another focus traversal operation for this + * Container. + * + * @since 1.4 + */ + public void setFocusTraversalKeys(int id, Set keystrokes) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + if (keystrokes == null) + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + + Set sa; + Set sb; + Set sc; + String name; + switch (id) + { + case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "forwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "backwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "upCycleFocusTraversalKeys"; + break; + case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "downCycleFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || sc.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + + if (focusTraversalKeys == null) + focusTraversalKeys = new Set[3]; + + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); + + focusTraversalKeys[id] = keystrokes; + } + + /** + * Returns the Set of focus traversal keys for a given traversal operation for + * this Container. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. + * + * @since 1.4 + */ + public Set getFocusTraversalKeys (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + Set s = null; + + if (focusTraversalKeys != null) + s = focusTraversalKeys[id]; + + if (s == null && parent != null) + s = parent.getFocusTraversalKeys (id); + + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getDefaultFocusTraversalKeys(id)) : s; + } + + /** + * Returns whether the Set of focus traversal keys for the given focus + * traversal operation has been explicitly defined for this Container. + * If this method returns false, this Container is inheriting the Set from + * an ancestor, or from the current KeyboardFocusManager. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. + * + * @since 1.4 + */ + public boolean areFocusTraversalKeysSet (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + return focusTraversalKeys != null && focusTraversalKeys[id] != null; + } + + /** + * Check whether the given Container is the focus cycle root of this + * Container's focus traversal cycle. If this Container is a focus + * cycle root itself, then it will be in two different focus cycles + * -- it's own, and that of its ancestor focus cycle root's. In + * that case, if <code>c</code> is either of those containers, this + * method will return true. + * + * @param c the candidate Container + * + * @return true if c is the focus cycle root of the focus traversal + * cycle to which this Container belongs, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot (Container c) + { + if (this == c + && isFocusCycleRoot ()) + return true; + + Container ancestor = getFocusCycleRootAncestor (); + + if (c == ancestor) + return true; + + return false; + } + + /** + * If this Container is a focus cycle root, set the focus traversal + * policy that determines the focus traversal order for its + * children. If non-null, this policy will be inherited by all + * inferior focus cycle roots. If <code>policy</code> is null, this + * Container will inherit its policy from the closest ancestor focus + * cycle root that's had its policy set. + * + * @param policy the new focus traversal policy for this Container or null + * + * @since 1.4 + */ + public void setFocusTraversalPolicy (FocusTraversalPolicy policy) + { + focusTraversalPolicy = policy; + } + + /** + * Return the focus traversal policy that determines the focus + * traversal order for this Container's children. This method + * returns null if this Container is not a focus cycle root. If the + * focus traversal policy has not been set explicitly, then this + * method will return an ancestor focus cycle root's policy instead. + * + * @return this Container's focus traversal policy or null + * + * @since 1.4 + */ + public FocusTraversalPolicy getFocusTraversalPolicy () + { + if (!isFocusCycleRoot ()) + return null; + + if (focusTraversalPolicy == null) + { + Container ancestor = getFocusCycleRootAncestor (); + + if (ancestor != this) + return ancestor.getFocusTraversalPolicy (); + else + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + return manager.getDefaultFocusTraversalPolicy (); + } + } + else + return focusTraversalPolicy; + } + + /** + * Check whether this Container's focus traversal policy has been + * explicitly set. If it has not, then this Container will inherit + * its focus traversal policy from one of its ancestor focus cycle + * roots. + * + * @return true if focus traversal policy is set, false otherwise + */ + public boolean isFocusTraversalPolicySet () + { + return focusTraversalPolicy == null; + } + + /** + * Set whether or not this Container is the root of a focus + * traversal cycle. This Container's focus traversal policy + * determines the order of focus traversal. Some policies prevent + * the focus from being transferred between two traversal cycles + * until an up or down traversal operation is performed. In that + * case, normal traversal (not up or down) is limited to this + * Container and all of this Container's descendents that are not + * descendents of inferior focus cycle roots. In the default case + * however, ContainerOrderFocusTraversalPolicy is in effect, and it + * supports implicit down-cycle traversal operations. + * + * @param focusCycleRoot true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public void setFocusCycleRoot (boolean focusCycleRoot) + { + this.focusCycleRoot = focusCycleRoot; + } + + /** + * Check whether this Container is a focus cycle root. + * + * @return true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot () + { + return focusCycleRoot; + } + + /** + * Transfer focus down one focus traversal cycle. If this Container + * is a focus cycle root, then its default component becomes the + * focus owner, and this Container becomes the current focus cycle + * root. No traversal will occur if this Container is not a focus + * cycle root. + * + * @since 1.4 + */ + public void transferFocusDownCycle () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + manager.downFocusCycle (this); + } + + /** + * Sets the ComponentOrientation property of this container and all components + * contained within it. + * + * @exception NullPointerException If orientation is null + * + * @since 1.4 + */ + public void applyComponentOrientation (ComponentOrientation orientation) + { + if (orientation == null) + throw new NullPointerException (); + } + + public void addPropertyChangeListener (PropertyChangeListener listener) + { + if (listener == null) + return; + + if (changeSupport == null) + changeSupport = new PropertyChangeSupport (this); + + changeSupport.addPropertyChangeListener (listener); + } + + public void addPropertyChangeListener (String name, + PropertyChangeListener listener) + { + if (listener == null) + return; + + if (changeSupport == null) + changeSupport = new PropertyChangeSupport (this); + + changeSupport.addPropertyChangeListener (name, listener); + } + + // Hidden helper methods. + + /** + * Perform a graphics operation on the children of this container. + * For each applicable child, the visitChild() method will be called + * to perform the graphics operation. + * + * @param gfx The graphics object that will be used to derive new + * graphics objects for the children. + * + * @param visitor Object encapsulating the graphics operation that + * should be performed. + * + * @param lightweightOnly If true, only lightweight components will + * be visited. + */ + private void visitChildren(Graphics gfx, GfxVisitor visitor, + boolean lightweightOnly) + { + synchronized (getTreeLock ()) + { + for (int i = ncomponents - 1; i >= 0; --i) + { + Component comp = component[i]; + // If we're visiting heavyweights as well, + // don't recurse into Containers here. This avoids + // painting the same nested child multiple times. + boolean applicable = comp.isVisible() + && (comp.isLightweight() + || !lightweightOnly && ! (comp instanceof Container)); + + if (applicable) + visitChild(gfx, visitor, comp); + } + } + } + + /** + * Perform a graphics operation on a child. A translated and clipped + * graphics object will be created, and the visit() method of the + * visitor will be called to perform the operation. + * + * @param gfx The graphics object that will be used to derive new + * graphics objects for the child. + * + * @param visitor Object encapsulating the graphics operation that + * should be performed. + * + * @param comp The child component that should be visited. + */ + private void visitChild(Graphics gfx, GfxVisitor visitor, + Component comp) + { + Rectangle bounds = comp.getBounds(); + Rectangle oldClip = gfx.getClipBounds(); + if (oldClip == null) + oldClip = bounds; + + Rectangle clip = oldClip.intersection(bounds); + + if (clip.isEmpty()) return; + + boolean clipped = false; + boolean translated = false; + try + { + gfx.setClip(clip.x, clip.y, clip.width, clip.height); + clipped = true; + gfx.translate(bounds.x, bounds.y); + translated = true; + visitor.visit(comp, gfx); + } + finally + { + if (translated) + gfx.translate (-bounds.x, -bounds.y); + if (clipped) + gfx.setClip (oldClip.x, oldClip.y, oldClip.width, oldClip.height); + } + } + + void dispatchEventImpl(AWTEvent e) + { + // Give lightweight dispatcher a chance to handle it. + if (eventTypeEnabled (e.id) + && dispatcher != null + && dispatcher.handleEvent (e)) + return; + + if ((e.id <= ContainerEvent.CONTAINER_LAST + && e.id >= ContainerEvent.CONTAINER_FIRST) + && (containerListener != null + || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + // This is used to implement Component.transferFocus. + Component findNextFocusComponent(Component child) + { + synchronized (getTreeLock ()) + { + int start, end; + if (child != null) + { + for (start = 0; start < ncomponents; ++start) + { + if (component[start] == child) + break; + } + end = start; + // This special case lets us be sure to terminate. + if (end == 0) + end = ncomponents; + ++start; + } + else + { + start = 0; + end = ncomponents; + } + + for (int j = start; j != end; ++j) + { + if (j >= ncomponents) + { + // The JCL says that we should wrap here. However, that + // seems wrong. To me it seems that focus order should be + // global within in given window. So instead if we reach + // the end we try to look in our parent, if we have one. + if (parent != null) + return parent.findNextFocusComponent(this); + j -= ncomponents; + } + if (component[j] instanceof Container) + { + Component c = component[j]; + c = c.findNextFocusComponent(null); + if (c != null) + return c; + } + else if (component[j].isFocusTraversable()) + return component[j]; + } + + return null; + } + } + + private void addNotifyContainerChildren() + { + synchronized (getTreeLock ()) + { + for (int i = ncomponents; --i >= 0; ) + { + component[i].addNotify(); + if (component[i].isLightweight ()) + { + + // If we're not lightweight, and we just got a lightweight + // child, we need a lightweight dispatcher to feed it events. + if (! this.isLightweight()) + { + if (dispatcher == null) + dispatcher = new LightweightDispatcher (this); + } + + + enableEvents(component[i].eventMask); + if (peer != null && !isLightweight ()) + enableEvents (AWTEvent.PAINT_EVENT_MASK); + } + } + } + } + + /** + * Deserialize this Container: + * <ol> + * <li>Read from the stream the default serializable fields.</li> + * <li>Read a list of serializable ContainerListeners as optional + * data. If the list is null, no listeners will be registered.</li> + * <li>Read this Container's FocusTraversalPolicy as optional data. + * If this is null, then this Container will use a + * DefaultFocusTraversalPolicy.</li> + * </ol> + * + * @param s the stream to read from + * @throws ClassNotFoundException if deserialization fails + * @throws IOException if the stream fails + */ + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject (); + String key = (String) s.readObject (); + while (key != null) + { + Object object = s.readObject (); + if ("containerL".equals (key)) + addContainerListener((ContainerListener) object); + // FIXME: under what key is the focus traversal policy stored? + else if ("focusTraversalPolicy".equals (key)) + setFocusTraversalPolicy ((FocusTraversalPolicy) object); + + key = (String) s.readObject(); + } + } + + /** + * Serialize this Container: + * <ol> + * <li>Write to the stream the default serializable fields.</li> + * <li>Write the list of serializable ContainerListeners as optional + * data.</li> + * <li>Write this Container's FocusTraversalPolicy as optional data.</li> + * </ol> + * + * @param s the stream to write to + * @throws IOException if the stream fails + */ + private void writeObject (ObjectOutputStream s) throws IOException + { + s.defaultWriteObject (); + AWTEventMulticaster.save (s, "containerL", containerListener); + if (focusTraversalPolicy instanceof Serializable) + s.writeObject (focusTraversalPolicy); + else + s.writeObject (null); + } + + // Nested classes. + + /* The following classes are used in concert with the + visitChildren() method to implement all the graphics operations + that requires traversal of the containment hierarchy. */ + + abstract static class GfxVisitor + { + public abstract void visit(Component c, Graphics gfx); + } + + static class GfxPaintVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPaintVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.paint(gfx); + } + } + + static class GfxPrintVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPrintVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.print(gfx); + } + } + + static class GfxPaintAllVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPaintAllVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.paintAll(gfx); + } + } + + static class GfxPrintAllVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPrintAllVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.printAll(gfx); + } + } + + /** + * This class provides accessibility support for subclasses of container. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.3 + */ + protected class AccessibleAWTContainer extends AccessibleAWTComponent + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 5081320404842566097L; + + /** + * The handler to fire PropertyChange when children are added or removed. + * + * @serial the handler for property changes + */ + protected ContainerListener accessibleContainerHandler + = new AccessibleContainerHandler(); + + /** + * The default constructor. + */ + protected AccessibleAWTContainer() + { + Container.this.addContainerListener(accessibleContainerHandler); + } + + /** + * Return the number of accessible children of the containing accessible + * object (at most the total number of its children). + * + * @return the number of accessible children + */ + public int getAccessibleChildrenCount() + { + synchronized (getTreeLock ()) + { + int count = 0; + int i = component == null ? 0 : component.length; + while (--i >= 0) + if (component[i] instanceof Accessible) + count++; + return count; + } + } + + /** + * Return the nth accessible child of the containing accessible object. + * + * @param i the child to grab, zero-based + * @return the accessible child, or null + */ + public Accessible getAccessibleChild(int i) + { + synchronized (getTreeLock ()) + { + if (component == null) + return null; + int index = -1; + while (i >= 0 && ++index < component.length) + if (component[index] instanceof Accessible) + i--; + if (i < 0) + return (Accessible) component[index]; + return null; + } + } + + /** + * Return the accessible child located at point (in the parent's + * coordinates), if one exists. + * + * @param p the point to look at + * + * @return an accessible object at that point, or null + * + * @throws NullPointerException if p is null + */ + public Accessible getAccessibleAt(Point p) + { + Component c = getComponentAt(p.x, p.y); + return c != Container.this && c instanceof Accessible ? (Accessible) c + : null; + } + + /** + * This class fires a <code>PropertyChange</code> listener, if registered, + * when children are added or removed from the enclosing accessible object. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.3 + */ + protected class AccessibleContainerHandler implements ContainerListener + { + /** + * Default constructor. + */ + protected AccessibleContainerHandler() + { + } + + /** + * Fired when a component is added; forwards to the PropertyChange + * listener. + * + * @param e the container event for adding + */ + public void componentAdded(ContainerEvent e) + { + AccessibleAWTContainer.this.firePropertyChange + (ACCESSIBLE_CHILD_PROPERTY, null, e.getChild()); + } + + /** + * Fired when a component is removed; forwards to the PropertyChange + * listener. + * + * @param e the container event for removing + */ + public void componentRemoved(ContainerEvent e) + { + AccessibleAWTContainer.this.firePropertyChange + (ACCESSIBLE_CHILD_PROPERTY, e.getChild(), null); + } + } // class AccessibleContainerHandler + } // class AccessibleAWTContainer +} // class Container + +/** + * There is a helper class implied from stack traces called + * LightweightDispatcher, but since it is not part of the public API, + * rather than mimic it exactly we write something which does "roughly + * the same thing". + */ + +class LightweightDispatcher implements Serializable +{ + private static final long serialVersionUID = 5184291520170872969L; + private Container nativeContainer; + private Cursor nativeCursor; + private long eventMask; + + private transient Component mouseEventTarget; + private transient Component pressedComponent; + private transient Component lastComponentEntered; + private transient Component tempComponent; + private transient int pressCount; + + LightweightDispatcher(Container c) + { + nativeContainer = c; + } + + void acquireComponentForMouseEvent(MouseEvent me) + { + int x = me.getX (); + int y = me.getY (); + + // Find the candidate which should receive this event. + Component parent = nativeContainer; + Component candidate = null; + Point p = me.getPoint(); + while (candidate == null && parent != null) + { + candidate = + SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); + if (candidate == null || (candidate.eventMask & me.getID()) == 0) + { + candidate = null; + p = SwingUtilities.convertPoint(parent, p.x, p.y, parent.parent); + parent = parent.parent; + } + } + + // If the only candidate we found was the native container itself, + // don't dispatch any event at all. We only care about the lightweight + // children here. + if (candidate == nativeContainer) + candidate = null; + + // If our candidate is new, inform the old target we're leaving. + if (lastComponentEntered != null + && lastComponentEntered.isShowing() + && lastComponentEntered != candidate) + { + // Old candidate could have been removed from + // the nativeContainer so we check first. + if (SwingUtilities.isDescendingFrom(lastComponentEntered, nativeContainer)) + { + Point tp = + SwingUtilities.convertPoint(nativeContainer, + x, y, lastComponentEntered); + MouseEvent exited = new MouseEvent (lastComponentEntered, + MouseEvent.MOUSE_EXITED, + me.getWhen (), + me.getModifiersEx (), + tp.x, tp.y, + me.getClickCount (), + me.isPopupTrigger (), + me.getButton ()); + tempComponent = lastComponentEntered; + lastComponentEntered = null; + tempComponent.dispatchEvent(exited); + } + lastComponentEntered = null; + } + // If we have a candidate, maybe enter it. + if (candidate != null) + { + mouseEventTarget = candidate; + if (candidate.isLightweight() + && candidate.isShowing() + && candidate != nativeContainer + && candidate != lastComponentEntered) + { + lastComponentEntered = mouseEventTarget; + Point cp = SwingUtilities.convertPoint(nativeContainer, + x, y, lastComponentEntered); + MouseEvent entered = new MouseEvent (lastComponentEntered, + MouseEvent.MOUSE_ENTERED, + me.getWhen (), + me.getModifiersEx (), + cp.x, cp.y, + me.getClickCount (), + me.isPopupTrigger (), + me.getButton ()); + lastComponentEntered.dispatchEvent (entered); + } + } + + if (me.getID() == MouseEvent.MOUSE_RELEASED + || me.getID() == MouseEvent.MOUSE_PRESSED && pressCount > 0 + || me.getID() == MouseEvent.MOUSE_DRAGGED) + // If any of the following events occur while a button is held down, + // they should be dispatched to the same component to which the + // original MOUSE_PRESSED event was dispatched: + // - MOUSE_RELEASED + // - MOUSE_PRESSED: another button pressed while the first is held down + // - MOUSE_DRAGGED + if (SwingUtilities.isDescendingFrom(pressedComponent, nativeContainer)) + mouseEventTarget = pressedComponent; + else if (me.getID() == MouseEvent.MOUSE_CLICKED) + { + // Don't dispatch CLICKED events whose target is not the same as the + // target for the original PRESSED event. + if (candidate != pressedComponent) + mouseEventTarget = null; + else if (pressCount == 0) + pressedComponent = null; + } + } + + boolean handleEvent(AWTEvent e) + { + if (e instanceof MouseEvent) + { + MouseEvent me = (MouseEvent) e; + + acquireComponentForMouseEvent(me); + + // Avoid dispatching ENTERED and EXITED events twice. + if (mouseEventTarget != null + && mouseEventTarget.isShowing() + && e.getID() != MouseEvent.MOUSE_ENTERED + && e.getID() != MouseEvent.MOUSE_EXITED) + { + MouseEvent newEvt = + SwingUtilities.convertMouseEvent(nativeContainer, me, + mouseEventTarget); + mouseEventTarget.dispatchEvent(newEvt); + + switch (e.getID()) + { + case MouseEvent.MOUSE_PRESSED: + if (pressCount++ == 0) + pressedComponent = mouseEventTarget; + break; + + case MouseEvent.MOUSE_RELEASED: + // Clear our memory of the original PRESSED event, only if + // we're not expecting a CLICKED event after this. If + // there is a CLICKED event after this, it will do clean up. + if (--pressCount == 0 + && mouseEventTarget != pressedComponent) + pressedComponent = null; + break; + } + if (newEvt.isConsumed()) + e.consume(); + } + } + + return e.isConsumed(); + } + +} // class LightweightDispatcher diff --git a/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java new file mode 100644 index 00000000000..152482c887b --- /dev/null +++ b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java @@ -0,0 +1,413 @@ +/* ContainerOrderFocusTraversalPolicy.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * ContainerOrderFocusTraversalPolicy defines a focus traversal order + * based on the order in which Components were packed in a Container. + * This policy performs a pre-order traversal of the Component + * hierarchy starting from a given focus cycle root. Portions of the + * hierarchy that are not visible and displayable are skipped. + * + * By default, this policy transfers focus down-cycle implicitly. + * That is, if a forward traversal is requested on a focus cycle root + * and the focus cycle root has focusable children, the focus will + * automatically be transfered down to the lower focus cycle. + * + * The default implementation of accept accepts only Components that + * are visible, displayable, enabled and focusable. Derived classes + * can override these acceptance criteria by overriding accept. + * + * @author Michael Koch + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @since 1.4 + */ +public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy + implements Serializable +{ + /** + * Compatible to JDK 1.4+ + */ + static final long serialVersionUID = 486933713763926351L; + + /** + * True if implicit down cycling is enabled. + */ + private boolean implicitDownCycleTraversal = true; + + /** + * Creates the <code>ContainerOrderFocusTraversalPolicy</code> object. + */ + public ContainerOrderFocusTraversalPolicy () + { + // Nothing to do here + } + + /** + * Returns the Component that should receive the focus after current. + * root must be a focus cycle root of current. + * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * + * @return the next Component in the focus traversal order for root, + * or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is not a focus cycle + * root of current, or if either root or current is null. + */ + public Component getComponentAfter (Container root, Component current) + { + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + if (!root.isFocusCycleRoot ()) + throw new IllegalArgumentException ("root is not a focus cycle root"); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: is this the right thing to do here? It moves the context + // for traversal up one focus traversal cycle. We'll need a test + // for this. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + // Check if we've reached the top of the component hierarchy. If + // so then we want to loop around to the first component in the + // focus traversal cycle. + if (current instanceof Window) + return getFirstComponent ((Container) current); + + Container parent = current.getParent (); + + synchronized (parent.getTreeLock ()) + { + Component[] components = parent.getComponents (); + int componentIndex = 0; + int numComponents = parent.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i] == current) + componentIndex = i; + } + + // Search forward for the next acceptable component. + for (int i = componentIndex + 1; i < numComponents; i++) + { + if (accept (components[i])) + return components[i]; + + if (components[i] instanceof Container) + { + Component result = getFirstComponent ((Container) components[i]); + + if (result != null + && implicitDownCycleTraversal) + return result; + } + } + + // No focusable components after current in its Container. So go + // to the next Component after current's Container (parent). + Component result = getComponentAfter (root, parent); + + return result; + } + } + + /** + * Returns the Component that should receive the focus before + * <code>current</code>. <code>root</code> must be a focus cycle + * root of current. + * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * + * @return the previous Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is not a focus cycle + * root of current, or if either root or current is null. + */ + public Component getComponentBefore (Container root, Component current) + { + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + if (!root.isFocusCycleRoot ()) + throw new IllegalArgumentException ("root is not a focus cycle root"); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: is this the right thing to do here? It moves the context + // for traversal up one focus traversal cycle. We'll need a test + // for this. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + // Check if we've reached the top of the component hierarchy. If + // so then we want to loop around to the last component in the + // focus traversal cycle. + if (current instanceof Window) + return getLastComponent ((Container) current); + + Container parent = current.getParent (); + + synchronized (parent.getTreeLock ()) + { + Component[] components = parent.getComponents (); + int componentIndex = 0; + int numComponents = parent.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i] == current) + componentIndex = i; + } + + // Search backward for the next acceptable component. + for (int i = componentIndex - 1; i >= 0; i--) + { + if (accept (components[i])) + return components[i]; + + if (components[i] instanceof Container) + { + Component result = getLastComponent ((Container) components[i]); + + if (result != null) + return result; + } + } + + // No focusable components before current in its Container. So go + // to the previous Component before current's Container (parent). + Component result = getComponentBefore (root, parent); + + return result; + } + } + + /** + * Returns the first Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the first Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getFirstComponent(Container root) + { + if (root == null) + throw new IllegalArgumentException (); + + if (!root.isVisible () + || !root.isDisplayable ()) + return null; + + if (accept (root)) + return root; + + Component[] componentArray = root.getComponents (); + + for (int i = 0; i < componentArray.length; i++) + { + Component component = componentArray [i]; + + if (accept (component)) + return component; + + if (component instanceof Container) + { + Component result = getFirstComponent ((Container) component); + + if (result != null) + return result; + } + } + + return null; + } + + /** + * Returns the last Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the last Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getLastComponent (Container root) + { + if (root == null) + throw new IllegalArgumentException (); + + if (!root.isVisible () + || !root.isDisplayable ()) + return null; + + if (accept (root)) + return root; + + Component[] componentArray = root.getComponents (); + + for (int i = componentArray.length - 1; i >= 0; i--) + { + Component component = componentArray [i]; + + if (accept (component)) + return component; + + if (component instanceof Container) + { + Component result = getLastComponent ((Container) component); + + if (result != null) + return result; + } + } + + return null; + } + + /** + * Returns the default Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the default Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getDefaultComponent (Container root) + { + return getFirstComponent (root); + } + + /** + * Set whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @param value the setting for implicit down cycling + */ + public void setImplicitDownCycleTraversal (boolean value) + { + implicitDownCycleTraversal = value; + } + + /** + * Check whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @return true if the focus will be transferred down-cycle + * implicitly + */ + public boolean getImplicitDownCycleTraversal () + { + return implicitDownCycleTraversal; + } + + /** + * Check whether the given Component is an acceptable target for the + * keyboard input focus. + * + * @param current the Component to check + * + * @return true if current is acceptable, false otherwise + */ + protected boolean accept (Component current) + { + return (current.visible + && current.isDisplayable () + && current.enabled + && current.focusable); + } +} diff --git a/libjava/classpath/java/awt/Cursor.java b/libjava/classpath/java/awt/Cursor.java new file mode 100644 index 00000000000..48a63f06f4f --- /dev/null +++ b/libjava/classpath/java/awt/Cursor.java @@ -0,0 +1,224 @@ +/* Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class represents various predefined cursor types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Cursor implements java.io.Serializable +{ + static final long serialVersionUID = 8028237497568985504L; + + /** + * Constant for the system default cursor type + */ + public static final int DEFAULT_CURSOR = 0; + + /** + * Constant for a cross-hair cursor. + */ + public static final int CROSSHAIR_CURSOR = 1; + + /** + * Constant for a cursor over a text field. + */ + public static final int TEXT_CURSOR = 2; + + /** + * Constant for a cursor to display while waiting for an action to complete. + */ + public static final int WAIT_CURSOR = 3; + + /** + * Cursor used over SW corner of window decorations. + */ + public static final int SW_RESIZE_CURSOR = 4; + + /** + * Cursor used over SE corner of window decorations. + */ + public static final int SE_RESIZE_CURSOR = 5; + + /** + * Cursor used over NW corner of window decorations. + */ + public static final int NW_RESIZE_CURSOR = 6; + + /** + * Cursor used over NE corner of window decorations. + */ + public static final int NE_RESIZE_CURSOR = 7; + + /** + * Cursor used over N edge of window decorations. + */ + public static final int N_RESIZE_CURSOR = 8; + + /** + * Cursor used over S edge of window decorations. + */ + public static final int S_RESIZE_CURSOR = 9; + + /** + * Cursor used over W edge of window decorations. + */ + public static final int W_RESIZE_CURSOR = 10; + + /** + * Cursor used over E edge of window decorations. + */ + public static final int E_RESIZE_CURSOR = 11; + + /** + * Constant for a hand cursor. + */ + public static final int HAND_CURSOR = 12; + + /** + * Constant for a cursor used during window move operations. + */ + public static final int MOVE_CURSOR = 13; + + public static final int CUSTOM_CURSOR = 0xFFFFFFFF; + + private static final int PREDEFINED_COUNT = 14; + + protected static Cursor[] predefined = new Cursor[PREDEFINED_COUNT]; + protected String name; + + /** + * @serial The numeric id of this cursor. + */ + int type; + + /** + * Initializes a new instance of <code>Cursor</code> with the specified + * type. + * + * @param type The cursor type. + * + * @exception IllegalArgumentException If the specified cursor type is invalid + */ + public Cursor(int type) + { + if (type < 0 || type >= PREDEFINED_COUNT) + throw new IllegalArgumentException ("invalid cursor " + type); + + this.type = type; + // FIXME: lookup and set name? + } + + /** This constructor is used internally only. + * Application code should call Toolkit.createCustomCursor(). + */ + protected Cursor(String name) + { + this.name = name; + this.type = CUSTOM_CURSOR; + } + + /** + * Returns an instance of <code>Cursor</code> for one of the specified + * predetermined types. + * + * @param type The type contant from this class. + * + * @return The requested predefined cursor. + * + * @exception IllegalArgumentException If the constant is not one of the + * predefined cursor type constants from this class. + */ + public static Cursor getPredefinedCursor(int type) + { + if (type < 0 || type >= PREDEFINED_COUNT) + throw new IllegalArgumentException ("invalid cursor " + type); + if (predefined[type] == null) + predefined[type] = new Cursor(type); + return predefined[type]; + } + + /** + * Retrieves the system specific custom Cursor named Cursor names are, + * for example: "Invalid.16x16". + * + * @exception AWTException + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public static Cursor getSystemCustomCursor(String name) + throws AWTException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + + // FIXME + return null; + } + + /** + * Returns an instance of the system default cursor type. + * + * @return The system default cursor. + */ + public static Cursor getDefaultCursor() + { + return getPredefinedCursor(DEFAULT_CURSOR); + } + + /** + * Returns the numeric type identifier for this cursor. + * + * @return The cursor id. + */ + public int getType() + { + return type; + } + + public String getName() + { + return name; + } + + public String toString() + { + return (this.getClass() + "[" + getName() + "]"); + } +} diff --git a/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java b/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java new file mode 100644 index 00000000000..46b56d3fc29 --- /dev/null +++ b/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java @@ -0,0 +1,109 @@ +/* DefaultFocusTraversalPolicy.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * DefaultFocusTraversalPolicy is the default focus traversal policy + * used by Containers. + * + * This policy sharpens ContainerOrderFocusTraversalPolicy's + * acceptance criteria, to reject those Components that have + * unfocusable peers. Despite this extra strictness, this policy will + * always accept a Component that has explicitly been set focusable by + * any means. + * + * This AWT implementation assumes that the peers of the following + * Components are not focusable: Canvas, Panel, Label, ScrollPane, + * Scrollbar, Window, and any lightweight Component. + * + * A Component's focusability is independent of the focusability of + * its peer. + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @since 1.4 + */ +public class DefaultFocusTraversalPolicy + extends ContainerOrderFocusTraversalPolicy +{ + /** + * Construct a default focus traversal policy. + */ + public DefaultFocusTraversalPolicy () + { + } + + /** + * Check whether a given Component would be acceptable as a focus + * owner. The Component must be displayable, visible and enabled to + * be acceptable. If the Component's focus traversability has been + * overridden, by overriding Component.isFocusTraversable or + * Component.isFocusable, or by calling Component.setFocusable, then + * the Component will be accepted if it is focusable. If the + * Component uses the default focus traversable behaviour, then + * <code>comp</code> will always be rejected if it is a Canvas, + * Panel, Label, ScrollPane, Scrollbar, Window or lightweight + * Component. + * + * @param comp the Component to check + * + * @return true if the Component is an acceptable target for + * keyboard input focus, false otherwise + */ + protected boolean accept (Component comp) + { + if (comp.visible + && comp.isDisplayable () + && comp.enabled) + { + if (comp.isFocusTraversableOverridden != 0 + && (comp.isFocusTraversable () || comp.isFocusable())) + return true; + + if (!(comp instanceof Canvas + || comp instanceof Panel + || comp instanceof Label + || comp instanceof ScrollPane + || comp instanceof Scrollbar + || comp instanceof Window + || comp.isLightweight ())) + return true; + } + return false; + } +} diff --git a/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java new file mode 100644 index 00000000000..f53cc5ef04d --- /dev/null +++ b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java @@ -0,0 +1,536 @@ +/* DefaultKeyboardFocusManager.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +// FIXME: finish documentation +public class DefaultKeyboardFocusManager extends KeyboardFocusManager +{ + /** + * This class models a request to delay the dispatch of events that + * arrive after a certain time, until a certain component becomes + * the focus owner. + */ + private class EventDelayRequest implements Comparable + { + /** A {@link java.util.List} of {@link java.awt.event.KeyEvent}s + that are being delayed, pending this request's {@link + Component} receiving the keyboard focus. */ + private LinkedList enqueuedKeyEvents = new LinkedList (); + + /** An event timestamp. All events that arrive after this time + should be queued in the {@link #enqueuedKeyEvents} {@link + java.util.List}. */ + public long timestamp; + /** When this {@link Component} becomes focused, all events + between this EventDelayRequest and the next one in will be + dispatched from {@link #enqueuedKeyEvents}. */ + public Component focusedComp; + + /** + * Construct a new EventDelayRequest. + * + * @param timestamp events that arrive after this time will be + * delayed + * @param focusedComp the Component that needs to receive focus + * before events are dispatched + */ + public EventDelayRequest (long timestamp, Component focusedComp) + { + this.timestamp = timestamp; + this.focusedComp = focusedComp; + } + + public int compareTo (Object o) + { + if (!(o instanceof EventDelayRequest)) + throw new ClassCastException (); + + EventDelayRequest request = (EventDelayRequest) o; + + if (request.timestamp < timestamp) + return -1; + else if (request.timestamp == timestamp) + return 0; + else + return 1; + } + + public boolean equals (Object o) + { + if (!(o instanceof EventDelayRequest) || o == null) + return false; + + EventDelayRequest request = (EventDelayRequest) o; + + return (request.timestamp == timestamp + && request.focusedComp == focusedComp); + } + + public void enqueueEvent (KeyEvent e) + { + KeyEvent last = (KeyEvent) enqueuedKeyEvents.getLast (); + if (last != null && e.getWhen () < last.getWhen ()) + throw new RuntimeException ("KeyEvents enqueued out-of-order"); + + if (e.getWhen () <= timestamp) + throw new RuntimeException ("KeyEvents enqueued before starting timestamp"); + + enqueuedKeyEvents.add (e); + } + + public void dispatchEvents () + { + int size = enqueuedKeyEvents.size (); + for (int i = 0; i < size; i++) + { + KeyEvent e = (KeyEvent) enqueuedKeyEvents.remove (0); + dispatchKeyEvent (e); + } + } + + public void discardEvents () + { + enqueuedKeyEvents.clear (); + } + } + + /** + * This flag indicates for which focus traversal key release event we + * possibly wait, before letting any more KEY_TYPED events through. + */ + private AWTKeyStroke waitForKeyStroke = null; + + /** The {@link java.util.SortedSet} of current {@link + #EventDelayRequest}s. */ + private SortedSet delayRequests = new TreeSet (); + + public DefaultKeyboardFocusManager () + { + } + + public boolean dispatchEvent (AWTEvent e) + { + if (e instanceof WindowEvent) + { + Window target = (Window) e.getSource (); + + if (e.id == WindowEvent.WINDOW_ACTIVATED) + setGlobalActiveWindow (target); + else if (e.id == WindowEvent.WINDOW_GAINED_FOCUS) + setGlobalFocusedWindow (target); + else if (e.id != WindowEvent.WINDOW_LOST_FOCUS + && e.id != WindowEvent.WINDOW_DEACTIVATED) + return false; + + redispatchEvent(target, e); + return true; + } + else if (e instanceof FocusEvent) + { + Component target = (Component) e.getSource (); + + if (e.id == FocusEvent.FOCUS_GAINED) + { + if (! (target instanceof Window)) + { + if (((FocusEvent) e).isTemporary ()) + setGlobalFocusOwner (target); + else + setGlobalPermanentFocusOwner (target); + } + + // Keep track of this window's focus owner. + + // Find the target Component's top-level ancestor. target + // may be a window. + Container parent = target.getParent (); + + while (parent != null + && !(parent instanceof Window)) + parent = parent.getParent (); + + // If the parent is null and target is not a window, then target is an + // unanchored component and so we don't want to set the focus owner. + if (! (parent == null && ! (target instanceof Window))) + { + Window toplevel = parent == null ? + (Window) target : (Window) parent; + + Component focusOwner = getFocusOwner (); + if (focusOwner != null + && ! (focusOwner instanceof Window)) + toplevel.setFocusOwner (focusOwner); + } + } + else if (e.id == FocusEvent.FOCUS_LOST) + { + if (((FocusEvent) e).isTemporary ()) + setGlobalFocusOwner (null); + else + setGlobalPermanentFocusOwner (null); + } + + redispatchEvent(target, e); + + return true; + } + else if (e instanceof KeyEvent) + { + // Loop through all registered KeyEventDispatchers, giving + // each a chance to handle this event. + Iterator i = getKeyEventDispatchers().iterator(); + + while (i.hasNext ()) + { + KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next (); + if (dispatcher.dispatchKeyEvent ((KeyEvent) e)) + return true; + } + + // processKeyEvent checks if this event represents a focus + // traversal key stroke. + Component focusOwner = getGlobalPermanentFocusOwner (); + + if (focusOwner != null) + processKeyEvent (focusOwner, (KeyEvent) e); + + if (e.isConsumed ()) + return true; + + if (enqueueKeyEvent ((KeyEvent) e)) + // This event was enqueued for dispatch at a later time. + return true; + else + // This event wasn't handled by any of the registered + // KeyEventDispatchers, and wasn't enqueued for dispatch + // later, so send it to the default dispatcher. + return dispatchKeyEvent ((KeyEvent) e); + } + + return false; + } + + private boolean enqueueKeyEvent (KeyEvent e) + { + Iterator i = delayRequests.iterator (); + boolean oneEnqueued = false; + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + if (e.getWhen () > request.timestamp) + { + request.enqueueEvent (e); + oneEnqueued = true; + } + } + return oneEnqueued; + } + + public boolean dispatchKeyEvent (KeyEvent e) + { + Component focusOwner = getGlobalPermanentFocusOwner (); + + if (focusOwner != null) + redispatchEvent(focusOwner, e); + + // Loop through all registered KeyEventPostProcessors, giving + // each a chance to process this event. + Iterator i = getKeyEventPostProcessors().iterator(); + + while (i.hasNext ()) + { + KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next (); + if (processor.postProcessKeyEvent ((KeyEvent) e)) + return true; + } + + // The event hasn't been consumed yet. Check if it is an + // MenuShortcut. + if (postProcessKeyEvent (e)) + return true; + + // Always return true. + return true; + } + + public boolean postProcessKeyEvent (KeyEvent e) + { + // Check if this event represents a menu shortcut. + + // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED. + int modifiers = e.getModifiersEx (); + if (e.getID() == KeyEvent.KEY_PRESSED + && (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0) + { + Window focusedWindow = getGlobalFocusedWindow (); + if (focusedWindow instanceof Frame) + { + MenuBar menubar = ((Frame) focusedWindow).getMenuBar (); + + if (menubar != null) + { + // If there's a menubar, loop through all menu items, + // checking whether each one has a shortcut, and if + // so, whether this key event should activate it. + int numMenus = menubar.getMenuCount (); + + for (int i = 0; i < numMenus; i++) + { + Menu menu = menubar.getMenu (i); + int numItems = menu.getItemCount (); + + for (int j = 0; j < numItems; j++) + { + MenuItem item = menu.getItem (j); + MenuShortcut shortcut = item.getShortcut (); + + if (item.isEnabled() && shortcut != null) + { + // Dispatch a new ActionEvent if: + // + // a) this is a Shift- KeyEvent, and the + // shortcut requires the Shift modifier + // + // or, b) this is not a Shift- KeyEvent, and the + // shortcut does not require the Shift + // modifier. + if (shortcut.getKey () == e.getKeyCode () + && ((shortcut.usesShiftModifier () + && (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0) + || (! shortcut.usesShiftModifier () + && (modifiers & KeyEvent.SHIFT_DOWN_MASK) == 0))) + { + item.dispatchEvent (new ActionEvent (item, + ActionEvent.ACTION_PERFORMED, + item.getActionCommand (), + modifiers)); + // The event was dispatched. + return true; + } + } + } + } + } + } + } + return false; + } + + public void processKeyEvent (Component comp, KeyEvent e) + { + AWTKeyStroke eventKeystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e); + // For every focus traversal keystroke, we need to also consume + // the other two key event types for the same key (e.g. if + // KEY_PRESSED TAB is a focus traversal keystroke, we also need to + // consume KEY_RELEASED and KEY_TYPED TAB key events). + // consuming KEY_RELEASED is easy, because their keyCodes matches + // the KEY_PRESSED event. Consuming the intermediate KEY_TYPED is + // very difficult because their is no clean way that we can know + // which KEY_TYPED belongs to a focusTraversalKey and which not. + // To address this problem we swallow every KEY_TYPE between the + // KEY_PRESSED event that matches a focusTraversalKey and the + // corresponding KEY_RELEASED. + AWTKeyStroke oppositeKeystroke = AWTKeyStroke.getAWTKeyStroke (e.getKeyCode (), + e.getModifiersEx (), + !(e.id == KeyEvent.KEY_RELEASED)); + + // Here we check if we are currently waiting for a KEY_RELEASED and + // swallow all KeyEvents that are to be delivered in between. This + // should only be the KEY_TYPED events that correspond to the + // focusTraversalKey's KEY_PRESSED event + if (waitForKeyStroke != null) + { + if (eventKeystroke.equals(waitForKeyStroke)) + // release this lock + waitForKeyStroke = null; + + // as long as we are waiting for the KEY_RELEASED, we swallow every + // KeyEvent, including the KEY_RELEASED + e.consume(); + return; + } + + Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + Set downKeystrokes = null; + if (comp instanceof Container) + downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + + if (forwardKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + focusNextComponent (comp); + e.consume (); + } + else if (backwardKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + focusPreviousComponent (comp); + e.consume (); + } + else if (upKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + upFocusCycle (comp); + e.consume (); + } + else if (comp instanceof Container + && downKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + downFocusCycle ((Container) comp); + e.consume (); + } + } + + protected void enqueueKeyEvents (long after, Component untilFocused) + { + delayRequests.add (new EventDelayRequest (after, untilFocused)); + } + + protected void dequeueKeyEvents (long after, Component untilFocused) + { + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + // Remove the KeyEvent with the oldest timestamp, which should be + // the first element in the SortedSet. + if (after < 0) + { + int size = delayRequests.size (); + if (size > 0) + delayRequests.remove (delayRequests.first ()); + } + else + { + EventDelayRequest template = new EventDelayRequest (after, untilFocused); + if (delayRequests.contains (template)) + { + EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first (); + delayRequests.remove (actual); + actual.dispatchEvents (); + } + } + } + + protected void discardKeyEvents (Component comp) + { + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + Iterator i = delayRequests.iterator (); + + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + + if (request.focusedComp == comp + || (comp instanceof Container + && ((Container) comp).isAncestorOf (request.focusedComp))) + request.discardEvents (); + } + } + + public void focusPreviousComponent (Component comp) + { + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + + Component previous = policy.getComponentBefore (focusCycleRoot, focusComp); + if (previous != null) + previous.requestFocusInWindow (); + } + + public void focusNextComponent (Component comp) + { + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + + Component next = policy.getComponentAfter (focusCycleRoot, focusComp); + if (next != null) + next.requestFocusInWindow (); + } + + public void upFocusCycle (Component comp) + { + Component focusComp = (comp == null) ? getGlobalFocusOwner () : comp; + Container focusCycleRoot = focusComp.getFocusCycleRootAncestor (); + + if (focusCycleRoot instanceof Window) + { + FocusTraversalPolicy policy = focusCycleRoot.getFocusTraversalPolicy (); + Component defaultComponent = policy.getDefaultComponent (focusCycleRoot); + if (defaultComponent != null) + defaultComponent.requestFocusInWindow (); + } + else + { + Container parentFocusCycleRoot = focusCycleRoot.getFocusCycleRootAncestor (); + + focusCycleRoot.requestFocusInWindow (); + setGlobalCurrentFocusCycleRoot (parentFocusCycleRoot); + } + } + + public void downFocusCycle (Container cont) + { + if (cont == null) + return; + + if (cont.isFocusCycleRoot (cont)) + { + FocusTraversalPolicy policy = cont.getFocusTraversalPolicy (); + Component defaultComponent = policy.getDefaultComponent (cont); + if (defaultComponent != null) + defaultComponent.requestFocusInWindow (); + setGlobalCurrentFocusCycleRoot (cont); + } + } +} // class DefaultKeyboardFocusManager diff --git a/libjava/classpath/java/awt/Dialog.java b/libjava/classpath/java/awt/Dialog.java new file mode 100644 index 00000000000..d3eb975a86d --- /dev/null +++ b/libjava/classpath/java/awt/Dialog.java @@ -0,0 +1,553 @@ +/* Dialog.java -- An AWT dialog box + Copyright (C) 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.DialogPeer; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * A dialog box widget class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class Dialog extends Window +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = 5920926903803293709L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial Indicates whether or not this dialog box is modal. + */ +private boolean modal; + +/** + * @serial Indicates whether or not this dialog box is resizable. + */ +private boolean resizable = true; + +/** + * @serial The title string for this dialog box, which can be + * <code>null</code>. + */ +private String title; + +/** + * This field indicates whether the dialog is undecorated or not. + */ +private boolean undecorated = false; + +/** + * Indicates that we are blocked for modality in show + */ +private boolean blocked = false; + +/** + * Secondary EventQueue to handle AWT events while + * we are blocked for modality in show + */ +private EventQueue eq2 = null; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Dialog</code> with the specified + * parent, that is resizable and not modal, and which has no title. + * + * @param parent The parent frame of this dialog box. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exception is always + * thrown when GraphicsEnvironment.isHeadless() returns true. + */ +public +Dialog(Frame parent) +{ + this(parent, "", false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Dialog</code> with the specified + * parent and modality, that is resizable and which has no title. + * + * @param parent The parent frame of this dialog box. + * @param modal <code>true</code> if this dialog box is modal, + * <code>false</code> otherwise. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exception is always + * thrown when GraphicsEnvironment.isHeadless() returns true. + */ +public +Dialog(Frame parent, boolean modal) +{ + this(parent, "", modal); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Dialog</code> with the specified + * parent, that is resizable and not modal, and which has the specified + * title. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exception is always + * thrown when GraphicsEnvironment.isHeadless() returns true. + */ +public +Dialog(Frame parent, String title) +{ + this(parent, title, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent, title, and modality, that is resizable. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal <code>true</code> if this dialog box is modal, + * <code>false</code> otherwise. + * + * @exception IllegalArgumentException If owner is null or + * GraphicsEnvironment.isHeadless() returns true. + */ +public +Dialog(Frame parent, String title, boolean modal) +{ + this (parent, title, modal, parent.getGraphicsConfiguration ()); +} + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent, title, modality and <code>GraphicsConfiguration</code>, + * that is resizable. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal <code>true</code> if this dialog box is modal, + * <code>false</code> otherwise. + * @param gc The <code>GraphicsConfiguration</code> object to use. + * + * @exception IllegalArgumentException If owner is null, the + * GraphicsConfiguration is not a screen device or + * GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.4 + */ +public +Dialog (Frame parent, String title, boolean modal, GraphicsConfiguration gc) +{ + super (parent, gc); + + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + this.modal = modal; + visible = false; + + setLayout(new BorderLayout()); +} + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent, that is resizable. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.2 + */ +public +Dialog (Dialog owner) +{ + this (owner, "", false, owner.getGraphicsConfiguration ()); +} + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent and title, that is resizable. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.2 + */ +public +Dialog (Dialog owner, String title) +{ + this (owner, title, false, owner.getGraphicsConfiguration ()); +} + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent, title and modality, that is resizable. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.2 + */ +public +Dialog (Dialog owner, String title, boolean modal) +{ + this (owner, title, modal, owner.getGraphicsConfiguration ()); +} + +/** + * Initializes a new instance of <code>Dialog</code> with the specified, + * parent, title, modality and <code>GraphicsConfiguration</code>, + * that is resizable. + * + * @exception IllegalArgumentException If parent is null, the + * GraphicsConfiguration is not a screen device or + * GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.4 + */ +public +Dialog (Dialog parent, String title, boolean modal, GraphicsConfiguration gc) +{ + super (parent, parent.getGraphicsConfiguration ()); + + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + this.modal = modal; + visible = false; + + setLayout (new BorderLayout ()); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the title of this dialog box. + * + * @return The title of this dialog box. + */ +public String +getTitle() +{ + return(title); +} + +/*************************************************************************/ + +/** + * Sets the title of this dialog box to the specified string. + * + * @param title The new title. + */ +public synchronized void +setTitle(String title) +{ + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + + if (peer != null) + { + DialogPeer d = (DialogPeer) peer; + d.setTitle (title); + } +} + +/*************************************************************************/ + +/** + * Tests whether or not this dialog box is modal. + * + * @return <code>true</code> if this dialog box is modal, + * <code>false</code> otherwise. + */ +public boolean +isModal() +{ + return(modal); +} + +/*************************************************************************/ + +/** + * Changes the modality of this dialog box. This can only be done before + * the peer is created. + * + * @param modal <code>true</code> to make this dialog box modal, + * <code>false</code> to make it non-modal. + */ +public void +setModal(boolean modal) +{ + this.modal = modal; +} + +/*************************************************************************/ + +/** + * Tests whether or not this dialog box is resizable. + * + * @return <code>true</code> if this dialog is resizable, <code>false</code>, + * otherwise. + */ +public boolean +isResizable() +{ + return(resizable); +} + +/*************************************************************************/ + +/** + * Changes the resizability of this dialog box. + * + * @param resizable <code>true</code> to make this dialog resizable, + * <code>false</code> to make it non-resizable. + */ +public synchronized void +setResizable(boolean resizable) +{ + this.resizable = resizable; + if (peer != null) + { + DialogPeer d = (DialogPeer) peer; + d.setResizable (resizable); + } +} + +/*************************************************************************/ + +/** + * Creates this object's native peer. + */ +public synchronized void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createDialog (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Makes this dialog visible and brings it to the front. + * If the dialog is modal and is not already visible, this call will not + * return until the dialog is hidden by someone calling hide or dispose. + * If this is the event dispatching thread we must ensure that another event + * thread runs while the one which invoked this method is blocked. + */ +public synchronized void +show() +{ + super.show(); + + if (isModal()) + { + // If already shown (and blocked) just return + if (blocked) + return; + + /* If show is called in the dispatch thread for a modal dialog it will + block so we must run another thread so the events keep being + dispatched.*/ + if (EventQueue.isDispatchThread ()) + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + eq2 = new EventQueue (); + eq.push (eq2); + } + + try + { + blocked = true; + wait (); + blocked = false; + } + catch (InterruptedException e) + { + blocked = false; + } + + if (eq2 != null) + { + eq2.pop (); + eq2 = null; + } + } +} + +/*************************************************************************/ + +/** + * Hides the Dialog and then + * causes show() to return if it is currently blocked. + */ + +public synchronized void +hide () +{ + if (blocked) + { + notifyAll (); + } + + super.hide(); +} + +/*************************************************************************/ + +/** + * Disposes the Dialog and then causes show() to return + * if it is currently blocked. + */ + +public synchronized void +dispose () +{ + if (blocked) + { + notifyAll (); + } + + super.dispose(); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this component. + * + * @return A debugging string for this component. + */ +protected String +paramString() +{ + return ("title+" + title + ",modal=" + modal + + ",resizable=" + resizable + "," + super.paramString()); +} + + /** + * Returns whether this frame is undecorated or not. + * + * @since 1.4 + */ + public boolean isUndecorated () + { + return undecorated; + } + + /** + * Disables or enables decorations for this frame. This method can only be + * called while the frame is not displayable. + * + * @exception IllegalComponentStateException If this frame is displayable. + * + * @since 1.4 + */ + public void setUndecorated (boolean undecorated) + { + if (isDisplayable ()) + throw new IllegalComponentStateException (); + + this.undecorated = undecorated; + } + + protected class AccessibleAWTDialog extends AccessibleAWTWindow + { + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.DIALOG; + } + + public AccessibleStateSet getAccessibleState() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isResizable()) + states.add(AccessibleState.RESIZABLE); + if (isModal()) + states.add(AccessibleState.MODAL); + return states; + } + } + + /** + * Gets the AccessibleContext associated with this <code>Dialog</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTDialog(); + return accessibleContext; + } + +} // class Dialog + diff --git a/libjava/classpath/java/awt/Dimension.java b/libjava/classpath/java/awt/Dimension.java new file mode 100644 index 00000000000..4c1a07bf920 --- /dev/null +++ b/libjava/classpath/java/awt/Dimension.java @@ -0,0 +1,234 @@ +/* Dimension.java -- represents a 2-dimensional span + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Dimension2D; +import java.io.Serializable; + +/** + * This class holds a width and height value pair. This is used in plenty + * of windowing classes, but also has geometric meaning. + * + * <p>It is valid for a dimension to have negative width or height; but it + * is considered to have no area. Therefore, the behavior in various methods + * is undefined in such a case. + * + * <p>There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get invalid results. Also, this + * class is not threadsafe. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Component + * @see LayoutManager + * @since 1.0 + * @status updated to 1.14 + */ +public class Dimension extends Dimension2D implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4723952579491349524L; + + /** + * The width of this object. + * + * @see #getSize() + * @see #setSize(double, double) + * @serial the width + */ + public int width; + + /** + * The height of this object. + * + * @see #getSize() + * @see #setSize(double, double) + * @serial the height + */ + public int height; + + /** + * Create a new Dimension with a width and height of zero. + */ + public Dimension() + { + } + + /** + * Create a new Dimension with width and height identical to that of the + * specified dimension. + * + * @param d the Dimension to copy + * @throws NullPointerException if d is null + */ + public Dimension(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Create a new Dimension with the specified width and height. + * + * @param w the width of this object + * @param h the height of this object + */ + public Dimension(int w, int h) + { + width = w; + height = h; + } + + /** + * Gets the width of this dimension. + * + * @return the width, as a double + */ + public double getWidth() + { + return width; + } + + /** + * Gets the height of this dimension. + * + * @return the height, as a double + */ + public double getHeight() + { + return height; + } + + /** + * Sets the size of this dimension. The values are rounded to int. + * + * @param w the new width + * @param h the new height + * @since 1.2 + */ + public void setSize(double w, double h) + { + width = (int) w; + height = (int) h; + } + + /** + * Returns the size of this dimension. A pretty useless method, as this is + * already a dimension. + * + * @return a copy of this dimension + * @see #setSize(Dimension) + * @since 1.1 + */ + public Dimension getSize() + { + return new Dimension(width, height); + } + + /** + * Sets the width and height of this object to match that of the + * specified object. + * + * @param d the Dimension to get the new width and height from + * @throws NullPointerException if d is null + * @see #getSize() + * @since 1.1 + */ + public void setSize(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Sets the width and height of this object to the specified values. + * + * @param w the new width value + * @param h the new height value + */ + public void setSize(int w, int h) + { + width = w; + height = h; + } + + /** + * Tests this object for equality against the specified object. This will + * be true if and only if the specified object is an instance of + * Dimension2D, and has the same width and height. + * + * @param obj the object to test against + * @return true if the object is equal to this + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Dimension)) + return false; + Dimension dim = (Dimension) obj; + return height == dim.height && width == dim.width; + } + + /** + * Return the hashcode for this object. It is not documented, but appears + * to be <code>((width + height) * (width + height + 1) / 2) + width</code>. + * + * @return the hashcode + */ + public int hashCode() + { + // Reverse engineering this was fun! + return (width + height) * (width + height + 1) / 2 + width; + } + + /** + * Returns a string representation of this object. The format is: + * <code>getClass().getName() + "[width=" + width + ",height=" + height + * + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + + "[width=" + width + ",height=" + height + ']'; + } +} // class Dimension diff --git a/libjava/classpath/java/awt/DisplayMode.java b/libjava/classpath/java/awt/DisplayMode.java new file mode 100644 index 00000000000..d41d4a87f57 --- /dev/null +++ b/libjava/classpath/java/awt/DisplayMode.java @@ -0,0 +1,164 @@ +/* DisplayMode.java -- a description of display mode configurations + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This encapsulates information about the display mode for a graphics + * device configuration. They are device dependent, and may not always be + * available. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsDevice + * @since 1.4 + * @status updated to 1.4 + */ +public final class DisplayMode +{ + /** + * Value of the bit depth if multiple depths are supported. + * + * @see #getBitDepth() + */ + public static final int BIT_DEPTH_MULTI = -1; + + /** + * Value of an unknown refresh rate. + * + * @see #getRefreshRate() + */ + public static final int REFRESH_RATE_UNKNOWN = 0; + + /** The width. */ + private final int width; + + /** The height. */ + private final int height; + + /** The bit depth. */ + private final int bitDepth; + + /** The refresh rate. */ + private final int refreshRate; + + /** + * Create a mode with the given parameters. + * + * @param width the width + * @param height the height + * @param bitDepth the bitDepth + * @param refreshRate the refreshRate + * @see #BIT_DEPTH_MULTI + * @see #REFRESH_RATE_UNKNOWN + */ + public DisplayMode(int width, int height, int bitDepth, int refreshRate) + { + this.width = width; + this.height = height; + this.bitDepth = bitDepth; + this.refreshRate = refreshRate; + } + + /** + * Returns the height, in pixels. + * + * @return the height + */ + public int getHeight() + { + return height; + } + + /** + * Returns the width, in pixels. + * + * @return the width + */ + public int getWidth() + { + return width; + } + + /** + * Returns the bit depth, in bits per pixel. This may be BIT_DEPTH_MULTI. + * + * @return the bit depth + * @see #BIT_DEPTH_MULTI + */ + public int getBitDepth() + { + return bitDepth; + } + + /** + * Returns the refresh rate, in hertz. This may be REFRESH_RATE_UNKNOWN. + * + * @return the refresh rate + * @see #REFRESH_RATE_UNKNOWN + */ + public int getRefreshRate() + { + return refreshRate; + } + + /** + * Test for equality. This returns true for two modes with identical + * parameters. + * + * @param dm The display mode to compare to + * + * @return true if it is equal + */ + public boolean equals (DisplayMode dm) + { + return (width == dm.width + && height == dm.height + && bitDepth == dm.bitDepth + && refreshRate == dm.refreshRate); + } + + /** + * Returns a hash code for the display mode. + * + * @return the hash code + */ + public int hashCode() + { + return width + height + bitDepth + refreshRate; + } +} // class DisplayMode diff --git a/libjava/classpath/java/awt/Event.java b/libjava/classpath/java/awt/Event.java new file mode 100644 index 00000000000..648139cc850 --- /dev/null +++ b/libjava/classpath/java/awt/Event.java @@ -0,0 +1,185 @@ +/* Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +public class Event implements java.io.Serializable +{ + static final long serialVersionUID = 5488922509400504703L; + + public static final int SHIFT_MASK = 1; + public static final int CTRL_MASK = 2; + public static final int META_MASK = 4; + public static final int ALT_MASK = 8; + + public static final int ACTION_EVENT = 1001; + public static final int BACK_SPACE = 8; + public static final int CAPS_LOCK = 1022; + public static final int DELETE = 127; + public static final int DOWN = 1005; + public static final int END = 1001; + public static final int ENTER = 10; + public static final int ESCAPE = 27; + public static final int F1 = 1008; + public static final int F10 = 1017; + public static final int F11 = 1018; + public static final int F12 = 1019; + public static final int F2 = 1009; + public static final int F3 = 1010; + public static final int F4 = 1011; + public static final int F5 = 1012; + public static final int F6 = 1013; + public static final int F7 = 1014; + public static final int F8 = 1015; + public static final int F9 = 1016; + public static final int GOT_FOCUS = 1004; + public static final int HOME = 1000; + public static final int INSERT = 1025; + public static final int KEY_ACTION = 403; + public static final int KEY_ACTION_RELEASE = 404; + public static final int KEY_PRESS = 401; + public static final int KEY_RELEASE = 402; + public static final int LEFT = 1006; + public static final int LIST_DESELECT = 702; + public static final int LIST_SELECT = 701; + public static final int LOAD_FILE = 1002; + public static final int LOST_FOCUS = 1005; + public static final int MOUSE_DOWN = 501; + public static final int MOUSE_DRAG = 506; + public static final int MOUSE_ENTER = 504; + public static final int MOUSE_EXIT = 505; + public static final int MOUSE_MOVE = 503; + public static final int MOUSE_UP = 502; + public static final int NUM_LOCK = 1023; + public static final int PAUSE = 1024; + public static final int PGDN = 1003; + public static final int PGUP = 1002; + public static final int PRINT_SCREEN = 1020; + public static final int RIGHT = 1007; + public static final int SAVE_FILE = 1003; + public static final int SCROLL_ABSOLUTE = 605; + public static final int SCROLL_BEGIN = 606; + public static final int SCROLL_END = 607; + public static final int SCROLL_LINE_DOWN = 602; + public static final int SCROLL_LINE_UP = 601; + public static final int SCROLL_LOCK = 1021; + public static final int SCROLL_PAGE_DOWN = 604; + public static final int SCROLL_PAGE_UP = 603; + public static final int TAB = 9; + public static final int UP = 1004; + public static final int WINDOW_DEICONIFY = 204; + public static final int WINDOW_DESTROY = 201; + public static final int WINDOW_EXPOSE = 202; + public static final int WINDOW_ICONIFY = 203; + public static final int WINDOW_MOVED = 205; + + public Object arg; + public int clickCount; + boolean consumed; // Required by serialization spec. + public Event evt; + public int id; + public int key; + public int modifiers; + public Object target; + public long when; + public int x; + public int y; + + public Event (Object target, int id, Object arg) + { + this.id = id; + this.target = target; + this.arg = arg; + } + + public Event (Object target, long when, int id, int x, int y, int key, + int modifiers) + { + this.target = target; + this.when = when; + this.id = id; + this.x = x; + this.y = y; + this.key = key; + this.modifiers = modifiers; + } + + public Event (Object target, long when, int id, int x, int y, int key, + int modifiers, Object arg) + { + this (target, when, id, x, y, key, modifiers); + this.arg = arg; + } + + public boolean controlDown () + { + return ((modifiers & CTRL_MASK) == 0 ? false : true); + } + + public boolean metaDown () + { + return ((modifiers & META_MASK) == 0 ? false : true); + } + + protected String paramString () + { + return "id=" + id + ",x=" + x + ",y=" + y + + ",target=" + target + ",arg=" + arg; + } + + public boolean shiftDown() + { + return ((modifiers & SHIFT_MASK) == 0 ? false : true); + } + + public String toString() + { + return getClass().getName() + "[" + paramString() + "]"; + } + + public void translate (int x, int y) + { + this.x += x; + this.y += y; + } +} diff --git a/libjava/classpath/java/awt/EventDispatchThread.java b/libjava/classpath/java/awt/EventDispatchThread.java new file mode 100644 index 00000000000..a64cdae8567 --- /dev/null +++ b/libjava/classpath/java/awt/EventDispatchThread.java @@ -0,0 +1,94 @@ +/* EventDispatchThread.java - + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * @author Bryce McKinlay + * @status believed complete, but untested. + */ +class EventDispatchThread extends Thread +{ + private static int dispatchThreadNum; + + private EventQueue queue; + + EventDispatchThread(EventQueue queue) + { + super(); + setName("AWT-EventQueue-" + ++dispatchThreadNum); + this.queue = queue; + setPriority(NORM_PRIORITY + 1); + } + + public void run() + { + while (true) + { + try + { + AWTEvent evt = queue.getNextEvent(); + + KeyboardFocusManager manager; + manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + // Try to dispatch this event to the current keyboard focus + // manager. It will dispatch all FocusEvents, all + // WindowEvents related to focus, and all KeyEvents, + // returning true. Otherwise, it returns false and we + // dispatch the event normally. + if (!manager.dispatchEvent (evt)) + queue.dispatchEvent(evt); + } + catch (ThreadDeath death) + { + // If someone wants to kill us, let them. + return; + } + catch (InterruptedException ie) + { + // We are interrupted when we should finish executing + return; + } + catch (Throwable x) + { + System.err.println("Exception during event dispatch:"); + x.printStackTrace(System.err); + } + } + } +} diff --git a/libjava/classpath/java/awt/EventQueue.java b/libjava/classpath/java/awt/EventQueue.java new file mode 100644 index 00000000000..a8b00781321 --- /dev/null +++ b/libjava/classpath/java/awt/EventQueue.java @@ -0,0 +1,552 @@ +/* EventQueue.java -- + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InvocationEvent; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.EmptyStackException; + +/* Written using on-line Java 2 Platform Standard Edition v1.3 API + * Specification, as well as "The Java Class Libraries", 2nd edition + * (Addison-Wesley, 1998). + * Status: Believed complete, but untested. + */ + +/** + * This class manages a queue of <code>AWTEvent</code> objects that + * are posted to it. The AWT system uses only one event queue for all + * events. + * + * @author Bryce McKinlay + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class EventQueue +{ + private static final int INITIAL_QUEUE_DEPTH = 8; + private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH]; + + private int next_in = 0; // Index where next event will be added to queue + private int next_out = 0; // Index of next event to be removed from queue + + private EventQueue next; + private EventQueue prev; + private AWTEvent currentEvent; + private long lastWhen = System.currentTimeMillis(); + + private EventDispatchThread dispatchThread = new EventDispatchThread(this); + private boolean shutdown = false; + + private long lastNativeQueueAccess = 0; + private long humanLatencyThreshold = 100; + + synchronized void setShutdown (boolean b) + { + shutdown = b; + } + + synchronized boolean isShutdown () + { + if (shutdown) + return true; + + // This is the exact self-shutdown condition specified in J2SE: + // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html + + if (peekEvent() == null + && ((ClasspathToolkit) Toolkit.getDefaultToolkit()).nativeQueueEmpty()) + { + Frame[] frames = Frame.getFrames(); + for (int i = 0; i < frames.length; ++i) + if (frames[i].isDisplayable()) + return false; + return true; + } + return false; + } + + /** + * Initializes a new instance of <code>EventQueue</code>. + */ + public EventQueue() + { + } + + /** + * Returns the next event in the queue. This method will block until + * an event is available or until the thread is interrupted. + * + * @return The next event in the queue. + * + * @exception InterruptedException If this thread is interrupted while + * waiting for an event to be posted to the queue. + */ + public synchronized AWTEvent getNextEvent() + throws InterruptedException + { + if (next != null) + return next.getNextEvent(); + + ClasspathToolkit tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit()); + long curr = System.currentTimeMillis(); + + if (! tk.nativeQueueEmpty() && + (curr - lastNativeQueueAccess > humanLatencyThreshold)) + { + tk.iterateNativeQueue(this, false); + lastNativeQueueAccess = curr; + } + + while (next_in == next_out) + { + // Only the EventDispatchThread associated with the top of the stack is + // allowed to get events from the native source; everyone else just + // waits on the head of the queue. + + if (isDispatchThread()) + { + // We are not allowed to return null from this method, yet it + // is possible that we actually have run out of native events + // in the enclosing while() loop, and none of the native events + // happened to cause AWT events. We therefore ought to check + // the isShutdown() condition here, before risking a "native + // wait". If we check it before entering this function we may + // wait forever for events after the shutdown condition has + // arisen. + + if (isShutdown()) + throw new InterruptedException(); + + tk.iterateNativeQueue(this, true); + lastNativeQueueAccess = System.currentTimeMillis(); + } + else + { + try + { + wait(); + } + catch (InterruptedException ie) + { + } + } + } + + AWTEvent res = queue[next_out]; + + if (++next_out == queue.length) + next_out = 0; + return res; + } + + /** + * Returns the next event in the queue without removing it from the queue. + * This method will block until an event is available or until the thread + * is interrupted. + * + * @return The next event in the queue. + * @specnote Does not block. Returns null if there are no events on the + * queue. + */ + public synchronized AWTEvent peekEvent() + { + if (next != null) + return next.peekEvent(); + + if (next_in != next_out) + return queue[next_out]; + else + return null; + } + + /** + * Returns the next event in the queue that has the specified id + * without removing it from the queue. + * This method will block until an event is available or until the thread + * is interrupted. + * + * @param id The event id to return. + * + * @return The next event in the queue. + * + * @specnote Does not block. Returns null if there are no matching events + * on the queue. + */ + public synchronized AWTEvent peekEvent(int id) + { + if (next != null) + return next.peekEvent(id); + + int i = next_out; + while (i != next_in) + { + AWTEvent qevt = queue[i]; + if (qevt.id == id) + return qevt; + } + return null; + } + + /** + * Posts a new event to the queue. + * + * @param evt The event to post to the queue. + * + * @exception NullPointerException If event is null. + */ + public synchronized void postEvent(AWTEvent evt) + { + if (evt == null) + throw new NullPointerException(); + + if (next != null) + { + next.postEvent(evt); + return; + } + + /* Check for any events already on the queue with the same source + and ID. */ + int i = next_out; + while (i != next_in) + { + AWTEvent qevt = queue[i]; + Object src; + if (qevt.id == evt.id + && (src = qevt.getSource()) == evt.getSource() + && src instanceof Component) + { + /* If there are, call coalesceEvents on the source component + to see if they can be combined. */ + Component srccmp = (Component) src; + AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt); + if (coalesced_evt != null) + { + /* Yes. Replace the existing event with the combined event. */ + queue[i] = coalesced_evt; + return; + } + break; + } + if (++i == queue.length) + i = 0; + } + + queue[next_in] = evt; + if (++next_in == queue.length) + next_in = 0; + + if (next_in == next_out) + { + /* Queue is full. Extend it. */ + AWTEvent[] oldQueue = queue; + queue = new AWTEvent[queue.length * 2]; + + int len = oldQueue.length - next_out; + System.arraycopy(oldQueue, next_out, queue, 0, len); + if (next_out != 0) + System.arraycopy(oldQueue, 0, queue, len, next_out); + + next_out = 0; + next_in = oldQueue.length; + } + + if (dispatchThread == null || !dispatchThread.isAlive()) + { + dispatchThread = new EventDispatchThread(this); + dispatchThread.start(); + } + + // Window events might represent the closing of a window, which + // might cause the end of the dispatch thread's life, so we'll wake + // it up here to give it a chance to check for shutdown. + + if (!isDispatchThread() + || (evt.getID() == WindowEvent.WINDOW_CLOSED) + || (evt.getID() == WindowEvent.WINDOW_CLOSING)) + ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue(); + + notify(); + } + + /** + * Causes runnable to have its run method called in the dispatch thread of the + * EventQueue. This will happen after all pending events are processed. The + * call blocks until this has happened. This method will throw an Error if + * called from the event dispatcher thread. + * + * @exception InterruptedException If another thread has interrupted + * this thread. + * @exception InvocationTargetException If an exception is thrown when running + * runnable. + * + * @since 1.2 + */ + public static void invokeAndWait(Runnable runnable) + throws InterruptedException, InvocationTargetException + { + if (isDispatchThread ()) + throw new Error("Can't call invokeAndWait from event dispatch thread"); + + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + Thread current = Thread.currentThread(); + + InvocationEvent ie = + new InvocationEvent(eq, runnable, current, true); + + synchronized (current) + { + eq.postEvent(ie); + current.wait(); + } + + Exception exception; + + if ((exception = ie.getException()) != null) + throw new InvocationTargetException(exception); + } + + /** + * This arranges for runnable to have its run method called in the + * dispatch thread of the EventQueue. This will happen after all + * pending events are processed. + * + * @since 1.2 + */ + public static void invokeLater(Runnable runnable) + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + + InvocationEvent ie = + new InvocationEvent(eq, runnable, null, false); + + eq.postEvent(ie); + } + + /** + * Return true if the current thread is the current AWT event dispatch + * thread. + */ + public static boolean isDispatchThread() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + + /* Find last EventQueue in chain */ + while (eq.next != null) + eq = eq.next; + + return (Thread.currentThread() == eq.dispatchThread); + } + + /** + * Return the event currently being dispatched by the event + * dispatch thread. If the current thread is not the event + * dispatch thread, this method returns null. + * + * @since 1.4 + */ + public static AWTEvent getCurrentEvent() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + Thread ct = Thread.currentThread(); + + /* Find out if this thread is the dispatch thread for any of the + EventQueues in the chain */ + while (ct != eq.dispatchThread) + { + // Try next EventQueue, if any + if (eq.next == null) + return null; // Not an event dispatch thread + eq = eq.next; + } + + return eq.currentEvent; + } + + /** + * Allows a custom EventQueue implementation to replace this one. + * All pending events are transferred to the new queue. Calls to postEvent, + * getNextEvent, and peekEvent and others are forwarded to the pushed queue + * until it is removed with a pop(). + * + * @exception NullPointerException if newEventQueue is null. + */ + public synchronized void push(EventQueue newEventQueue) + { + if (newEventQueue == null) + throw new NullPointerException (); + + /* Make sure we are at the top of the stack because callers can + only get a reference to the one at the bottom using + Toolkit.getDefaultToolkit().getSystemEventQueue() */ + if (next != null) + { + next.push (newEventQueue); + return; + } + + /* Make sure we have a live dispatch thread to drive the queue */ + if (dispatchThread == null) + dispatchThread = new EventDispatchThread(this); + + int i = next_out; + while (i != next_in) + { + newEventQueue.postEvent(queue[i]); + next_out = i; + if (++i == queue.length) + i = 0; + } + + next = newEventQueue; + newEventQueue.prev = this; + } + + /** Transfer any pending events from this queue back to the parent queue that + * was previously push()ed. Event dispatch from this queue is suspended. + * + * @exception EmptyStackException If no previous push was made on this + * EventQueue. + */ + protected void pop() throws EmptyStackException + { + if (prev == null) + throw new EmptyStackException(); + + /* The order is important here, we must get the prev lock first, + or deadlock could occur as callers usually get here following + prev's next pointer, and thus obtain prev's lock before trying + to get this lock. */ + synchronized (prev) + { + prev.next = next; + if (next != null) + next.prev = prev; + + synchronized (this) + { + int i = next_out; + while (i != next_in) + { + prev.postEvent(queue[i]); + next_out = i; + if (++i == queue.length) + i = 0; + } + // Empty the queue so it can be reused + next_in = 0; + next_out = 0; + + ((ClasspathToolkit) Toolkit.getDefaultToolkit()).wakeNativeQueue(); + setShutdown(true); + dispatchThread = null; + this.notifyAll(); + } + } + } + + /** + * Dispatches an event. The manner in which the event is dispatched depends + * upon the type of the event and the type of the event's source object. + * + * @exception NullPointerException If event is null. + */ + protected void dispatchEvent(AWTEvent evt) + { + currentEvent = evt; + + if (evt instanceof InputEvent) + lastWhen = ((InputEvent) evt).getWhen(); + else if (evt instanceof ActionEvent) + lastWhen = ((ActionEvent) evt).getWhen(); + else if (evt instanceof InvocationEvent) + lastWhen = ((InvocationEvent) evt).getWhen(); + + if (evt instanceof ActiveEvent) + { + ActiveEvent active_evt = (ActiveEvent) evt; + active_evt.dispatch(); + } + else + { + Object source = evt.getSource(); + + if (source instanceof Component) + { + Component srccmp = (Component) source; + srccmp.dispatchEvent(evt); + } + else if (source instanceof MenuComponent) + { + MenuComponent srccmp = (MenuComponent) source; + srccmp.dispatchEvent(evt); + } + } + } + + /** + * Returns the timestamp of the most recent event that had a timestamp, or + * the initialization time of the event queue if no events have been fired. + * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s, + * <code>InputMethodEvent</code>s, and <code>InvocationEvent</code>s have + * timestamps, but this may be added to other events in future versions. + * If this is called by the event dispatching thread, it can be any + * (sequential) value, but to other threads, the safest bet is to return + * System.currentTimeMillis(). + * + * @return the most recent timestamp + * @see InputEvent#getWhen() + * @see ActionEvent#getWhen() + * @see InvocationEvent#getWhen() + * @see InputMethodEvent#getWhen() + * @since 1.4 + */ + public static long getMostRecentEventTime() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + if (Thread.currentThread() != eq.dispatchThread) + return System.currentTimeMillis(); + return eq.lastWhen; + } +} diff --git a/libjava/classpath/java/awt/FileDialog.java b/libjava/classpath/java/awt/FileDialog.java new file mode 100644 index 00000000000..7f2723e7e9a --- /dev/null +++ b/libjava/classpath/java/awt/FileDialog.java @@ -0,0 +1,318 @@ +/* FileDialog.java -- A filename selection dialog box + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.FileDialogPeer; +import java.io.FilenameFilter; +import java.io.Serializable; + +/** + * This class implements a file selection dialog box widget. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class FileDialog extends Dialog implements Serializable +{ + +/* + * Static Variables + */ + +/** + * Indicates that the purpose of the dialog is for opening a file. + */ +public static final int LOAD = 0; + +/** + * Indicates that the purpose of the dialog is for saving a file. + */ +public static final int SAVE = 1; + +// Serialization constant +private static final long serialVersionUID = 5035145889651310422L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The directory for this file dialog. + */ +private String dir; + +/** + * @serial The filename for this file dialog + */ +private String file; + +/** + * @serial The filter for selecting filenames to display + */ +private FilenameFilter filter; + +/** + * @serial The mode of this dialog, either <code>LOAD</code> or + * <code>SAVE</code>. + */ +private int mode; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>FileDialog</code> with the + * specified parent. This dialog will have no title and will be for + * loading a file. + * + * @param parent The parent frame for this dialog. + */ +public +FileDialog(Frame parent) +{ + this(parent, "", LOAD); +} + +/*************************************************************************/ + +/** + * Initialized a new instance of <code>FileDialog</code> with the + * specified parent and title. This dialog will be for opening a file. + * + * @param parent The parent frame for this dialog. + * @param title The title for this dialog. + */ +public +FileDialog(Frame parent, String title) +{ + this(parent, title, LOAD); +} + +/*************************************************************************/ + +/** + * Initialized a new instance of <code>FileDialog</code> with the + * specified parent, title, and mode. + * + * @param parent The parent frame for this dialog. + * @param title The title for this dialog. + * @param mode The mode of the dialog, either <code>LOAD</code> or + * <code>SAVE</code>. + * + * @exception IllegalArgumentException If an illegal file dialog mode + * is supplied. + */ +public +FileDialog(Frame parent, String title, int mode) +{ + super(parent, title, true); + + if ((mode != LOAD) && (mode != SAVE)) + throw new IllegalArgumentException ( + "Mode argument must be either LOAD or SAVE"); + + setMode (mode); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the mode of this dialog, either <code>LOAD</code> or + * <code>SAVE</code>. + * + * @return The mode of this dialog. + */ +public int +getMode() +{ + return(mode); +} + +/*************************************************************************/ + +/** + * Sets the mode of this dialog to either <code>LOAD</code> or + * <code>SAVE</code>. This method is only effective before the native + * peer is created. + * + * @param mode The new mode of this file dialog. + * + * @exception IllegalArgumentException If an illegal file dialog mode + * is supplied. + */ +public void +setMode(int mode) +{ + if ((mode != LOAD) && (mode != SAVE)) + throw new IllegalArgumentException("Bad mode: " + mode); + + this.mode = mode; +} + +/*************************************************************************/ + +/** + * Returns the directory for this file dialog. + * + * @return The directory for this file dialog. + */ +public String +getDirectory() +{ + return(dir); +} + +/*************************************************************************/ + +/** + * Sets the directory for this file dialog. + * + * @param dir The new directory for this file dialog. + */ +public synchronized void +setDirectory(String dir) +{ + this.dir = dir; + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setDirectory (dir); + } +} + +/*************************************************************************/ + +/** + * Returns the file that is selected in this dialog. + * + * @return The file that is selected in this dialog. + */ +public String +getFile() +{ + return(file); +} + +/*************************************************************************/ + +/** + * Sets the selected file for this dialog. + * + * @param file The selected file for this dialog. + */ +public synchronized void +setFile(String file) +{ + this.file = file; + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setFile (file); + } +} + +/*************************************************************************/ + +/** + * Returns the filename filter being used by this dialog. + * + * @return The filename filter being used by this dialog. + */ +public FilenameFilter +getFilenameFilter() +{ + return(filter); +} + +/*************************************************************************/ + +/** + * Sets the filename filter used by this dialog. + * + * @param filter The new filename filter for this file dialog box. + */ +public synchronized void +setFilenameFilter(FilenameFilter filter) +{ + this.filter = filter; + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setFilenameFilter (filter); + } +} + +/*************************************************************************/ + +/** + * Creates the native peer for this file dialog box. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createFileDialog (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return ("dir=" + dir + ",file=" + file + + ",mode=" + mode + "," + super.paramString()); +} + +} // class FileDialog + diff --git a/libjava/classpath/java/awt/FlowLayout.java b/libjava/classpath/java/awt/FlowLayout.java new file mode 100644 index 00000000000..4674990ac52 --- /dev/null +++ b/libjava/classpath/java/awt/FlowLayout.java @@ -0,0 +1,364 @@ +/* FlowLayout.java -- Grid-based layout engine + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** This class implements a flow-based layout. Components are laid + * out in order from left to right. When a component cannot be placed + * without horizontal clipping, a new row is started. This class + * supports horizontal and vertical gaps. These are used for spacing + * between components. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FlowLayout implements LayoutManager, Serializable +{ + /** Constant that specifies left alignment. */ + public static final int LEFT = 0; + /** Constant that specifies center alignment. */ + public static final int CENTER = 1; + /** Constant that specifies right alignment. */ + public static final int RIGHT = 2; + + /** Constant that specifies alignment to leading edge of container's + * orientation. */ + public static final int LEADING = 3; + /** Constant that specifies alignment to trailing edge of container's + * orientation. */ + public static final int TRAILING = 4; + + // Serialization constant + private static final long serialVersionUID = -7262534875583282631L; + + /** + * Add a new component to the layout. This particular implementation + * does nothing. + * + * @param name the name + * @param comp the component + */ + public void addLayoutComponent (String name, Component comp) + { + // Nothing. + } + + /** + * Returns the current justification value for this object. + * + * @return The current justification value for this object. + */ + public int getAlignment () + { + return align; + } + + /** + * Returns the horizontal gap between components. + * + * @return The horizontal gap between components. + */ + public int getHgap () + { + return hgap; + } + + /** + * Returns the vertical gap between lines of components. + * + * @return The vertical gap between lines of components. + */ + public int getVgap () + { + return vgap; + } + + /** + * Initializes a new instance of <code>FlowLayout</code> with a center + * justification and a default horizontal and vertical gap of 5. + */ + public FlowLayout () + { + this (CENTER, 5, 5); + } + + /** + * Initializes a new instance of <code>FlowLayout</code> with the specified + * justification and a default horizontal and vertical gap of 5. + * + * @param align The justification setting, which should be one of the + * contants in this class. + */ + public FlowLayout (int align) + { + this (align, 5, 5); + } + + /** + * Initializes a new instance of <code>FlowLayout</code> with the specified + * justification and gap values + * @param align Alignment + * @param hgap The horizontal gap + * @param vgap The vertical gap + * @exception IllegalArgumentException If either gap is negative + */ + public FlowLayout (int align, int hgap, int vgap) + { + // Use methods to set fields so that we can have all the checking + // in one place. + setVgap (vgap); + setHgap (hgap); + setAlignment (align); + } + + /** Lay out the container's components based on current settings. + * @param parent The parent container + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int num = parent.getComponentCount (); + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + Dimension d = parent.getSize (); + Insets ins = parent.getInsets (); + + ComponentOrientation orient = parent.getComponentOrientation (); + boolean left_to_right = orient.isLeftToRight (); + + int y = ins.top + vgap; + int i = 0; + while (i < num) + { + // Find the components which go in the current row. + int new_w = ins.left + hgap + ins.right; + int new_h = 0; + int j; + boolean found_one = false; + for (j = i; j < num; ++j) + { + // Skip invisible items. + if (! comps[j].visible) + continue; + + Dimension c = comps[j].getPreferredSize (); + + int next_w = new_w + hgap + c.width; + if (next_w <= d.width || ! found_one) + { + new_w = next_w; + new_h = Math.max (new_h, c.height); + found_one = true; + } + else + { + // Must start a new row, and we already found an item + break; + } + } + + // Set the location of each component for this row. + int x; + + int myalign = align; + if (align == LEADING) + myalign = left_to_right ? LEFT : RIGHT; + else if (align == TRAILING) + myalign = left_to_right ? RIGHT : LEFT; + + if (myalign == LEFT) + x = ins.left + hgap; + else if (myalign == CENTER) + x = ins.left + (d.width - new_w) / 2 + hgap; + else + x = ins.left + (d.width - new_w) + hgap; + + for (int k = i; k < j; ++k) + { + if (comps[k].visible) + { + Dimension c = comps[k].getPreferredSize (); + comps[k].setBounds (x, y + (new_h - c.height) / 2, + c.width, c.height); + x += c.width + hgap; + } + } + + // Advance to next row. + i = j; + y += new_h + vgap; + } + } + } + + /** + * Returns the minimum layout size for the specified container using + * this layout. + * @param cont The parent container + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize (Container cont) + { + return getSize (cont, true); + } + + /** + * Returns the preferred layout size for the specified container using + * this layout. + * @param cont The parent container + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize (Container cont) + { + return getSize (cont, false); + } + + /** Remove the indicated component from this layout manager. + * This particular implementation does nothing. + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + // Nothing. + } + + /** + * Sets the justification value for this object to the specified value. + * + * @param align The new justification value for this object, which must + * be one of the constants in this class. + */ + public void setAlignment (int align) + { + if (align != LEFT && align != RIGHT && align != CENTER + && align != LEADING && align != TRAILING) + throw new IllegalArgumentException ("invalid alignment: " + align); + this.align = align; + } + + /** + * Sets the horizontal gap between components to the specified value. + * + * @param hgap The new horizontal gap between components. + */ + public void setHgap (int hgap) + { + if (hgap < 0) + throw new IllegalArgumentException ("horizontal gap must be nonnegative"); + this.hgap = hgap; + } + + /** + * Sets the vertical gap between lines of components to the specified value. + * + * @param vgap The new vertical gap. + */ + public void setVgap (int vgap) + { + if (vgap < 0) + throw new IllegalArgumentException ("vertical gap must be nonnegative"); + this.vgap = vgap; + } + + /** Return String description of this object. + * @return A string representation of this object. + */ + public String toString () + { + return ("[" + getClass ().getName () + ",hgap=" + hgap + ",vgap=" + vgap + + ",align=" + align + "]"); + } + + // This method is used to compute the various sizes. + private Dimension getSize (Container parent, boolean is_min) + { + synchronized (parent.getTreeLock ()) + { + int w, h, num = parent.getComponentCount (); + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + w = 0; + h = 0; + for (int i = 0; i < num; ++i) + { + if (! comps[i].visible) + continue; + + // FIXME: can we just directly read the fields in Component? + // Or will that not work with subclassing? + Dimension d; + + if (is_min) + d = comps[i].getMinimumSize (); + else + d = comps[i].getPreferredSize (); + + w += d.width; + h = Math.max (d.height, h); + } + + Insets ins = parent.getInsets (); + + w += (num + 1) * hgap + ins.left + ins.right; + h += 2 * vgap + ins.top + ins.bottom; + + return new Dimension (w, h); + } + } + + /** + * @serial The justification alignment of the lines of components, which + * will be one of the constants defined in this class. + */ + private int align; + + /** + * @serial The horizontal gap between components. + */ + private int hgap; + + /** + * @serial The vertical gap between lines of components. + */ + private int vgap; +} diff --git a/libjava/classpath/java/awt/FocusTraversalPolicy.java b/libjava/classpath/java/awt/FocusTraversalPolicy.java new file mode 100644 index 00000000000..a7f1558dde9 --- /dev/null +++ b/libjava/classpath/java/awt/FocusTraversalPolicy.java @@ -0,0 +1,103 @@ +/* FocusTraversalPolicy.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * @since 1.4 + */ +public abstract class FocusTraversalPolicy +{ + /** + * Creates a <code>FocusTraversalPolicy</code> object. + */ + public FocusTraversalPolicy() + { + // Do nothing in here. + } + + /** + * Returns the Component that should receive the focus after a Component. + * + * @exception IllegalArgumentException If root or current is null, + * or if root is not a focus cycle root of current. + */ + public abstract Component getComponentAfter(Container root, + Component current); + + /** + * Returns the Component that should receive the focus before a Component. + * + * @exception IllegalArgumentException If root or current is null, + * or if root is not a focus cycle root of current. + */ + public abstract Component getComponentBefore(Container root, + Component current); + + /** + * Returns the first Component in the traversal cycle. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getFirstComponent(Container root); + + /** + * Returns the last Component in the traversal cycle. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getLastComponent(Container root); + + /** + * Returns the default Component to focus. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getDefaultComponent(Container root); + + /** + * Returns the Component that should receive the focus when a Window is made + * visible for the first time. + * + * @exception IllegalArgumentException If window is null. + */ + public Component getInitialComponent(Window window) + { + return getDefaultComponent(window); + } +} // class FocusTraversalPolicy diff --git a/libjava/classpath/java/awt/Font.java b/libjava/classpath/java/awt/Font.java new file mode 100644 index 00000000000..30961ab2fa4 --- /dev/null +++ b/libjava/classpath/java/awt/Font.java @@ -0,0 +1,1336 @@ +/* Font.java -- Font object + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.peer.ClasspathFontPeer; + +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.peer.FontPeer; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * This class represents a windowing system font. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Graydon Hoare (graydon@redhat.com) + */ +public class Font implements Serializable +{ + +/* + * Static Variables + */ + +/** + * Constant indicating a "plain" font. + */ +public static final int PLAIN = 0; + +/** + * Constant indicating a "bold" font. + */ +public static final int BOLD = 1; + +/** + * Constant indicating an "italic" font. + */ +public static final int ITALIC = 2; + +/** + * Constant indicating the baseline mode characteristic of Roman. + */ +public static final int ROMAN_BASELINE = 0; + +/** + * Constant indicating the baseline mode characteristic of Chinese. + */ +public static final int CENTER_BASELINE = 1; + +/** + * Constant indicating the baseline mode characteristic of Devanigri. + */ +public static final int HANGING_BASELINE = 2; + + + /** + * Indicates to <code>createFont</code> that the supplied font data + * is in TrueType format. + * + * <p><em>Specification Note:</em> The Sun JavaDoc for J2SE 1.4 does + * not indicate whether this value also subsumes OpenType. OpenType + * is essentially the same format as TrueType, but allows to define + * glyph shapes in the same way as PostScript, using cubic bezier + * curves. + * + * @since 1.3 + */ + public static final int TRUETYPE_FONT = 0; + + + /** + * A flag for <code>layoutGlyphVector</code>, indicating that the + * orientation of a text run is from left to right. + * + * @since 1.4 + */ + public static final int LAYOUT_LEFT_TO_RIGHT = 0; + + + /** + * A flag for <code>layoutGlyphVector</code>, indicating that the + * orientation of a text run is from right to left. + * + * @since 1.4 + */ + public static final int LAYOUT_RIGHT_TO_LEFT = 1; + + + /** + * A flag for <code>layoutGlyphVector</code>, indicating that the + * text does not contain valid characters before the + * <code>start</code> position. If this flag is set, + * <code>layoutGlyphVector</code> does not examine the text before + * <code>start</code>, even if this would be necessary to select the + * correct glyphs (e.g., for Arabic text). + * + * @since 1.4 + */ + public static final int LAYOUT_NO_START_CONTEXT = 2; + + + /** + * A flag for <code>layoutGlyphVector</code>, indicating that the + * text does not contain valid characters after the + * <code>limit</code> position. If this flag is set, + * <code>layoutGlyphVector</code> does not examine the text after + * <code>limit</code>, even if this would be necessary to select the + * correct glyphs (e.g., for Arabic text). + * + * @since 1.4 + */ + public static final int LAYOUT_NO_LIMIT_CONTEXT = 4; + + /** + * The logical name of this font. + * + * @since 1.0 + */ + protected String name; + + /** + * The size of this font in pixels. + * + * @since 1.0 + */ + protected int size; + + /** + * The style of this font -- PLAIN, BOLD, ITALIC or BOLD+ITALIC. + * + * @since 1.0 + */ + protected int style; + +// Serialization constant +private static final long serialVersionUID = -4206021311591459213L; + + + // The ClasspathToolkit-provided peer which implements this font + private ClasspathFontPeer peer; + +/*************************************************************************/ + +/* + * Static Methods + */ + +/** + * Creates a <code>Font</code> object from the specified string, which + * is in one of the following formats: + * <p> + * <ul> + * <li>fontname-style-pointsize + * <li>fontname-style + * <li>fontname-pointsize + * <li>fontname + * </ul> + * <p> + * The style should be one of BOLD, ITALIC, or BOLDITALIC. The default + * style if none is specified is PLAIN. The default size if none + * is specified is 12. + * + * @param fontspec a string specifying the required font (<code>null</code> + * permitted, interpreted as 'Dialog-PLAIN-12'). + * + * @return A font. + */ + public static Font decode (String fontspec) +{ + if (fontspec == null) + fontspec = "Dialog-PLAIN-12"; + String name = null; + int style = PLAIN; + int size = 12; + + StringTokenizer st = new StringTokenizer(fontspec, "- "); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (name == null) + { + name = token; + continue; + } + + if (token.toUpperCase().equals("BOLD")) + { + style = BOLD; + continue; + } + if (token.toUpperCase().equals("ITALIC")) + { + style = ITALIC; + continue; + } + if (token.toUpperCase().equals("BOLDITALIC")) + { + style = BOLD | ITALIC; + continue; + } + + int tokenval = 0; + try + { + tokenval = Integer.parseInt(token); + } + catch(NumberFormatException e) + { + // Ignored. + } + + if (tokenval != 0) + size = tokenval; + } + + HashMap attrs = new HashMap(); + ClasspathFontPeer.copyStyleToAttrs (style, attrs); + ClasspathFontPeer.copySizeToAttrs (size, attrs); + + return getFontFromToolkit (name, attrs); +} + + /* These methods delegate to the toolkit. */ + + protected static ClasspathToolkit tk () + { + return (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); + } + + /* Every factory method in Font should eventually call this. */ + protected static Font getFontFromToolkit (String name, Map attribs) + { + return tk ().getFont (name, attribs); + } + + /* Every Font constructor should eventually call this. */ + protected static ClasspathFontPeer getPeerFromToolkit (String name, Map attrs) + { + return tk ().getClasspathFontPeer (name, attrs); + } + + +/*************************************************************************/ + +/** + * Returns a <code>Font</code> object from the passed property name. + * + * @param propname The name of the system property. + * @param defval Value to use if the property is not found. + * + * @return The requested font, or <code>default</code> if the property + * not exist or is malformed. + */ + public static Font getFont (String propname, Font defval) +{ + String propval = System.getProperty(propname); + if (propval != null) + return decode (propval); + return defval; +} + +/*************************************************************************/ + +/** + * Returns a <code>Font</code> object from the passed property name. + * + * @param propname The name of the system property. + * + * @return The requested font, or <code>null</code> if the property + * not exist or is malformed. + */ + public static Font getFont (String propname) +{ + return getFont (propname, (Font)null); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Font</code> with the specified + * attributes. + * + * @param name The name of the font. + * @param style The font style. + * @param size The font point size. + */ + + public Font (String name, int style, int size) + { + HashMap attrs = new HashMap(); + ClasspathFontPeer.copyStyleToAttrs (style, attrs); + ClasspathFontPeer.copySizeToAttrs (size, attrs); + this.peer = getPeerFromToolkit (name, attrs); + } + + public Font (Map attrs) + { + this(null, attrs); + } + + /* This extra constructor is here to permit ClasspathToolkit and to build + a font with a "logical name" as well as attrs. */ + public Font (String name, Map attrs) + { + // If attrs is null, setting it to an empty HashMap will give this + // Font default attributes. + if (attrs == null) + attrs = new HashMap(); + this.peer = getPeerFromToolkit (name, attrs); + } + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the logical name of the font. A logical name is the name the + * font was constructed with. It may be the name of a logical font (one + * of 6 required names in all java environments) or it may be a face + * name. + * + * @return The logical name of the font. + * + * @see #getFamily() + * @see #getFontName() + */ + public String getName () +{ + return peer.getName (this); +} + +/*************************************************************************/ + +/** + * Returns the style of the font. + * + * @return The font style. + */ + public int getSize () +{ + return (int) peer.getSize (this); +} + + public float getSize2D () +{ + return peer.getSize (this); +} + +/*************************************************************************/ + +/** + * Tests whether or not this is a plain font. This will be true if + * and only if neither the bold nor the italics style is set. + * + * @return <code>true</code> if this is a plain font, <code>false</code> + * otherwise. + */ + public boolean isPlain () +{ + return peer.isPlain (this); +} + +/*************************************************************************/ + +/** + * Tests whether or not this font is bold. + * + * @return <code>true</code> if this font is bold, <code>false</code> + * otherwise. + */ + public boolean isBold () +{ + return peer.isBold (this); +} + +/*************************************************************************/ + +/** + * Tests whether or not this font is italic. + * + * @return <code>true</code> if this font is italic, <code>false</code> + * otherwise. + */ + public boolean isItalic () +{ + return peer.isItalic (this); +} + +/*************************************************************************/ + +/** + * Returns the family name of this font. A family name describes a design + * or "brand name" (such as Helvetica or Palatino). It is less specific + * than a font face name (such as Helvetica Bold). + * + * @return A string containing the font family name. + * + * @since 1.2 + * + * @see #getName() + * @see #getFontName() + * @see GraphicsEnvironment#getAvailableFontFamilyNames() + */ + public String getFamily () +{ + return peer.getFamily (this); +} + +/** + * Returns integer code representing the sum of style flags of this font, a + * combination of either {@link #PLAIN}, {@link #BOLD}, or {@link #ITALIC}. + * + * @return code representing the style of this font. + * + * @see #isPlain() + * @see #isBold() + * @see #isItalic() + */ + public int getStyle () +{ + return peer.getStyle (this); +} + +/** + * Checks if specified character maps to a glyph in this font. + * + * @param c The character to check. + * + * @return Whether the character has a corresponding glyph in this font. + * + * @since 1.2 + */ + public boolean canDisplay (char c) +{ + return peer.canDisplay (this, c); +} + +/** + * Checks how much of a given string can be mapped to glyphs in + * this font. + * + * @param s The string to check. + * + * @return The index of the first character in <code>s</code> which cannot + * be converted to a glyph by this font, or <code>-1</code> if all + * characters can be mapped to glyphs. + * + * @since 1.2 + */ + public int canDisplayUpTo (String s) +{ + return peer.canDisplayUpTo (this, new StringCharacterIterator (s), + 0, s.length () - 1); +} + +/** + * Checks how much of a given sequence of text can be mapped to glyphs in + * this font. + * + * @param text Array containing the text to check. + * @param start Position of first character to check in <code>text</code>. + * @param limit Position of last character to check in <code>text</code>. + * + * @return The index of the first character in the indicated range which + * cannot be converted to a glyph by this font, or <code>-1</code> if all + * characters can be mapped to glyphs. + * + * @since 1.2 + * + * @throws IndexOutOfBoundsException if the range [start, limit] is + * invalid in <code>text</code>. + */ + public int canDisplayUpTo (char[] text, int start, int limit) +{ + return peer.canDisplayUpTo + (this, new StringCharacterIterator (new String (text)), start, limit); +} + +/** + * Checks how much of a given sequence of text can be mapped to glyphs in + * this font. + * + * @param i Iterator over the text to check. + * @param start Position of first character to check in <code>i</code>. + * @param limit Position of last character to check in <code>i</code>. + * + * @return The index of the first character in the indicated range which + * cannot be converted to a glyph by this font, or <code>-1</code> if all + * characters can be mapped to glyphs. + * + * @since 1.2 + * + * @throws IndexOutOfBoundsException if the range [start, limit] is + * invalid in <code>i</code>. + */ + public int canDisplayUpTo (CharacterIterator i, int start, int limit) +{ + return peer.canDisplayUpTo (this, i, start, limit); +} + +/** + * Creates a new font with point size 1 and {@link #PLAIN} style, + * reading font data from the provided input stream. The resulting font + * can have further fonts derived from it using its + * <code>deriveFont</code> method. + * + * @param fontFormat Integer code indicating the format the font data is + * in.Currently this can only be {@link #TRUETYPE_FONT}. + * @param is {@link InputStream} from which font data will be read. This + * stream is not closed after font data is extracted. + * + * @return A new {@link Font} of the format indicated. + * + * @throws IllegalArgumentException if <code>fontType</code> is not + * recognized. + * @throws FontFormatException if data in InputStream is not of format + * indicated. + * @throws IOException if insufficient data is present on InputStream. + * + * @since 1.3 + */ + public static Font createFont (int fontFormat, InputStream is) + throws FontFormatException, IOException +{ + return tk().createFont (fontFormat, is); +} + +/** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param str The string to convert to Glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector (FontRenderContext ctx, String str) +{ + return peer.createGlyphVector (this, ctx, new StringCharacterIterator (str)); +} + +/** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param i Iterator over the text to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector (FontRenderContext ctx, CharacterIterator i) +{ + return peer.createGlyphVector (this, ctx, i); +} + +/** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param chars Array of characters to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector (FontRenderContext ctx, char[] chars) +{ + return peer.createGlyphVector + (this, ctx, new StringCharacterIterator (new String (chars))); +} + +/** + * Extracts a sequence of glyphs from a font, returning a new {@link + * GlyphVector} with a mapped glyph for each input glyph code. + * + * @param ctx The rendering context used for precise glyph placement. + * @param glyphCodes Array of characters to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + * + * @specnote This method is documented to perform character-to-glyph + * conversions, in the Sun documentation, but its second parameter name is + * "glyphCodes" and it is not clear to me why it would exist if its + * purpose was to transport character codes inside integers. I assume it + * is mis-documented in the Sun documentation. + */ + + public GlyphVector createGlyphVector (FontRenderContext ctx, int[] glyphCodes) +{ + return peer.createGlyphVector (this, ctx, glyphCodes); +} + +/** + * Produces a new {@link Font} based on the current font, adjusted to a + * new size and style. + * + * @param style The style of the newly created font. + * @param size The size of the newly created font. + * + * @return A clone of the current font, with the specified size and style. + * + * @since 1.2 + */ + public Font deriveFont (int style, float size) +{ + return peer.deriveFont (this, style, size); +} + +/** + * Produces a new {@link Font} based on the current font, adjusted to a + * new size. + * + * @param size The size of the newly created font. + * + * @return A clone of the current font, with the specified size. + * + * @since 1.2 + */ + public Font deriveFont (float size) +{ + return peer.deriveFont (this, size); +} + +/** + * Produces a new {@link Font} based on the current font, adjusted to a + * new style. + * + * @param style The style of the newly created font. + * + * @return A clone of the current font, with the specified style. + * + * @since 1.2 + */ + public Font deriveFont (int style) +{ + return peer.deriveFont (this, style); +} + +/** + * Produces a new {@link Font} based on the current font, adjusted to a + * new style and subjected to a new affine transformation. + * + * @param style The style of the newly created font. + * @param a The transformation to apply. + * + * @return A clone of the current font, with the specified style and + * transform. + * + * @throws IllegalArgumentException If transformation is + * <code>null</code>. + * + * @since 1.2 + */ + public Font deriveFont (int style, AffineTransform a) +{ + if (a == null) + throw new IllegalArgumentException ("Affine transformation is null"); + + return peer.deriveFont (this, style, a); +} + +/** + * Produces a new {@link Font} based on the current font, subjected + * to a new affine transformation. + * + * @param a The transformation to apply. + * + * @return A clone of the current font, with the specified transform. + * + * @throws IllegalArgumentException If transformation is + * <code>null</code>. + * + * @since 1.2 + */ + public Font deriveFont (AffineTransform a) +{ + if (a == null) + throw new IllegalArgumentException ("Affine transformation is null"); + + return peer.deriveFont (this, a); +} + +/** + * Produces a new {@link Font} based on the current font, adjusted to a + * new set of attributes. + * + * @param attributes Attributes of the newly created font. + * + * @return A clone of the current font, with the specified attributes. + * + * @since 1.2 + */ + public Font deriveFont (Map attributes) +{ + return peer.deriveFont (this, attributes); +} + +/** + * Returns a map of chracter attributes which this font currently has set. + * + * @return A map of chracter attributes which this font currently has set. + * + * @see #getAvailableAttributes() + * @see java.text.AttributedCharacterIterator.Attribute + * @see java.awt.font.TextAttribute + */ + public Map getAttributes () +{ + return peer.getAttributes (this); +} + +/** + * Returns an array of chracter attribute keys which this font understands. + * + * @return An array of chracter attribute keys which this font understands. + * + * @see #getAttributes() + * @see java.text.AttributedCharacterIterator.Attribute + * @see java.awt.font.TextAttribute + */ + public AttributedCharacterIterator.Attribute[] getAvailableAttributes() +{ + return peer.getAvailableAttributes (this); +} + +/** + * Returns a baseline code (one of {@link #ROMAN_BASELINE}, {@link + * #CENTER_BASELINE} or {@link #HANGING_BASELINE}) indicating which baseline + * this font will measure baseline offsets for, when presenting glyph + * metrics for a given character. + * + * Baseline offsets describe the position of a glyph relative to an + * invisible line drawn under, through the center of, or over a line of + * rendered text, respectively. Different scripts use different baseline + * modes, so clients should not assume all baseline offsets in a glyph + * vector are from a common baseline. + * + * @param c The character code to select a baseline mode for. + * + * @return The baseline mode which would be used in a glyph associated + * with the provided character. + * + * @since 1.2 + * + * @see LineMetrics#getBaselineOffsets() + */ + public byte getBaselineFor (char c) +{ + return peer.getBaselineFor (this, c); +} + +/** + * Returns the family name of this font. A family name describes a + * typographic style (such as Helvetica or Palatino). It is more specific + * than a logical font name (such as Sans Serif) but less specific than a + * font face name (such as Helvetica Bold). + * + * @param lc The locale in which to describe the name of the font family. + * + * @return A string containing the font family name, localized for the + * provided locale. + * + * @since 1.2 + * + * @see #getName() + * @see #getFontName() + * @see GraphicsEnvironment#getAvailableFontFamilyNames() + * @see Locale + */ + public String getFamily (Locale lc) +{ + return peer.getFamily (this, lc); +} + +/** + * Returns a font appropriate for the given attribute set. + * + * @param attributes The attributes required for the new font. + * + * @return A new Font with the given attributes. + * + * @since 1.2 + * + * @see java.awt.font.TextAttribute + */ + public static Font getFont (Map attributes) +{ + return getFontFromToolkit (null, attributes); +} + +/** + * Returns the font face name of the font. A font face name describes a + * specific variant of a font family (such as Helvetica Bold). It is more + * specific than both a font family name (such as Helvetica) and a logical + * font name (such as Sans Serif). + * + * @return The font face name of the font. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + */ + public String getFontName () +{ + return peer.getFontName (this); +} + +/** + * Returns the font face name of the font. A font face name describes a + * specific variant of a font family (such as Helvetica Bold). It is more + * specific than both a font family name (such as Helvetica). + * + * @param lc The locale in which to describe the name of the font face. + * + * @return A string containing the font face name, localized for the + * provided locale. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + */ + public String getFontName (Locale lc) +{ + return peer.getFontName (this, lc); +} + +/** + * Returns the italic angle of this font, a measurement of its slant when + * style is {@link #ITALIC}. The precise meaning is the inverse slope of a + * caret line which "best measures" the font's italic posture. + * + * @return The italic angle. + * + * @see java.awt.font.TextAttribute#POSTURE + */ + public float getItalicAngle () +{ + return peer.getItalicAngle (this); +} + +/** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param text The string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>text</code>. + */ + public LineMetrics getLineMetrics(String text, int begin, + int limit, FontRenderContext rc) +{ + return peer.getLineMetrics (this, new StringCharacterIterator (text), + begin, limit, rc); +} + +/** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param chars The string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>chars</code>. + */ + public LineMetrics getLineMetrics(char[] chars, int begin, + int limit, FontRenderContext rc) +{ + return peer.getLineMetrics (this, new StringCharacterIterator (new String(chars)), + begin, limit, rc); +} + +/** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param ci The string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>ci</code>. + */ + public LineMetrics getLineMetrics (CharacterIterator ci, int begin, + int limit, FontRenderContext rc) +{ + return peer.getLineMetrics (this, ci, begin, limit, rc); +} + +/** + * Returns the maximal bounding box of all the bounding boxes in this + * font, when the font's bounding boxes are evaluated in a given {@link + * FontRenderContext} + * + * @param rc Context in which to evaluate bounding boxes. + * + * @return The maximal bounding box. + */ + public Rectangle2D getMaxCharBounds (FontRenderContext rc) +{ + return peer.getMaxCharBounds (this, rc); +} + +/** + * Returns the glyph code this font uses to represent missing glyphs. This + * code will be present in glyph vectors when the font was unable to + * locate a glyph to represent a particular character code. + * + * @return The missing glyph code. + * + * @since 1.2 + */ + public int getMissingGlyphCode () +{ + return peer.getMissingGlyphCode (this); +} + +/** + * Returns the overall number of glyphs in this font. This number is one + * more than the greatest glyph code used in any glyph vectors this font + * produces. In other words, glyph codes are taken from the range + * <code>[ 0, getNumGlyphs() - 1 ]</code>. + * + * @return The number of glyphs in this font. + * + * @since 1.2 + */ + public int getNumGlyphs () +{ + return peer.getMissingGlyphCode (this); +} + +/** + * Returns the PostScript Name of this font. + * + * @return The PostScript Name of this font. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + * @see #getFontName() + */ + public String getPSName () +{ + return peer.getPostScriptName (this); +} + +/** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param str The string to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @see #createGlyphVector(FontRenderContext, String) + */ + public Rectangle2D getStringBounds (String str, FontRenderContext frc) +{ + return getStringBounds (str, 0, str.length () - 1, frc); +} + +/** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param str The string to measure. + * @param begin Index of the first character in <code>str</code> to measure. + * @param limit Index of the last character in <code>str</code> to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>str</code>. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, String) + */ + public Rectangle2D getStringBounds (String str, int begin, + int limit, FontRenderContext frc) +{ + return peer.getStringBounds (this, new StringCharacterIterator(str), begin, limit, frc); +} + +/** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param ci The text to measure. + * @param begin Index of the first character in <code>ci</code> to measure. + * @param limit Index of the last character in <code>ci</code> to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>ci</code>. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, CharacterIterator) + */ + public Rectangle2D getStringBounds (CharacterIterator ci, int begin, + int limit, FontRenderContext frc) +{ + return peer.getStringBounds (this, ci, begin, limit, frc); +} + +/** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param chars The text to measure. + * @param begin Index of the first character in <code>ci</code> to measure. + * @param limit Index of the last character in <code>ci</code> to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>chars</code>. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, char[]) + */ + public Rectangle2D getStringBounds (char[] chars, int begin, + int limit, FontRenderContext frc) +{ + return peer.getStringBounds (this, new StringCharacterIterator (new String (chars)), + begin, limit, frc); +} + +/** + * Returns a copy of the affine transformation this font is currently + * subject to, if any. + * + * @return The current transformation. + */ + public AffineTransform getTransform () +{ + return peer.getTransform (this); +} + +/** + * Indicates whether this font's line metrics are uniform. A font may be + * composed of several "subfonts", each covering a different code range, + * and each with their own line metrics. A font with no subfonts, or + * subfonts with identical line metrics, is said to have "uniform" line + * metrics. + * + * @return Whether this font has uniform line metrics. + * + * @see LineMetrics + * @see #getLineMetrics(String, FontRenderContext) + */ + public boolean hasUniformLineMetrics () +{ + return peer.hasUniformLineMetrics (this); +} + +/** + * Indicates whether this font is subject to a non-identity affine + * transformation. + * + * @return <code>true</code> iff the font has a non-identity affine + * transformation applied to it. + */ + public boolean isTransformed () +{ + return peer.isTransformed (this); +} + +/** + * Produces a glyph vector representing a full layout fo the specified + * text in this font. Full layouts may include complex shaping and + * reordering operations, for scripts such as Arabic or Hindi. + * + * Bidirectional (bidi) layout is not performed in this method; text + * should have its bidi direction specified with one of the flags {@link + * #LAYOUT_LEFT_TO_RIGHT} or {@link #LAYOUT_RIGHT_TO_LEFT}. + * + * Some types of layout (notably Arabic glyph shaping) may examine context + * characters beyond the bounds of the indicated range, in order to select + * an appropriate shape. The flags {@link #LAYOUT_NO_START_CONTEXT} and + * {@link #LAYOUT_NO_LIMIT_CONTEXT} can be provided to prevent these extra + * context areas from being examined, for instance if they contain invalid + * characters. + * + * @param frc Context in which to perform the layout. + * @param chars Text to perform layout on. + * @param start Index of first character to perform layout on. + * @param limit Index of last character to perform layout on. + * @param flags Combination of flags controlling layout. + * + * @return A new {@link GlyphVector} representing the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>chars</code>. + */ + public GlyphVector layoutGlyphVector (FontRenderContext frc, + char[] chars, int start, + int limit, int flags) +{ + return peer.layoutGlyphVector (this, frc, chars, start, limit, flags); +} + + +/** + * Returns a native peer object for this font. + * + * @return A native peer object for this font. + * + * @deprecated + */ + public FontPeer getPeer () +{ + return peer; +} + + +/** + * Returns a hash value for this font. + * + * @return A hash for this font. + */ + public int hashCode() +{ + return this.toString().hashCode(); +} + + +/** + * Tests whether or not the specified object is equal to this font. This + * will be true if and only if: + * <P> + * <ul> + * <li>The object is not <code>null</code>. + * <li>The object is an instance of <code>Font</code>. + * <li>The object has the same names, style, size, and transform as this object. + * </ul> + * + * @return <code>true</code> if the specified object is equal to this + * object, <code>false</code> otherwise. + */ +public boolean +equals(Object obj) +{ + if (obj == null) + return(false); + + if (!(obj instanceof Font)) + return(false); + + Font f = (Font)obj; + + return (f.getName ().equals (this.getName ()) && + f.getFamily ().equals (this.getFamily ()) && + f.getFontName ().equals (this.getFontName ()) && + f.getTransform ().equals (this.getTransform ()) && + f.getSize() == this.getSize() && + f.getStyle() == this.getStyle()); +} + +/*************************************************************************/ + +/** + * Returns a string representation of this font. + * + * @return A string representation of this font. + */ +public String +toString() +{ + String styleString = ""; + + switch (getStyle ()) + { + case 0: + styleString = "plain"; + break; + case 1: + styleString = "bold"; + break; + case 2: + styleString = "italic"; + break; + default: + styleString = "unknown"; + } + + return getClass ().getName () + + "[family=" + getFamily () + + ",name=" + getFontName () + + ",style=" + styleString + + ",size=" + getSize () + "]"; +} + + + /** + * Determines the line metrics for a run of text. + * + * @param str the text run to be measured. + * + * @param frc the font rendering parameters that are used for the + * measurement. The exact placement and size of text slightly + * depends on device-specific characteristics, for instance + * the device resolution or anti-aliasing. For this reason, + * the returned measurement will only be accurate if the + * passed <code>FontRenderContext</code> correctly reflects + * the relevant parameters. Hence, <code>frc</code> should be + * obtained from the same <code>Graphics2D</code> that will + * be used for drawing, and any rendering hints should be set + * to the desired values before obtaining <code>frc</code>. + * + * @see java.awt.Graphics2D#getFontRenderContext() + */ + public LineMetrics getLineMetrics(String str, FontRenderContext frc) + { + return getLineMetrics (str, 0, str.length () - 1, frc); + } + +} // class Font + diff --git a/libjava/classpath/java/awt/FontFormatException.java b/libjava/classpath/java/awt/FontFormatException.java new file mode 100644 index 00000000000..6ec8757fb5b --- /dev/null +++ b/libjava/classpath/java/awt/FontFormatException.java @@ -0,0 +1,65 @@ +/* FontFormatException.java -- the specified font is bad + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Thrown when a specified font is bad. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Font + * @since 1.3 + * @status updated to 1.4 + */ +public class FontFormatException extends Exception +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -4481290147811361272L; + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public FontFormatException(String message) + { + super(message); + } +} // class FontFormatException diff --git a/libjava/classpath/java/awt/FontMetrics.java b/libjava/classpath/java/awt/FontMetrics.java new file mode 100644 index 00000000000..e702a625766 --- /dev/null +++ b/libjava/classpath/java/awt/FontMetrics.java @@ -0,0 +1,425 @@ +/* FontMetrics.java -- Information about about a fonts display characteristics + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.Rectangle2D; +import java.text.CharacterIterator; + +// FIXME: I leave many methods basically unimplemented. This +// should be reviewed. + +/** + * This class returns information about the display characteristics of + * a font. It is abstract, and concrete subclasses should implement at + * least the following methods: + * + * <ul> + * <li>getAscent()</li> + * <li>getDescent()</li> + * <li>getLeading()</li> + * <li>getMaxAdvance()</li> + * <li>charWidth(char)</li> + * <li>charsWidth(char[], int, int)</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class FontMetrics implements java.io.Serializable +{ + // Serialization constant. + private static final long serialVersionUID = 1681126225205050147L; + + /** + * This is the font for which metrics will be returned. + */ + protected Font font; + + /** + * Initializes a new instance of <code>FontMetrics</code> for the + * specified font. + * + * @param font The font to return metric information for. + */ + protected FontMetrics(Font font) + { + this.font = font; + } + + /** + * Returns the font that this object is creating metric information fo. + * + * @return The font for this object. + */ + public Font getFont() + { + return font; + } + + /** + * Returns the leading, or spacing between lines, for this font. + * + * @return The font leading. + */ + public int getLeading() + { + return 0; + } + + /** + * Returns the ascent of the font, which is the distance from the base + * to the top of the majority of characters in the set. Some characters + * can exceed this value however. + * + * @return The font ascent. + */ + public int getAscent() + { + return 1; + } + + /** + * Returns the descent of the font, which is the distance from the base + * to the bottom of the majority of characters in the set. Some characters + * can exceed this value however. + * + * @return The font descent. + */ + public int getDescent() + { + return 1; + } + + /** + * Returns the height of a line in this font. This will be the sum + * of the leading, the ascent, and the descent. + * + * @return The height of the font. + */ + public int getHeight() + { + return getAscent() + getDescent() + getLeading(); + } + + /** + * Returns the maximum ascent value. This is the maximum distance any + * character in the font rised above the baseline. + * + * @return The maximum ascent for this font. + */ + public int getMaxAscent() + { + return getAscent(); + } + + /** + * Returns the maximum descent value. This is the maximum distance any + * character in the font extends below the baseline. + * + * @return The maximum descent for this font. + */ + public int getMaxDescent() + { + return getMaxDecent(); + } + + /** + * Returns the maximum descent value. This is the maximum distance any + * character in the font extends below the baseline. + * + * @return The maximum descent for this font. + * + * @deprecated This method is deprecated in favor of + * <code>getMaxDescent()</code>. + */ + public int getMaxDecent() + { + return getDescent(); + } + + /** + * Returns the width of the widest character in the font. + * + * @return The width of the widest character in the font. + */ + public int getMaxAdvance() + { + return -1; + } + + /** + * Returns the width of the specified character. + * + * @param ch The character to return the width of. + * + * @return The width of the specified character. + */ + public int charWidth(int ch) + { + return charWidth((char) ch); + } + + /** + * Returns the width of the specified character. + * + * @param ch The character to return the width of. + * + * @return The width of the specified character. + */ + public int charWidth(char ch) + { + return 1; + } + + /** + * Returns the total width of the specified string + * + * @param str The string to return the width of. + * + * @return The width of the string. + */ + public int stringWidth(String str) + { + char[] buf = new char[str.length()]; + str.getChars(0, str.length(), buf, 0); + + return charsWidth(buf, 0, buf.length); + } + + /** + * Returns the total width of the specified character array. + * + * @param buf The character array containing the data. + * @param offset The offset into the array to start calculating from. + * @param len The total number of bytes to process. + * + * @return The width of the requested characters. + */ + public int charsWidth(char[] buf, int offset, int len) + { + int total_width = 0; + for (int i = offset; i < len; i++) + total_width += charWidth(buf[i]); + return total_width; + } + + /** + * Returns the total width of the specified byte array. + * + * @param buf The byte array containing the data. + * @param offset The offset into the array to start calculating from. + * @param len The total number of bytes to process. + * + * @return The width of the requested characters. + */ + public int bytesWidth(byte[] buf, int offset, int len) + { + int total_width = 0; + for (int i = offset; i < len; i++) + total_width = charWidth((char) buf[i]); + + return total_width; + } + + /** + * Returns the widths of the first 256 characters in the font. + * + * @return The widths of the first 256 characters in the font. + */ + public int[] getWidths() + { + int[] result = new int[256]; + for (char i = 0; i < 256; i++) + result[i] = charWidth(i); + return result; + } + + /** + * Returns a string representation of this object. + * + * @return A string representation of this object. + */ + public String toString() + { + return (this.getClass() + "[font=" + font + ",ascent=" + getAscent() + + ",descent=" + getDescent() + ",height=" + getHeight() + "]"); + } + + // Generic FontRenderContext used when getLineMetrics is called with a + // plain Graphics object. + private static final FontRenderContext gRC = new FontRenderContext(null, + false, + false); + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param text The string to calculate metrics from. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + */ + public LineMetrics getLineMetrics(String text, Graphics g) + { + return getLineMetrics(text, 0, text.length(), g); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param text The string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>text</code>. + */ + public LineMetrics getLineMetrics(String text, int begin, int limit, + Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(text, begin, limit, rc); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param chars The string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>text</code>. + */ + public LineMetrics getLineMetrics(char[] chars, int begin, int limit, + Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(chars, begin, limit, rc); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param ci An iterator over the string to calculate metrics from. + * @param begin Index of first character in <code>text</code> to measure. + * @param limit Index of last character in <code>text</code> to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in <code>text</code>. + */ + public LineMetrics getLineMetrics(CharacterIterator ci, int begin, + int limit, Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(ci, begin, limit, rc); + } + + public Rectangle2D getStringBounds(String str, Graphics context) + { + return font.getStringBounds(str, getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(String str, int beginIndex, int limit, + Graphics context) + { + return font.getStringBounds(str, beginIndex, limit, + getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit, + Graphics context) + { + return font.getStringBounds(chars, beginIndex, limit, + getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, + int limit, Graphics context) + { + return font.getStringBounds(ci, beginIndex, limit, + getFontRenderContext(context)); + } + + private FontRenderContext getFontRenderContext(Graphics context) + { + if (context instanceof Graphics2D) + return ((Graphics2D) context).getFontRenderContext(); + + return gRC; + } +} diff --git a/libjava/classpath/java/awt/Frame.java b/libjava/classpath/java/awt/Frame.java new file mode 100644 index 00000000000..0cb97f8b596 --- /dev/null +++ b/libjava/classpath/java/awt/Frame.java @@ -0,0 +1,649 @@ +/* Frame.java -- AWT toplevel window + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.FramePeer; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * This class is a top-level window with a title bar and window + * decorations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Frame extends Window implements MenuContainer +{ +/** + * Constant for the default cursor. + * @deprecated Replaced by <code>Cursor.DEFAULT_CURSOR</code> instead. + */ +public static final int DEFAULT_CURSOR = Cursor.DEFAULT_CURSOR; + +/** + * Constant for a cross-hair cursor. + * @deprecated Use <code>Cursor.CROSSHAIR_CURSOR</code> instead. + */ +public static final int CROSSHAIR_CURSOR = Cursor.CROSSHAIR_CURSOR; + +/** + * Constant for a cursor over a text field. + * @deprecated Use <code>Cursor.TEXT_CURSOR</code> instead. + */ +public static final int TEXT_CURSOR = Cursor.TEXT_CURSOR; + +/** + * Constant for a cursor to display while waiting for an action to complete. + * @deprecated Use <code>Cursor.WAIT_CURSOR</code>. + */ +public static final int WAIT_CURSOR = Cursor.WAIT_CURSOR; + +/** + * Cursor used over SW corner of window decorations. + * @deprecated Use <code>Cursor.SW_RESIZE_CURSOR</code> instead. + */ +public static final int SW_RESIZE_CURSOR = Cursor.SW_RESIZE_CURSOR; + +/** + * Cursor used over SE corner of window decorations. + * @deprecated Use <code>Cursor.SE_RESIZE_CURSOR</code> instead. + */ +public static final int SE_RESIZE_CURSOR = Cursor.SE_RESIZE_CURSOR; + +/** + * Cursor used over NW corner of window decorations. + * @deprecated Use <code>Cursor.NW_RESIZE_CURSOR</code> instead. + */ +public static final int NW_RESIZE_CURSOR = Cursor.NW_RESIZE_CURSOR; + +/** + * Cursor used over NE corner of window decorations. + * @deprecated Use <code>Cursor.NE_RESIZE_CURSOR</code> instead. + */ +public static final int NE_RESIZE_CURSOR = Cursor.NE_RESIZE_CURSOR; + +/** + * Cursor used over N edge of window decorations. + * @deprecated Use <code>Cursor.N_RESIZE_CURSOR</code> instead. + */ +public static final int N_RESIZE_CURSOR = Cursor.N_RESIZE_CURSOR; + +/** + * Cursor used over S edge of window decorations. + * @deprecated Use <code>Cursor.S_RESIZE_CURSOR</code> instead. + */ +public static final int S_RESIZE_CURSOR = Cursor.S_RESIZE_CURSOR; + +/** + * Cursor used over E edge of window decorations. + * @deprecated Use <code>Cursor.E_RESIZE_CURSOR</code> instead. + */ +public static final int E_RESIZE_CURSOR = Cursor.E_RESIZE_CURSOR; + +/** + * Cursor used over W edge of window decorations. + * @deprecated Use <code>Cursor.W_RESIZE_CURSOR</code> instead. + */ +public static final int W_RESIZE_CURSOR = Cursor.W_RESIZE_CURSOR; + +/** + * Constant for a hand cursor. + * @deprecated Use <code>Cursor.HAND_CURSOR</code> instead. + */ +public static final int HAND_CURSOR = Cursor.HAND_CURSOR; + +/** + * Constant for a cursor used during window move operations. + * @deprecated Use <code>Cursor.MOVE_CURSOR</code> instead. + */ +public static final int MOVE_CURSOR = Cursor.MOVE_CURSOR; + +public static final int ICONIFIED = 1; +public static final int MAXIMIZED_BOTH = 6; +public static final int MAXIMIZED_HORIZ = 2; +public static final int MAXIMIZED_VERT = 4; +public static final int NORMAL = 0; + +// Serialization version constant +private static final long serialVersionUID = 2673458971256075116L; + +/** + * @serial The version of the class data being serialized + * // FIXME: what is this value? + */ +private int frameSerializedDataVersion; + +/** + * @serial Image used as the icon when this frame is minimized. + */ +private Image icon; + +/** + * @serial Constant used by the JDK Motif peer set. Not used in + * this implementation. + */ +private boolean mbManagement; + +/** + * @serial The menu bar for this frame. + */ +//private MenuBar menuBar = new MenuBar(); +private MenuBar menuBar; + +/** + * @serial A list of other top-level windows owned by this window. + */ +Vector ownedWindows = new Vector(); + +/** + * @serial Indicates whether or not this frame is resizable. + */ +private boolean resizable = true; + +/** + * @serial The state of this frame. + * // FIXME: What are the values here? + * This is package-private to avoid an accessor method. + */ +int state; + +/** + * @serial The title of the frame. + */ +private String title = ""; + + /** + * Maximized bounds for this frame. + */ + private Rectangle maximizedBounds; + + /** + * This field indicates whether the frame is undecorated or not. + */ + private boolean undecorated = false; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_frame_number; + +/** + * Initializes a new instance of <code>Frame</code> that is not visible + * and has no title. + */ +public +Frame() +{ + this(""); + noteFrame(this); +} + +/** + * Initializes a new instance of <code>Frame</code> that is not visible + * and has the specified title. + * + * @param title The title of this frame. + */ +public +Frame(String title) +{ + super(); + this.title = title; + // Top-level frames are initially invisible. + visible = false; + noteFrame(this); +} + +public +Frame(GraphicsConfiguration gc) +{ + super(gc); + visible = false; + noteFrame(this); +} + +public +Frame(String title, GraphicsConfiguration gc) +{ + super(gc); + setTitle(title); + visible = false; + noteFrame(this); +} + +/** + * Returns this frame's title string. + * + * @return This frame's title string. + */ +public String +getTitle() +{ + return(title); +} + +/* + * Sets this frame's title to the specified value. + * + * @param title The new frame title. + */ +public synchronized void +setTitle(String title) +{ + this.title = title; + if (peer != null) + ((FramePeer) peer).setTitle(title); +} + +/** + * Returns this frame's icon. + * + * @return This frame's icon, or <code>null</code> if this frame does not + * have an icon. + */ +public Image +getIconImage() +{ + return(icon); +} + +/** + * Sets this frame's icon to the specified value. + * + * @icon The new icon for this frame. + */ +public synchronized void +setIconImage(Image icon) +{ + this.icon = icon; + if (peer != null) + ((FramePeer) peer).setIconImage(icon); +} + +/** + * Returns this frame's menu bar. + * + * @return This frame's menu bar, or <code>null</code> if this frame + * does not have a menu bar. + */ +public MenuBar +getMenuBar() +{ + return(menuBar); +} + +/** + * Sets this frame's menu bar. + * + * @param menuBar The new menu bar for this frame. + */ +public synchronized void +setMenuBar(MenuBar menuBar) +{ + if (peer != null) + { + if (this.menuBar != null) + this.menuBar.removeNotify(); + if (menuBar != null) + menuBar.addNotify(); + invalidateTree (); + ((FramePeer) peer).setMenuBar(menuBar); + } + this.menuBar = menuBar; +} + +/** + * Tests whether or not this frame is resizable. This will be + * <code>true</code> by default. + * + * @return <code>true</code> if this frame is resizable, <code>false</code> + * otherwise. + */ +public boolean +isResizable() +{ + return(resizable); +} + +/** + * Sets the resizability of this frame to the specified value. + * + * @param resizable <code>true</code> to make the frame resizable, + * <code>false</code> to make it non-resizable. + */ +public synchronized void +setResizable(boolean resizable) +{ + this.resizable = resizable; + if (peer != null) + ((FramePeer) peer).setResizable(resizable); +} + +/** + * Returns the cursor type of the cursor for this window. This will + * be one of the constants in this class. + * + * @return The cursor type for this frame. + * + * @deprecated Use <code>Component.getCursor()</code> instead. + */ +public int +getCursorType() +{ + return(getCursor().getType()); +} + +/** + * Sets the cursor for this window to the specified type. The specified + * type should be one of the constants in this class. + * + * @param type The cursor type. + * + * @deprecated Use <code>Component.setCursor(Cursor)</code> instead. + */ +public void +setCursor(int type) +{ + setCursor(new Cursor(type)); +} + +/** + * Removes the specified component from this frame's menu. + * + * @param menu The menu component to remove. + */ +public void +remove(MenuComponent menu) +{ + menuBar.remove(menu); +} + +/** + * Notifies this frame that it should create its native peer. + */ +private static void fireDummyEvent() +{ + EventQueue.invokeLater(new Runnable() + { + public void run() + { + // Do nothing here. + } + }); +} + +public void +addNotify() +{ + if (menuBar != null) + menuBar.addNotify(); + if (peer == null) + peer = getToolkit ().createFrame (this); + + // We now know there's a Frame (us) with a live peer, so we can start the + // fundamental queue and dispatch thread, by inserting a dummy event. + if (parent != null && parent.isDisplayable()) + fireDummyEvent(); + + super.addNotify(); +} + +public void removeNotify() +{ + if (menuBar != null) + menuBar.removeNotify(); + super.removeNotify(); + + // By now we've been disconnected from the peer, and the peer set to + // null. This is formally the same as saying "we just became + // un-displayable", so we wake up the event queue with a dummy event to + // see if it's time to shut down. + fireDummyEvent(); +} + + /** + * Returns a debugging string describing this window. + * + * @return A debugging string describing this window. + */ + protected String paramString () + { + String title = getTitle (); + + String resizable = ""; + if (isResizable ()) + resizable = ",resizable"; + + String state = ""; + switch (getState ()) + { + case NORMAL: + state = ",normal"; + break; + case ICONIFIED: + state = ",iconified"; + break; + case MAXIMIZED_BOTH: + state = ",maximized-both"; + break; + case MAXIMIZED_HORIZ: + state = ",maximized-horiz"; + break; + case MAXIMIZED_VERT: + state = ",maximized-vert"; + break; + } + + return super.paramString () + ",title=" + title + resizable + state; + } + +private static ArrayList weakFrames = new ArrayList(); + +private static void noteFrame(Frame f) +{ + weakFrames.add(new WeakReference(f)); +} + +public static Frame[] getFrames() +{ + int n = 0; + synchronized (weakFrames) + { + Iterator i = weakFrames.iterator(); + while (i.hasNext()) + { + WeakReference wr = (WeakReference) i.next(); + if (wr.get() != null) + ++n; + } + if (n == 0) + return new Frame[0]; + else + { + Frame[] frames = new Frame[n]; + n = 0; + i = weakFrames.iterator(); + while (i.hasNext()) + { + WeakReference wr = (WeakReference) i.next(); + if (wr.get() != null) + frames[n++] = (Frame) wr.get(); + } + return frames; + } + } +} + + public void setState (int state) + { + int current_state = getExtendedState (); + + if (state == NORMAL + && (current_state & ICONIFIED) != 0) + setExtendedState (current_state | ICONIFIED); + + if (state == ICONIFIED + && (current_state & ~ICONIFIED) == 0) + setExtendedState (current_state & ~ICONIFIED); + } + + public int getState () + { + /* FIXME: State might have changed in the peer... Must check. */ + + return (state & ICONIFIED) != 0 ? ICONIFIED : NORMAL; + } + + /** + * @since 1.4 + */ + public void setExtendedState (int state) + { + this.state = state; + } + + /** + * @since 1.4 + */ + public int getExtendedState () + { + return state; + } + + /** + * @since 1.4 + */ + public void setMaximizedBounds (Rectangle maximizedBounds) + { + this.maximizedBounds = maximizedBounds; + } + + /** + * Returns the maximized bounds of this frame. + * + * @return the maximized rectangle, may be null. + * + * @since 1.4 + */ + public Rectangle getMaximizedBounds () + { + return maximizedBounds; + } + + /** + * Returns whether this frame is undecorated or not. + * + * @since 1.4 + */ + public boolean isUndecorated () + { + return undecorated; + } + + /** + * Disables or enables decorations for this frame. This method can only be + * called while the frame is not displayable. + * + * @exception IllegalComponentStateException If this frame is displayable. + * + * @since 1.4 + */ + public void setUndecorated (boolean undecorated) + { + if (isDisplayable ()) + throw new IllegalComponentStateException (); + + this.undecorated = undecorated; + } + + /** + * Generate a unique name for this frame. + * + * @return A unique name for this frame. + */ + String generateName () + { + return "frame" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_frame_number++; + } + + protected class AccessibleAWTFrame extends AccessibleAWTWindow + { + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FRAME; + } + + public AccessibleStateSet getAccessibleState() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isResizable()) + states.add(AccessibleState.RESIZABLE); + if ((state & ICONIFIED) != 0) + states.add(AccessibleState.ICONIFIED); + return states; + } + } + + /** + * Gets the AccessibleContext associated with this <code>Frame</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTFrame(); + return accessibleContext; + } + +} diff --git a/libjava/classpath/java/awt/GradientPaint.java b/libjava/classpath/java/awt/GradientPaint.java new file mode 100644 index 00000000000..74067f49e86 --- /dev/null +++ b/libjava/classpath/java/awt/GradientPaint.java @@ -0,0 +1,229 @@ +/* GradientPaint.java -- + Copyright (C) 2002, 2005, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import gnu.java.awt.GradientPaintContext; + +/** + * A paint object that can be used to color a region by blending two colors. + * Instances of this class are immutable. + */ +public class GradientPaint implements Paint +{ + private final float x1; + private final float y1; + private final Color c1; + private final float x2; + private final float y2; + private final Color c2; + private final boolean cyclic; + + /** + * Creates a new acyclic <code>GradientPaint</code>. + * + * @param x1 the x-coordinate of the anchor point for color 1. + * @param y1 the y-coordinate of the anchor point for color 1. + * @param c1 color 1 (<code>null</code> not permitted). + * @param x2 the x-coordinate of the anchor point for color 2. + * @param y2 the y-coordinate of the anchor point for color 2. + * @param c2 the second color (<code>null</code> not permitted). + */ + public GradientPaint(float x1, float y1, Color c1, + float x2, float y2, Color c2) + { + this(x1, y1, c1, x2, y2, c2, false); + } + + /** + * Creates a new acyclic <code>GradientPaint</code>. + * + * @param p1 anchor point 1 (<code>null</code> not permitted). + * @param c1 color 1 (<code>null</code> not permitted). + * @param p2 anchor point 2 (<code>null</code> not permitted). + * @param c2 color 2 (<code>null</code> not permitted). + */ + public GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2) + { + this((float) p1.getX(), (float) p1.getY(), c1, + (float) p2.getX(), (float) p2.getY(), c2, false); + } + + /** + * Creates a new cyclic or acyclic <code>GradientPaint</code>. + * + * @param x1 the x-coordinate of the anchor point for color 1. + * @param y1 the y-coordinate of the anchor point for color 1. + * @param c1 color 1 (<code>null</code> not permitted). + * @param x2 the x-coordinate of the anchor point for color 2. + * @param y2 the y-coordinate of the anchor point for color 2. + * @param c2 the second color (<code>null</code> not permitted). + * @param cyclic a flag that controls whether the gradient is cyclic or + * acyclic. + */ + public GradientPaint(float x1, float y1, Color c1, + float x2, float y2, Color c2, boolean cyclic) + { + if (c1 == null || c2 == null) + throw new NullPointerException(); + this.x1 = x1; + this.y1 = y1; + this.c1 = c1; + this.x2 = x2; + this.y2 = y2; + this.c2 = c2; + this.cyclic = cyclic; + } + + /** + * Creates a new cyclic or acyclic <code>GradientPaint</code>. + * + * @param p1 anchor point 1 (<code>null</code> not permitted). + * @param c1 color 1 (<code>null</code> not permitted). + * @param p2 anchor point 2 (<code>null</code> not permitted). + * @param c2 color 2 (<code>null</code> not permitted). + * @param cyclic a flag that controls whether the gradient is cyclic or + * acyclic. + */ + public GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2, + boolean cyclic) + { + this((float) p1.getX(), (float) p1.getY(), c1, + (float) p2.getX(), (float) p2.getY(), c2, cyclic); + } + + /** + * Returns a point with the same coordinates as the anchor point for color 1. + * Note that if you modify this point, the <code>GradientPaint</code> remains + * unchanged. + * + * @return A point with the same coordinates as the anchor point for color 1. + */ + public Point2D getPoint1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the first color. + * + * @return The color (never <code>null</code>). + */ + public Color getColor1() + { + return c1; + } + + /** + * Returns a point with the same coordinates as the anchor point for color 2. + * Note that if you modify this point, the <code>GradientPaint</code> remains + * unchanged. + * + * @return A point with the same coordinates as the anchor point for color 2. + */ + public Point2D getPoint2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Returns the second color. + * + * @return The color (never <code>null</code>). + */ + public Color getColor2() + { + return c2; + } + + /** + * Returns <code>true</code> if this <code>GradientPaint</code> instance is + * cyclic, and <code>false</code> otherwise. + * + * @return A boolean. + */ + public boolean isCyclic() + { + return cyclic; + } + + /** + * Returns the {@link PaintContext} used to generate the color pattern. + * + * @param cm the color model, used as a hint (ignored in this + * implementation). + * @param deviceBounds the device space bounding box of the painted area. + * @param userBounds the user space bounding box of the painted area. + * @param xform the transformation from user space to device space. + * @param hints any hints for choosing between rendering alternatives. + * + * @return The context for performing the paint + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + Point2D xp1 = xform.transform(getPoint1(), null); + Point2D xp2 = xform.transform(getPoint2(), null); + return new GradientPaintContext((float) xp1.getX(), (float) xp1.getY(), c1, + (float) xp2.getX(), (float) xp2.getY(), c2, cyclic); + } + + /** + * Returns the transparency code for this <code>GradientPaint</code> instance. + * This is derived from the two {@link Color} objects used in creating this + * object: if both colors are opaque, this method returns + * {@link Transparency#OPAQUE}, otherwise it returns + * {@link Transparency#TRANSLUCENT}. + * + * @return {@link Transparency#OPAQUE} or {@link Transparency#TRANSLUCENT}. + */ + public int getTransparency() + { + if (c1.getAlpha() == 255 && c2.getAlpha() == 255) + return Transparency.OPAQUE; + else + return Transparency.TRANSLUCENT; + } + +} // class GradientPaint diff --git a/libjava/classpath/java/awt/Graphics.java b/libjava/classpath/java/awt/Graphics.java new file mode 100644 index 00000000000..ff26190e5f0 --- /dev/null +++ b/libjava/classpath/java/awt/Graphics.java @@ -0,0 +1,767 @@ +/* Graphics.java -- Abstract Java drawing class + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ImageObserver; +import java.text.AttributedCharacterIterator; + +/** + * This is the abstract superclass of classes for drawing to graphics + * devices such as the screen or printers. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class Graphics +{ + +/* + * Instance Variables + */ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Default constructor for subclasses. + */ +protected +Graphics() +{ +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns a copy of this <code>Graphics</code> object. + * + * @return A copy of this object. + */ +public abstract Graphics +create(); + +/*************************************************************************/ + +/** + * Returns a copy of this <code>Graphics</code> object. The origin point + * will be translated to the point (x, y) and the cliping rectangle set + * to the intersection of the clipping rectangle in this object and the + * rectangle specified by the parameters to this method. + * + * @param x The new X coordinate of the clipping region rect. + * @param y The new Y coordinate of the clipping region rect. + * @param width The width of the clipping region intersect rectangle. + * @param height The height of the clipping region intersect rectangle. + * + * @return A copy of this object, modified as specified. + */ +public Graphics +create(int x, int y, int width, int height) +{ + Graphics g = create(); + + g.translate(x, y); + // FIXME: I'm not sure if this will work. Are the old clip rect bounds + // translated above? + g.clipRect(0, 0, width, height); + + return(g); +} + +/*************************************************************************/ + +/** + * Translates this context so that its new origin point is the point + * (x, y). + * + * @param x The new X coordinate of the origin. + * @param y The new Y coordinate of the origin. + */ +public abstract void +translate(int x, int y); + +/*************************************************************************/ + +/** + * Returns the current color for this object. + * + * @return The color for this object. + */ +public abstract Color +getColor(); + +/*************************************************************************/ + +/** + * Sets the current color for this object. + * + * @param color The new color. + */ +public abstract void +setColor(Color color); + +/*************************************************************************/ + +/** + * Sets this context into "paint" mode, where the target pixels are + * completely overwritten when drawn on. + */ +public abstract void +setPaintMode(); + +/*************************************************************************/ + +/** + * Sets this context info "XOR" mode, where the targe pixles are + * XOR-ed when drawn on. + * + * @param color The color to XOR against. + */ +public abstract void +setXORMode(Color color); + +/*************************************************************************/ + +/** + * Returns the current font for this graphics context. + * + * @return The current font. + */ +public abstract Font +getFont(); + +/*************************************************************************/ + +/** + * Sets the font for this graphics context to the specified value. + * + * @param font The new font. + */ +public abstract void +setFont(Font font); + +/*************************************************************************/ + +/** + * Returns the font metrics for the current font. + * + * @return The font metrics for the current font. + */ +public FontMetrics +getFontMetrics() +{ + return(getFontMetrics(getFont())); +} + +/*************************************************************************/ + +/** + * Returns the font metrics for the specified font. + * + * @param font The font to return metrics for. + * + * @return The requested font metrics. + */ +public abstract FontMetrics +getFontMetrics(Font font); + +/*************************************************************************/ + +/** + * Returns the bounding rectangle of the clipping region for this + * graphics context. + * + * @return The bounding rectangle for the clipping region. + */ +public abstract Rectangle +getClipBounds(); + +/*************************************************************************/ + +/** + * Returns the bounding rectangle of the clipping region for this + * graphics context. + * + * @return The bounding rectangle for the clipping region. + * + * @deprecated This method is deprecated in favor of + * <code>getClipBounds()</code>. + */ +public Rectangle +getClipRect() +{ + return(getClipBounds()); +} + +/*************************************************************************/ + +/** + * Sets the clipping region to the intersection of the current clipping + * region and the rectangle determined by the specified parameters. + * + * @param x The X coordinate of the upper left corner of the intersect rect. + * @param y The Y coordinate of the upper left corner of the intersect rect. + * @param width The width of the intersect rect. + * @param height The height of the intersect rect. + */ +public abstract void +clipRect(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Sets the clipping region to the rectangle determined by the specified + * parameters. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ +public abstract void +setClip(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Returns the current clipping region as a <code>Shape</code> object. + * + * @return The clipping region as a <code>Shape</code>. + */ +public abstract Shape +getClip(); + +/*************************************************************************/ + +/** + * Sets the clipping region to the specified <code>Shape</code>. + * + * @param clip The new clipping region. + */ +public abstract void +setClip(Shape clip); + +/*************************************************************************/ + +/** + * Copies the specified rectangle to the specified offset location. + * + * @param x The X coordinate of the upper left corner of the copy rect. + * @param y The Y coordinate of the upper left corner of the copy rect. + * @param width The width of the copy rect. + * @param height The height of the copy rect. + * @param dx The offset from the X value to start drawing. + * @param dy The offset from the Y value to start drawing. + */ +public abstract void +copyArea(int x, int y, int width, int height, int dx, int dy); + +/*************************************************************************/ + +/** + * Draws a line between the two specified points. + * + * @param x1 The X coordinate of the first point. + * @param y1 The Y coordinate of the first point. + * @param x2 The X coordinate of the second point. + * @param y2 The Y coordinate of the second point. + */ +public abstract void +drawLine(int x1, int y1, int x2, int y2); + +/*************************************************************************/ + +/** + * Fills the area bounded by the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + */ +public abstract void +fillRect(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Draws the outline of the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the draw rect. + * @param y The Y coordinate of the upper left corner of the draw rect. + * @param width The width of the draw rect. + * @param height The height of the draw rect. + */ +public void +drawRect(int x, int y, int width, int height) +{ + int x1 = x; + int y1 = y; + int x2 = x + width; + int y2 = y + height; + drawLine(x1, y1, x2, y1); + drawLine(x2, y1, x2, y2); + drawLine(x2, y2, x1, y2); + drawLine(x1, y2, x1, y1); +} + +/*************************************************************************/ + +/** + * Clears the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the clear rect. + * @param y The Y coordinate of the upper left corner of the clear rect. + * @param width The width of the clear rect. + * @param height The height of the clear rect. + */ +public abstract void +clearRect(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Draws the outline of the specified rectangle with rounded cornders. + * + * @param x The X coordinate of the upper left corner of the draw rect. + * @param y The Y coordinate of the upper left corner of the draw rect. + * @param width The width of the draw rect. + * @param height The height of the draw rect. + * @param arcWidth The width of the corner arcs. + * @param arcHeight The height of the corner arcs. + */ +public abstract void +drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight); + +/*************************************************************************/ + +/** + * Fills the specified rectangle with rounded cornders. + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + * @param arcWidth The width of the corner arcs. + * @param arcHeight The height of the corner arcs. + */ +public abstract void +fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight); + +/*************************************************************************/ + +public void +draw3DRect(int x, int y, int width, int height, boolean raised) +{ + Color color = getColor(); + Color tl = color.brighter(); + Color br = color.darker(); + + if (!raised) + { + Color tmp = tl; + tl = br; + br = tmp; + } + + int x1 = x; + int y1 = y; + int x2 = x + width; + int y2 = y + height; + + setColor(tl); + drawLine(x1, y1, x2, y1); + drawLine(x1, y2, x1, y1); + setColor(br); + drawLine(x2, y1, x2, y2); + drawLine(x2, y2, x1, y2); + setColor(color); +} + +/** + * Fills the specified rectangle with a 3D effect + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + * @param raised <code>true</code> if the rectangle appears raised, + * <code>false</code> if it should appear etched. + */ +public void +fill3DRect(int x, int y, int width, int height, boolean raised) +{ + fillRect(x, y, width, height); + draw3DRect(x, y, width-1, height-1, raised); +} + +/*************************************************************************/ + +/** + * Draws an oval that just fits within the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ +public abstract void +drawOval(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Fills an oval that just fits within the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ +public abstract void +fillOval(int x, int y, int width, int height); + +/*************************************************************************/ + +/** + * Draws an arc using the specified bounding rectangle and the specified + * angle parameter. The arc is centered at the center of the rectangle. + * The arc starts at the arcAngle position and extend for arcAngle + * degrees. The degree origin is at the 3 o'clock position. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + * @param arcStart The beginning angle of the arc. + * @param arcAngle The extent of the arc. + */ +public abstract void +drawArc(int x, int y, int width, int height, int arcStart, int arcAngle); + +/*************************************************************************/ + +/** + * Fills the arc define by the specified bounding rectangle and the specified + * angle parameter. The arc is centered at the center of the rectangle. + * The arc starts at the arcAngle position and extend for arcAngle + * degrees. The degree origin is at the 3 o'clock position. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + * @param arcStart The beginning angle of the arc. + * @param arcAngle The extent of the arc. + */ +public abstract void +fillArc(int x, int y, int width, int height, int arcStart, int arcAngle); + +/*************************************************************************/ + +/** + * Draws a series of interconnected lines determined by the arrays + * of corresponding x and y coordinates. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ +public abstract void +drawPolyline(int xPoints[], int yPoints[], int npoints); + +/*************************************************************************/ + +/** + * Draws a series of interconnected lines determined by the arrays + * of corresponding x and y coordinates. The figure is closed if necessary + * by connecting the first and last points. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ +public abstract void +drawPolygon(int xPoints[], int yPoints[], int npoints); + +/*************************************************************************/ + +/** + * Draws the specified polygon. + * + * @param polygon The polygon to draw. + */ +public void +drawPolygon(Polygon polygon) +{ + drawPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints); +} + +/*************************************************************************/ + +/** + * Fills the polygon determined by the arrays + * of corresponding x and y coordinates. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ +public abstract void +fillPolygon(int xPoints[], int yPoints[], int npoints); + +/*************************************************************************/ + +/** + * Fills the specified polygon + * + * @param polygon The polygon to fill. + */ +public void +fillPolygon(Polygon polygon) +{ + fillPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints); +} + +/*************************************************************************/ + +/** + * Draws the specified string starting at the specified point. + * + * @param string The string to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + */ +public abstract void +drawString(String string, int x, int y); + +public abstract void drawString (AttributedCharacterIterator ci, int x, int y); + +/*************************************************************************/ + +/** + * Draws the specified characters starting at the specified point. + * + * @param data The array of characters to draw. + * @param offset The offset into the array to start drawing characters from. + * @param length The number of characters to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + */ +public void +drawChars(char data[], int offset, int length, int x, int y) +{ + drawString(new String(data, offset, length), x, y); +} + +public void +drawBytes(byte[] data, int offset, int length, int x, int y) +{ + String str = new String(data, offset, length); + drawString(str, x, y); +} + +/*************************************************************************/ + +/** + * Draws all of the image that is available and returns. If the image + * is not completely loaded, <code>false</code> is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param observer The image observer to notify as data becomes available. + * + * @return <code>true</code> if all the image data is available, + * <code>false</code> otherwise. + */ +public abstract boolean +drawImage(Image image, int x, int y, ImageObserver observer); + +/*************************************************************************/ + +/** + * Draws all of the image that is available and returns. The image + * is scaled to fit in the specified rectangle. If the image + * is not completely loaded, <code>false</code> is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param width The width of the rectangle to draw in. + * @param height The height of the rectangle to draw in. + * @param observer The image observer to notify as data becomes available. + * + * @return <code>true</code> if all the image data is available, + * <code>false</code> otherwise. + */ +public abstract boolean +drawImage(Image image, int x, int y, int width, int height, + ImageObserver observer); + +/*************************************************************************/ + +/** + * Draws all of the image that is available and returns. If the image + * is not completely loaded, <code>false</code> is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param bgcolor The background color to use for the image. + * @param observer The image observer to notify as data becomes available. + * + * @return <code>true</code> if all the image data is available, + * <code>false</code> otherwise. + */ +public abstract boolean +drawImage(Image image, int x, int y, Color bgcolor, ImageObserver observer); + +/*************************************************************************/ + +/** + * Draws all of the image that is available and returns. The image + * is scaled to fit in the specified rectangle. If the image + * is not completely loaded, <code>false</code> is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param width The width of the rectangle to draw in. + * @param height The height of the rectangle to draw in. + * @param bgcolor The background color to use for the image. + * @param observer The image observer to notify as data becomes available. + * + * @return <code>true</code> if all the image data is available, + * <code>false</code> otherwise. + */ +public abstract boolean +drawImage(Image image, int x, int y, int width, int height, Color bgcolor, + ImageObserver observer); + +/*************************************************************************/ + +/** + * FIXME: Write Javadocs for this when you understand it. + */ +public abstract boolean +drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, ImageObserver observer); + +/*************************************************************************/ + +/** + * FIXME: Write Javadocs for this when you understand it. + */ +public abstract boolean +drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, Color bgcolor, ImageObserver observer); + +/*************************************************************************/ + +/** + * Free any resources held by this graphics context immediately instead + * of waiting for the object to be garbage collected and finalized. + */ +public abstract void +dispose(); + +/*************************************************************************/ + +/** + * Frees the resources held by this graphics context when it is + * garbage collected. + */ +public void +finalize() +{ + dispose(); +} + +/*************************************************************************/ + +/** + * Returns a string representation of this object. + * + * @return A string representation of this object. + */ +public String +toString() +{ + return getClass ().getName () + "[font=" + getFont () + ",color=" + getColor () + "]"; +} + +public boolean +hitClip(int x, int y, int width, int height) +{ + throw new UnsupportedOperationException("not implemented yet"); +} + +public Rectangle +getClipBounds(Rectangle r) +{ + Rectangle clipBounds = getClipBounds(); + + if (r == null) + return clipBounds; + + r.x = clipBounds.x; + r.y = clipBounds.y; + r.width = clipBounds.width; + r.height = clipBounds.height; + return r; +} + +} // class Graphics + diff --git a/libjava/classpath/java/awt/Graphics2D.java b/libjava/classpath/java/awt/Graphics2D.java new file mode 100644 index 00000000000..3faa9dc0c66 --- /dev/null +++ b/libjava/classpath/java/awt/Graphics2D.java @@ -0,0 +1,158 @@ +/* Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class Graphics2D extends Graphics +{ + + protected Graphics2D() + { + } + + public void draw3DRect(int x, int y, int width, int height, + boolean raised) + { + super.draw3DRect(x, y, width, height, raised); + } + + public void fill3DRect(int x, int y, int width, int height, + boolean raised) + { + super.fill3DRect(x, y, width, height, raised); + } + + public abstract void draw(Shape shape); + + public abstract boolean drawImage(Image image, AffineTransform xform, + ImageObserver obs); + + public abstract void drawImage(BufferedImage image, + BufferedImageOp op, + int x, + int y); + + public abstract void drawRenderedImage(RenderedImage image, + AffineTransform xform); + + public abstract void drawRenderableImage(RenderableImage image, + AffineTransform xform); + + public abstract void drawString(String text, int x, int y); + + public abstract void drawString(String text, float x, float y); + + public abstract void drawString(AttributedCharacterIterator iterator, + int x, int y); + + public abstract void drawString(AttributedCharacterIterator iterator, + float x, float y); + + // public abstract void drawGlyphVector(GlyphVector g, float x, float y); + + public abstract void fill(Shape shape); + + public abstract boolean hit(Rectangle rect, Shape text, + boolean onStroke); + + public abstract GraphicsConfiguration getDeviceConfiguration(); + + public abstract void setComposite(Composite comp); + + public abstract void setPaint(Paint paint); + + public abstract void setStroke(Stroke stroke); + + public abstract void setRenderingHint(RenderingHints.Key hintKey, + Object hintValue); + + public abstract Object getRenderingHint(RenderingHints.Key hintKey); + + public abstract void setRenderingHints(Map hints); + + public abstract void addRenderingHints(Map hints); + + public abstract RenderingHints getRenderingHints(); + + public abstract void translate(int x, int y); + + public abstract void translate(double tx, double ty); + + public abstract void rotate(double theta); + + public abstract void rotate(double theta, double x, double y); + + public abstract void scale(double scaleX, double scaleY); + + public abstract void shear(double shearX, double shearY); + + public abstract void transform(AffineTransform Tx); + + public abstract void setTransform(AffineTransform Tx); + + public abstract AffineTransform getTransform(); + + public abstract Paint getPaint(); + + public abstract Composite getComposite(); + + public abstract void setBackground(Color color); + + public abstract Color getBackground(); + + public abstract Stroke getStroke(); + + public abstract void clip(Shape s); + + public abstract FontRenderContext getFontRenderContext (); + + public abstract void drawGlyphVector (GlyphVector g, float x, float y); +} diff --git a/libjava/classpath/java/awt/GraphicsConfigTemplate.java b/libjava/classpath/java/awt/GraphicsConfigTemplate.java new file mode 100644 index 00000000000..e46888378fe --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsConfigTemplate.java @@ -0,0 +1,106 @@ +/* GraphicsConfigTemplate.java -- a template for selecting configurations + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This allows filtering an array of GraphicsConfigurations for the best + * one based on various requirements. The resulting configuration has had + * all non-default attributes set as required to meet or exceed the request. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsConfiguration + * @see GraphicsDevice + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class GraphicsConfigTemplate implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -8061369279557787079L; + + /** States that a feature is required to select a configuration. */ + public static final int REQUIRED = 1; + + /** + * States that a feature is preferred, but not required, to select a + * configuration. In the case of multiple valid configurations, the tie + * breaks in favor of the one with the feature. + */ + public static final int PREFERRED = 2; + + /** + * States that a feature is not necessary in the configuration. In the case + * of multiple valid configurations, the tie breaks in favor of the one + * without the feature, to reduce overhead. + */ + public static final int UNNECESSARY = 3; + + /** + * The default constructor. + */ + public GraphicsConfigTemplate() + { + } + + /** + * Returns the "best" match among the array of possible configurations, given + * the criteria of this template. + * + * @param array the array to choose from + * @return the best match + * @throws NullPointerException if array is null + */ + public abstract GraphicsConfiguration getBestConfiguration + (GraphicsConfiguration[] array); + + /** + * Returns true if the given configuration supports all the features required + * by this template. + * + * @param config the configuration to test + * @return true if it is a match + * @throws NullPointerException if config is null + */ + public abstract boolean isGraphicsConfigSupported + (GraphicsConfiguration config); +} // class GraphicsConfigTemplate diff --git a/libjava/classpath/java/awt/GraphicsConfiguration.java b/libjava/classpath/java/awt/GraphicsConfiguration.java new file mode 100644 index 00000000000..6dc27793d43 --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsConfiguration.java @@ -0,0 +1,218 @@ +/* GraphicsConfiguration.java -- describes characteristics of graphics + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; + +/** + * This class describes the configuration of various graphics devices, such + * as a monitor or printer. Different configurations may exist for the same + * device, according to the different native modes supported. + * + * <p>Virtual devices are supported (for example, in a multiple screen + * environment, a virtual device covers all screens simultaneously); the + * configuration will have a non-zero relative coordinate system in such + * a case. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Window + * @see Frame + * @see GraphicsEnvironment + * @see GraphicsDevice + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class GraphicsConfiguration +{ + /** + * The default constructor. + * + * @see GraphicsDevice#getConfigurations() + * @see GraphicsDevice#getDefaultConfiguration() + * @see GraphicsDevice#getBestConfiguration(GraphicsConfigTemplate) + * @see Graphics2D#getDeviceConfiguration() + */ + protected GraphicsConfiguration () + { + } + + /** + * Gets the associated device that this configuration describes. + * + * @return the device + */ + public abstract GraphicsDevice getDevice(); + + /** + * Returns a buffered image optimized to this device, so that blitting can + * be supported in the buffered image. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @return the buffered image, or null if none is supported + */ + public abstract BufferedImage createCompatibleImage(int w, int h); + + /** + * Returns a buffered volatile image optimized to this device, so that + * blitting can be supported in the buffered image. Because the buffer is + * volatile, it can be optimized by native graphics accelerators. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @return the buffered image, or null if none is supported + * @see Component#createVolatileImage(int, int) + * @since 1.4 + */ + public abstract VolatileImage createCompatibleVolatileImage(int w, int h); + + /** + * Returns a buffered volatile image optimized to this device, and with the + * given capabilities, so that blitting can be supported in the buffered + * image. Because the buffer is volatile, it can be optimized by native + * graphics accelerators. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @param caps the desired capabilities of the image buffer + * @return the buffered image, or null if none is supported + * @throws AWTException if the capabilities cannot be met + * @since 1.4 + */ + public VolatileImage createCompatibleVolatileImage(int w, int h, + ImageCapabilities caps) + throws AWTException + { + throw new AWTException("not implemented"); + } + + /** + * Returns a buffered image optimized to this device, and with the specified + * transparency, so that blitting can be supported in the buffered image. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @param transparency the transparency of the buffer + * @return the buffered image, or null if none is supported + * @see Transparency#OPAQUE + * @see Transparency#BITMASK + * @see Transparency#TRANSLUCENT + */ + public abstract BufferedImage createCompatibleImage(int w, int h, + int transparency); + + /** + * Gets the color model of the corresponding device. + * + * @return the color model + */ + public abstract ColorModel getColorModel(); + + /** + * Gets a color model for the corresponding device which supports the desired + * transparency level. + * + * @param transparency the transparency of the model + * @return the color model, with transparency + * @see Transparency#OPAQUE + * @see Transparency#BITMASK + * @see Transparency#TRANSLUCENT + */ + public abstract ColorModel getColorModel(int transparency); + + /** + * Returns a transform that maps user coordinates to device coordinates. The + * preferred mapping is about 72 user units to 1 inch (2.54 cm) of physical + * space. This is often the identity transform. The device coordinates have + * the origin at the upper left, with increasing x to the right, and + * increasing y to the bottom. + * + * @return the transformation from user space to device space + * @see #getNormalizingTransform() + */ + public abstract AffineTransform getDefaultTransform(); + + /** + * Returns a transform that maps user coordinates to device coordinates. The + * exact mapping is 72 user units to 1 inch (2.54 cm) of physical space. + * This is often the identity transform. The device coordinates have the + * origin at the upper left, with increasing x to the right, and increasing + * y to the bottom. Note that this is more accurate (and thus, sometimes more + * costly) than the default transform. + * + * @return the normalized transformation from user space to device space + * @see #getDefaultTransform() + */ + public abstract AffineTransform getNormalizingTransform(); + + /** + * Returns the bounds of the configuration, in device coordinates. If this + * is a virtual device (for example, encompassing several screens), the + * bounds may have a non-zero origin. + * + * @return the device bounds + * @since 1.3 + */ + public abstract Rectangle getBounds(); + + /** + * Returns the buffering capabilities of this configuration. + * + * @return the buffer capabilities + * @since 1.4 + */ + public BufferCapabilities getBufferCapabilities() + { + throw new Error("not implemented"); + } + + /** + * Returns the imaging capabilities of this configuration. + * + * @return the image capabilities + * @since 1.4 + */ + public ImageCapabilities getImageCapabilities() + { + throw new Error("not implemented"); + } +} // class GraphicsConfiguration diff --git a/libjava/classpath/java/awt/GraphicsDevice.java b/libjava/classpath/java/awt/GraphicsDevice.java new file mode 100644 index 00000000000..95487a2a1cc --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsDevice.java @@ -0,0 +1,292 @@ +/* GraphicsDevice.java -- information about a graphics device + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.VolatileImage; + +/** + * This describes a graphics device available to the given environment. This + * includes screen and printer devices, and the different configurations for + * each device. Also, this allows you to create virtual devices which operate + * over a multi-screen environment. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsEnvironment + * @see GraphicsConfiguration + * @since 1.3 + * @status updated to 1.4 + */ +public abstract class GraphicsDevice +{ + /** Device is a raster screen. */ + public static final int TYPE_RASTER_SCREEN = 0; + + /** Device is a printer. */ + public static final int TYPE_PRINTER = 1; + + /** Device is an image buffer not visible to the user. */ + public static final int TYPE_IMAGE_BUFFER = 2; + + /** The current full-screen window, or null if there is none. */ + private Window full_screen; + + /** + * The bounds of the fullscreen window before it has been switched to full + * screen. + */ + private Rectangle fullScreenOldBounds; + + /** The current display mode, or null if unknown. */ + private DisplayMode mode; + + /** + * The default constructor. + * + * @see GraphicsEnvironment#getScreenDevices() + * @see GraphicsEnvironment#getDefaultScreenDevice() + * @see GraphicsConfiguration#getDevice() + */ + protected GraphicsDevice() + { + } + + /** + * Returns the type of the device. + * + * @return the device type + * @see #TYPE_RASTER_SCREEN + * @see #TYPE_PRINTER + * @see #TYPE_IMAGE_BUFFER + */ + public abstract int getType(); + + /** + * Returns an identification string for the device. This can be + * vendor-specific, and may be useful for debugging. + * + * @return the identification + */ + public abstract String getIDstring(); + + /** + * Return all configurations valid for this device. + * + * @return an array of configurations + */ + public abstract GraphicsConfiguration[] getConfigurations(); + + /** + * Return the default configuration for this device. + * + * @return the default configuration + */ + public abstract GraphicsConfiguration getDefaultConfiguration(); + + /** + * Return the best configuration, according to the criteria in the given + * template. + * + * @param template the template to adjust by + * @return the best configuration + * @throws NullPointerException if template is null + */ + public GraphicsConfiguration getBestConfiguration + (GraphicsConfigTemplate template) + { + return template.getBestConfiguration(getConfigurations()); + } + + /** + * Returns true if the device supports full-screen exclusive mode. The + * default implementation returns true; subclass it if this is not the case. + * + * @return true if full screen support is available + * @since 1.4 + */ + public boolean isFullScreenSupported() + { + return true; + } + + /** + * Toggle the given window between full screen and normal mode. The previous + * full-screen window, if different, is restored; if the given window is + * null, no window will be full screen. If + * <code>isFullScreenSupported()</code> returns true, full screen mode is + * considered to be exclusive, which implies:<ul> + * <li>Windows cannot overlap the full-screen window. All other application + * windows will always appear beneath the full-screen window in the + * Z-order.</li> + * <li>Input method windows are disabled. It is advisable to call + * <code>Component.enableInputMethods(false)</code> to make a component + * a non-client of the input method framework.</li> + * </ul><br> + * If <code>isFullScreenSupported()</code> returns false, full-screen + * exclusive mode is simulated by resizing the window to the size of the + * screen and positioning it at (0,0). This is also what this method does. + * If a device supports real fullscreen mode then it should override this + * method as well as #isFullScreenSupported and #getFullScreenWindow. + * + * @param w the window to toggle + * @see #isFullScreenSupported() + * @see #getFullScreenWindow() + * @see #setDisplayMode(DisplayMode) + * @see Component#enableInputMethods(boolean) + * @since 1.4 + */ + public synchronized void setFullScreenWindow(Window w) + { + // Restore the previous window to normal mode and release the reference. + if (full_screen != null) + { + full_screen.setBounds(fullScreenOldBounds); + } + + full_screen = null; + + // If w != null, make it full-screen. + if (w != null) + { + fullScreenOldBounds = w.getBounds(); + full_screen = w; + DisplayMode dMode = getDisplayMode(); + full_screen.setBounds(0, 0, dMode.getWidth(), dMode.getHeight()); + full_screen.requestFocus(); + full_screen.setLocationRelativeTo(null); + } + } + + /** + * Returns the current full-screen window of the device, or null if no + * window is full-screen. + * + * @return the full-screen window + * @see #setFullScreenWindow(Window) + * @since 1.4 + */ + public Window getFullScreenWindow() + { + return full_screen; + } + + /** + * Returns whether this device supports low-level display changes. This may + * depend on whether full-screen exclusive mode is available. + * + * XXX The default implementation returns false for now. + * + * @return true if display changes are supported + * @see #setDisplayMode(DisplayMode) + * @since 1.4 + */ + public boolean isDisplayChangeSupported() + { + return false; + } + + /** + * Sets the display mode. This may be dependent on the availability of + * full-screen exclusive mode. + * + * @param mode the new mode + * @throws IllegalArgumentException if the new mode is not in getDisplayModes + * @throws UnsupportedOperationException if ! isDisplayChangeSupported() + * @see #getDisplayMode() + * @see #getDisplayModes() + * @see #isDisplayChangeSupported() + * @since 1.4 + */ + public void setDisplayMode(DisplayMode mode) + { + DisplayMode[] array = getDisplayModes(); + if (! isDisplayChangeSupported()) + throw new UnsupportedOperationException(); + int i = array == null ? 0 : array.length; + while (--i >= 0) + if (array[i].equals(mode)) + break; + if (i < 0) + throw new IllegalArgumentException(); + this.mode = mode; + } + + /** + * Returns the current display mode of this device, or null if unknown. + * + * @return the current display mode + * @see #setDisplayMode(DisplayMode) + * @see #getDisplayModes() + * @since 1.4 + */ + public DisplayMode getDisplayMode() + { + return mode; + } + + /** + * Return an array of all available display modes. This implementation + * returns a 0-length array, so subclasses must override this. + * + * @return the array of available modes + * @since 1.4 + */ + public DisplayMode[] getDisplayModes() + { + return new DisplayMode[0]; + } + + /** + * Return the number of bytes available in accelerated memory on this + * device. The device may support creation or caching on a first-come, + * first-served basis, depending on the operating system and driver. + * Memory may be a finite resource, and because of multi-threading, you + * are not guaranteed that the result of this method ensures your image + * will successfully be put in accelerated memory. A negative result means + * the memory is unlimited. The default implementation assumes no special + * memory is available, and returns 0. + * + * @return the size of accelerated memory available + * @see VolatileImage#flush() + * @see ImageCapabilities#isAccelerated() + */ + public int getAvailableAcceleratedMemory() + { + return 0; + } +} // class GraphicsDevice diff --git a/libjava/classpath/java/awt/GraphicsEnvironment.java b/libjava/classpath/java/awt/GraphicsEnvironment.java new file mode 100644 index 00000000000..a82e7a357de --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsEnvironment.java @@ -0,0 +1,244 @@ +/* GraphicsEnvironment.java -- information about the graphics environment + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import gnu.classpath.SystemProperties; +import java.awt.image.BufferedImage; +import java.util.Locale; + +/** + * This descibes the collection of GraphicsDevice and Font objects available + * on a given platform. The resources might be local or remote, and specify + * the valid configurations for displaying graphics. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsDevice + * @see GraphicsConfiguration + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class GraphicsEnvironment +{ + private static GraphicsEnvironment localGraphicsEnvironment; + + /** + * The environment must be obtained from a factory or query method, hence + * this constructor is protected. + */ + protected GraphicsEnvironment() + { + } + + /** + * Returns the local graphics environment. If the java.awt.graphicsenv + * system property is set, it instantiates the specified class, + * otherwise it assume that the awt toolkit is a ClasspathToolkit + * and delegates to it to create the instance. + * + * @return the local environment + */ + public static GraphicsEnvironment getLocalGraphicsEnvironment() + { + if (localGraphicsEnvironment != null) + return localGraphicsEnvironment; + + String graphicsenv = SystemProperties.getProperty("java.awt.graphicsenv", + null); + if (graphicsenv != null) + { + try + { + // We intentionally use the bootstrap class loader. + localGraphicsEnvironment = (GraphicsEnvironment) + Class.forName(graphicsenv).newInstance(); + return localGraphicsEnvironment; + } + catch (Exception x) + { + throw (InternalError) + new InternalError("Unable to instantiate java.awt.graphicsenv") + .initCause(x); + } + } + else + { + ClasspathToolkit tk; + tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit()); + localGraphicsEnvironment = tk.getLocalGraphicsEnvironment(); + return localGraphicsEnvironment; + } + } + + /** + * Check if the local environment is headless, meaning that it does not + * support a display, keyboard, or mouse. Many methods in the Abstract + * Windows Toolkit (java.awt) throw a {@link HeadlessException} if this + * returns true. + * + * This method returns true if the java.awt.headless property is set + * to "true". + * + * @return true if the environment is headless, meaning that graphics are + * unsupported + * @since 1.4 + */ + public static boolean isHeadless() + { + String headless = SystemProperties.getProperty("java.awt.headless", null); + return "true".equalsIgnoreCase(headless); + } + + /** + * Check if the given environment is headless, meaning that it does not + * support a display, keyboard, or mouse. Many methods in the Abstract + * Windows Toolkit (java.awt) throw a {@link HeadlessException} if this + * returns true. This default implementation returns isHeadless(), so + * subclasses need only override it if they differ. + * + * @return true if the environment is headless, meaning that graphics are + * unsupported + * @since 1.4 + */ + public boolean isHeadlessInstance() + { + return isHeadless(); + } + + /** + * Get an array of all the GraphicsDevice objects. + * + * @return the available graphics devices, may be 0 length + * @throws HeadlessException if the environment is headless + */ + public abstract GraphicsDevice[] getScreenDevices(); + + /** + * Get the default screen GraphicsDevice object. + * + * @return the default screen device + * @throws HeadlessException if the environment is headless + */ + public abstract GraphicsDevice getDefaultScreenDevice(); + + /** + * Return a Graphics2D object which will render into the specified image. + * + * @param image the image to render into + * @return the object that renders into the image + */ + public abstract Graphics2D createGraphics(BufferedImage image); + + /** + * Returns an array of the one-point size fonts available in this + * environment. From there, the user can select the font and derive the + * correct one of proper size and attributes, using <code>deriveFont</code>. + * Only one master version of each font appears in this array; if a font + * can be derived from another, it must be created in that way. + * + * @return the array of available fonts + * @see #getAvailableFontFamilyNames() + * @see Font#deriveFont(int, float) + * @since 1.2 + */ + public abstract Font[] getAllFonts(); + + /** + * Returns an array of the font family names available in this environment. + * This allows flexibility in choosing the style of font, while still letting + * the Font class decide its best match. + * + * @return the array of available font families + * @see #getAllFonts() + * @see Font#getFamily() + * @since 1.2 + */ + public abstract String[] getAvailableFontFamilyNames(); + + /** + * Returns an array of the font family names available in this environment, + * localized to the current Locale if l is non-null. This allows + * flexibility in choosing the style of font, while still letting the Font + * class decide its best match. + * + * @param l the locale to use + * @return the array of available font families, localized + * @see #getAllFonts() + * @see Font#getFamily() + * @since 1.2 + */ + public abstract String[] getAvailableFontFamilyNames(Locale l); + + /** + * Returns the point where a window should be centered. You should probably + * also check that the window fits within the screen bounds. The default + * simply returns the center of the maximum window bounds; subclasses should + * override this if native objects (like scrollbars) make that off-centered. + * + * @return the centering point + * @throws HeadlessException if the environment is headless + * @see #getMaximumWindowBounds() + * @since 1.4 + */ + public Point getCenterPoint() + { + Rectangle r = getMaximumWindowBounds(); + return new Point(r.x + r.width / 2, r.y + r.height / 2); + } + + /** + * Returns the maximum bounds for a centered window object. The default + * implementation simply returns the bounds of the default configuration + * of the default screen; subclasses should override this to if native + * objects (like scrollbars) reduce what is truly available. Also, + * subclasses should override this if the window should be centered across + * a multi-screen display. + * + * @return the maximum window bounds + * @throws HeadlessException if the environment is headless + * @see #getCenterPoint() + * @see GraphicsConfiguration#getBounds() + * @see Toolkit#getScreenInsets(GraphicsConfiguration) + * @since 1.4 + */ + public Rectangle getMaximumWindowBounds() + { + return getDefaultScreenDevice().getDefaultConfiguration().getBounds(); + } +} // class GraphicsEnvironment diff --git a/libjava/classpath/java/awt/GridBagConstraints.java b/libjava/classpath/java/awt/GridBagConstraints.java new file mode 100644 index 00000000000..8d8b4fae534 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagConstraints.java @@ -0,0 +1,195 @@ +/* GridBagConstraints.java -- Constraints for GridBag layout manager + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This specifies the constraints for a component managed by the + * GridBagLayout layout manager. + */ +public class GridBagConstraints implements Cloneable, Serializable +{ + static final long serialVersionUID = -1000070633030801713L; + + /** Fill in both directions. */ + public static final int BOTH = 1; + /** Don't fill. */ + public static final int NONE = 0; + /** Fill horizontally. */ + public static final int HORIZONTAL = 2; + /** Fill vertically. */ + public static final int VERTICAL = 3; + + /** Position in the center. */ + public static final int CENTER = 10; + /** Position to the east. */ + public static final int EAST = 13; + /** Position to the north. */ + public static final int NORTH = 11; + /** Position to the northeast. */ + public static final int NORTHEAST = 12; + /** Position to the northwest. */ + public static final int NORTHWEST = 18; + /** Position to the south. */ + public static final int SOUTH = 15; + /** Position to the southeast. */ + public static final int SOUTHEAST = 14; + /** Position to the southwest. */ + public static final int SOUTHWEST = 16; + /** Position to the west. */ + public static final int WEST = 17; + + /** Occupy all remaining cells except last cell. */ + public static final int RELATIVE = -1; + /** Occupy all remaining cells. */ + public static final int REMAINDER = 0; + + /** + * Position to where the first text line would end. Equals to NORTHEAST for + * horizontal left-to-right orientations. + */ + public static final int FIRST_LINE_END = 24; + + /** + * Position to where the first text line would start. Equals to NORTHWEST for + * horizontal left-to-right orientations. + */ + public static final int FIRST_LINE_START = 23; + + /** + * Position to where the last text line would end. Equals to SOUTHEAST for + * horizontal left-to-right orientations. + */ + public static final int LAST_LINE_END = 26; + + /** + * Position to where the last text line would start. Equals to SOUTHWEST for + * horizontal left-to-right orientations. + */ + public static final int LAST_LINE_START = 25; + + /** + * Position to where a text line would end. Equals to EAST for + * left-to-right orientations. + */ + public static final int LINE_END = 22; + + /** + * Position to where a text line would start. Equals to WEST for + * left-to-right orientations. + */ + public static final int LINE_START = 21; + + /** + * Position to where a page ends. Equals SOUTH for horizontal orientations. + */ + public static final int PAGE_END = 20; + + /** + * Position to where a page starts. Equals NORTH for horizontal orientations. + */ + public static final int PAGE_START = 19; + + public int anchor; + public int fill; + public int gridheight; + public int gridwidth; + public int gridx; + public int gridy; + public Insets insets; + public int ipadx; + public int ipady; + public double weightx; + public double weighty; + + /** Create a copy of this object. */ + public Object clone () + { + try + { + GridBagConstraints g = (GridBagConstraints) super.clone (); + g.insets = (Insets) insets.clone (); + return g; + } + catch (CloneNotSupportedException _) + { + // Can't happen. + return null; + } + } + + /** Create a new GridBagConstraints object with the default + * parameters. */ + public GridBagConstraints () + { + this.anchor = CENTER; + this.fill = NONE; + this.gridx = RELATIVE; + this.gridy = RELATIVE; + this.gridwidth = 1; + this.gridheight = 1; + this.ipadx = 0; + this.ipady = 0; + this.insets = new Insets (0, 0, 0, 0); + this.weightx = 0; + this.weighty = 0; + } + + /** Create a new GridBagConstraints object with the indicated + * parameters. */ + public GridBagConstraints (int gridx, int gridy, + int gridwidth, int gridheight, + double weightx, double weighty, + int anchor, int fill, + Insets insets, int ipadx, int ipady) + { + this.anchor = anchor; + this.fill = fill; + this.gridx = gridx; + this.gridy = gridy; + this.gridwidth = gridwidth; + this.gridheight = gridheight; + this.ipadx = ipadx; + this.ipady = ipady; + this.insets = insets; + this.weightx = weightx; + this.weighty = weighty; + } +} diff --git a/libjava/classpath/java/awt/GridBagLayout.java b/libjava/classpath/java/awt/GridBagLayout.java new file mode 100644 index 00000000000..767610c5693 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagLayout.java @@ -0,0 +1,1069 @@ +/* GridBagLayout - Layout manager for components according to GridBagConstraints + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @author Jeroen Frijters (jeroen@frijters.net) + */ +public class GridBagLayout + implements Serializable, LayoutManager2 +{ + private static final long serialVersionUID = 8838754796412211005L; + + protected static final int MINSIZE = 1; + protected static final int PREFERREDSIZE = 2; + protected static final int MAXGRIDSIZE = 512; + + // comptable remembers the original contraints given to us. + // internalcomptable is used to keep track of modified constraint values + // that we calculate, particularly when we are given RELATIVE and + // REMAINDER constraints. + // Constraints kept in comptable are never modified, and constraints + // kept in internalcomptable can be modified internally only. + protected Hashtable comptable; + private Hashtable internalcomptable; + protected GridBagLayoutInfo layoutInfo; + protected GridBagConstraints defaultConstraints; + + public double[] columnWeights; + public int[] columnWidths; + public double[] rowWeights; + public int[] rowHeights; + + public GridBagLayout () + { + this.comptable = new Hashtable(); + this.internalcomptable = new Hashtable(); + this.defaultConstraints= new GridBagConstraints(); + } + + /** + * Helper method to calc the sum of a range of elements in an int array. + */ + private int sumIntArray (int[] array, int upto) + { + int result = 0; + + for (int i = 0; i < upto; i++) + result += array [i]; + + return result; + } + + /** + * Helper method to calc the sum of all elements in an int array. + */ + private int sumIntArray (int[] array) + { + return sumIntArray(array, array.length); + } + + /** + * Helper method to calc the sum of all elements in an double array. + */ + private double sumDoubleArray (double[] array) + { + double result = 0; + + for (int i = 0; i < array.length; i++) + result += array [i]; + + return result; + } + + public void addLayoutComponent (String name, Component component) + { + // do nothing here. + } + + public void removeLayoutComponent (Component component) + { + // do nothing here + } + + public void addLayoutComponent (Component component, Object constraints) + { + if (constraints == null) + return; + + if (!(constraints instanceof GridBagConstraints)) + throw new IllegalArgumentException("constraints " + + constraints + + " are not an instance of GridBagConstraints"); + + setConstraints (component, (GridBagConstraints) constraints); + } + + public Dimension preferredLayoutSize (Container parent) + { + if (parent == null) + return new Dimension (0, 0); + + GridBagLayoutInfo li = getLayoutInfo (parent, PREFERREDSIZE); + return getMinSize (parent, li); + } + + public Dimension minimumLayoutSize (Container parent) + { + if (parent == null) + return new Dimension (0, 0); + + GridBagLayoutInfo li = getLayoutInfo (parent, MINSIZE); + return getMinSize (parent, li); + } + + public Dimension maximumLayoutSize (Container target) + { + return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public void layoutContainer (Container parent) + { + arrangeGrid (parent); + } + + public float getLayoutAlignmentX (Container target) + { + return Component.CENTER_ALIGNMENT; + } + + public float getLayoutAlignmentY (Container target) + { + return Component.CENTER_ALIGNMENT; + } + + public void invalidateLayout (Container target) + { + this.layoutInfo = null; + } + + public void setConstraints (Component component, + GridBagConstraints constraints) + { + GridBagConstraints clone = (GridBagConstraints) constraints.clone(); + + if (clone.gridx < 0) + clone.gridx = GridBagConstraints.RELATIVE; + + if (clone.gridy < 0) + clone.gridy = GridBagConstraints.RELATIVE; + + if (clone.gridwidth == 0) + clone.gridwidth = GridBagConstraints.REMAINDER; + else if (clone.gridwidth < 0 + && clone.gridwidth != GridBagConstraints.REMAINDER + && clone.gridwidth != GridBagConstraints.RELATIVE) + clone.gridwidth = 1; + + if (clone.gridheight == 0) + clone.gridheight = GridBagConstraints.REMAINDER; + else if (clone.gridheight < 0 + && clone.gridheight != GridBagConstraints.REMAINDER + && clone.gridheight != GridBagConstraints.RELATIVE) + clone.gridheight = 1; + + comptable.put (component, clone); + } + + public GridBagConstraints getConstraints (Component component) + { + return (GridBagConstraints) (lookupConstraints (component).clone()); + } + + protected GridBagConstraints lookupConstraints (Component component) + { + GridBagConstraints result = (GridBagConstraints) comptable.get (component); + + if (result == null) + { + setConstraints (component, defaultConstraints); + result = (GridBagConstraints) comptable.get (component); + } + + return result; + } + + private GridBagConstraints lookupInternalConstraints (Component component) + { + GridBagConstraints result = + (GridBagConstraints) internalcomptable.get (component); + + if (result == null) + { + result = (GridBagConstraints) lookupConstraints(component).clone(); + internalcomptable.put (component, result); + } + + return result; + } + + /** + * @since 1.1 + */ + public Point getLayoutOrigin () + { + if (layoutInfo == null) + return new Point (0, 0); + + return new Point (layoutInfo.pos_x, layoutInfo.pos_y); + } + + /** + * @since 1.1 + */ + public int[][] getLayoutDimensions () + { + int[][] result = new int [2][]; + if (layoutInfo == null) + { + result[0] = new int[0]; + result[1] = new int[0]; + + return result; + } + + result [0] = new int [layoutInfo.cols]; + System.arraycopy (layoutInfo.colWidths, 0, result [0], 0, layoutInfo.cols); + result [1] = new int [layoutInfo.rows]; + System.arraycopy (layoutInfo.rowHeights, 0, result [1], 0, layoutInfo.rows); + return result; + } + + public double[][] getLayoutWeights () + { + double[][] result = new double [2][]; + if (layoutInfo == null) + { + result[0] = new double[0]; + result[1] = new double[0]; + + return result; + } + + result [0] = new double [layoutInfo.cols]; + System.arraycopy (layoutInfo.colWeights, 0, result [0], 0, layoutInfo.cols); + result [1] = new double [layoutInfo.rows]; + System.arraycopy (layoutInfo.rowWeights, 0, result [1], 0, layoutInfo.rows); + return result; + } + + /** + * @since 1.1 + */ + public Point location (int x, int y) + { + if (layoutInfo == null) + return new Point (0, 0); + + int col; + int row; + int pixel_x = layoutInfo.pos_x; + int pixel_y = layoutInfo.pos_y; + + for (col = 0; col < layoutInfo.cols; col++) + { + int w = layoutInfo.colWidths [col]; + if (x < pixel_x + w) + break; + + pixel_x += w; + } + + for (row = 0; row < layoutInfo.rows; row++) + { + int h = layoutInfo.rowHeights [row]; + if (y < pixel_y + h) + break; + + pixel_y += h; + } + + return new Point (col, row); + } + + /** + * Obsolete. + */ + protected void AdjustForGravity (GridBagConstraints gbc, Rectangle rect) + { + // FIXME + throw new Error ("Not implemented"); + } + + /** + * Obsolete. + */ + protected void ArrangeGrid (Container parent) + { + Component[] components = parent.getComponents(); + + if (components.length == 0) + return; + + GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE); + if (info.cols == 0 && info.rows == 0) + return; + layoutInfo = info; + + // DEBUG + //dumpLayoutInfo (layoutInfo); + + for(int i = 0; i < components.length; i++) + { + Component component = components [i]; + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = + lookupInternalConstraints(component); + + int cellx = sumIntArray(layoutInfo.colWidths, constraints.gridx); + int celly = sumIntArray(layoutInfo.rowHeights, constraints.gridy); + int cellw = sumIntArray(layoutInfo.colWidths, + constraints.gridx + constraints.gridwidth) - cellx; + int cellh = sumIntArray(layoutInfo.rowHeights, + constraints.gridy + constraints.gridheight) - celly; + + Insets insets = constraints.insets; + if (insets != null) + { + cellx += insets.left; + celly += insets.top; + cellw -= insets.left + insets.right; + cellh -= insets.top + insets.bottom; + } + + Dimension dim = component.getPreferredSize(); + + // Note: Documentation says that padding is added on both sides, but + // visual inspection shows that the Sun implementation only adds it + // once, so we do the same. + dim.width += constraints.ipadx; + dim.height += constraints.ipady; + + switch(constraints.fill) + { + case GridBagConstraints.HORIZONTAL: + dim.width = cellw; + break; + case GridBagConstraints.VERTICAL: + dim.height = cellh; + break; + case GridBagConstraints.BOTH: + dim.width = cellw; + dim.height = cellh; + break; + } + + int x; + int y; + + switch(constraints.anchor) + { + case GridBagConstraints.NORTH: + x = cellx + (cellw - dim.width) / 2; + y = celly; + break; + case GridBagConstraints.SOUTH: + x = cellx + (cellw - dim.width) / 2; + y = celly + cellh - dim.height; + break; + case GridBagConstraints.WEST: + x = cellx; + y = celly + (cellh - dim.height) / 2; + break; + case GridBagConstraints.EAST: + x = cellx + cellw - dim.width; + y = celly + (cellh - dim.height) / 2; + break; + case GridBagConstraints.NORTHEAST: + x = cellx + cellw - dim.width; + y = celly; + break; + case GridBagConstraints.NORTHWEST: + x = cellx; + y = celly; + break; + case GridBagConstraints.SOUTHEAST: + x = cellx + cellw - dim.width; + y = celly + cellh - dim.height; + break; + case GridBagConstraints.SOUTHWEST: + x = cellx; + y = celly + cellh - dim.height; + break; + default: + x = cellx + (cellw - dim.width) / 2; + y = celly + (cellh - dim.height) / 2; + break; + } + + component.setBounds(layoutInfo.pos_x + x, layoutInfo.pos_y + y, dim.width, dim.height); + } + + // DEBUG + //dumpLayoutInfo (layoutInfo); + } + + /** + * Obsolete. + */ + protected GridBagLayoutInfo GetLayoutInfo (Container parent, int sizeflag) + { + if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE) + throw new IllegalArgumentException(); + + Dimension parentDim = parent.getSize (); + Insets parentInsets = parent.getInsets (); + parentDim.width -= parentInsets.left + parentInsets.right; + parentDim.height -= parentInsets.top + parentInsets.bottom; + + int current_y = 0; + int max_x = 0; + int max_y = 0; + + // Guaranteed to contain the last component added to the given row + // or column, whose gridwidth/height is not REMAINDER. + HashMap lastInRow = new HashMap(); + HashMap lastInCol = new HashMap(); + + Component[] components = parent.getComponents(); + + // Components sorted by gridwidths/heights, + // smallest to largest, with REMAINDER and RELATIVE at the end. + // These are useful when determining sizes and weights. + ArrayList sortedByWidth = new ArrayList(components.length); + ArrayList sortedByHeight = new ArrayList(components.length); + + // STEP 1: first we figure out how many rows/columns + for (int i = 0; i < components.length; i++) + { + Component component = components [i]; + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + // When looking up the constraint for the first time, check the + // original unmodified constraint. After the first time, always + // refer to the internal modified constraint. + GridBagConstraints originalConstraints = lookupConstraints (component); + GridBagConstraints constraints = (GridBagConstraints) originalConstraints.clone(); + internalcomptable.put(component, constraints); + + // Cases: + // + // 1. gridy == RELATIVE, gridx == RELATIVE + // + // use y as the row number; check for the next + // available slot at row y + // + // 2. only gridx == RELATIVE + // + // check for the next available slot at row gridy + // + // 3. only gridy == RELATIVE + // + // check for the next available slot at column gridx + // + // 4. neither gridx or gridy == RELATIVE + // + // nothing to check; just add it + + + // cases 1 and 2 + if(constraints.gridx == GridBagConstraints.RELATIVE) + { + if (constraints.gridy == GridBagConstraints.RELATIVE) + constraints.gridy = current_y; + + int x; + + // Check the component that occupies the right-most spot in this + // row. We want to add this component after it. + // If this row is empty, add to the 0 position. + if (!lastInRow.containsKey(new Integer(constraints.gridy))) + x = 0; + else + { + Component lastComponent = (Component) lastInRow.get(new Integer(constraints.gridy)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + x = lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth); + } + + // Determine if this component will fit in the slot vertically. + // If not, bump it over to where it does fit. + for (int y = constraints.gridy + 1; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if (lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = (Component) lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + x = Math.max (x, + lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth)); + } + } + + constraints.gridx = x; + } + // case 3 + else if(constraints.gridy == GridBagConstraints.RELATIVE) + { + int y; + // Check the component that occupies the bottom-most spot in + // this column. We want to add this component below it. + // If this column is empty, add to the 0 position. + if (!lastInCol.containsKey(new Integer(constraints.gridx))) + y = 0; + else + { + Component lastComponent = (Component)lastInCol.get(new Integer(constraints.gridx)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + y = lastConstraints.gridy + Math.max(1, lastConstraints.gridheight); + } + + // Determine if this component will fit in the slot horizontally. + // If not, bump it down to where it does fit. + for (int x = constraints.gridx + 1; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if (lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = (Component) lastInCol.get(new Integer(x)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + y = Math.max (y, + lastConstraints.gridy + Math.max(1, lastConstraints.gridheight)); + } + } + + constraints.gridy = y; + } + // case 4: do nothing + + max_x = Math.max(max_x, + constraints.gridx + Math.max(1, constraints.gridwidth)); + max_y = Math.max(max_y, + constraints.gridy + Math.max(1, constraints.gridheight)); + + sortBySpan(component, constraints.gridwidth, sortedByWidth, true); + sortBySpan(component, constraints.gridheight, sortedByHeight, false); + + // Update our reference points for RELATIVE gridx and gridy. + if(constraints.gridwidth == GridBagConstraints.REMAINDER) + { + current_y = constraints.gridy + Math.max(1, constraints.gridheight); + } + else if (constraints.gridwidth != GridBagConstraints.REMAINDER) + { + for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if(lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = (Component) lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + if (constraints.gridx > lastConstraints.gridx) + { + lastInRow.put(new Integer(y), component); + } + } + else + { + lastInRow.put(new Integer(y), component); + } + } + + for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if(lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = (Component) lastInCol.get(new Integer(x)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + if (constraints.gridy > lastConstraints.gridy) + { + lastInCol.put(new Integer(x), component); + } + } + else + { + lastInCol.put(new Integer(x), component); + } + } + } + } // end of STEP 1 + + GridBagLayoutInfo info = new GridBagLayoutInfo(max_x, max_y); + + // Check if column widths and row heights are overridden. + + for (int x = 0; x < max_x; x++) + { + if(columnWidths != null && columnWidths.length > x) + info.colWidths[x] = columnWidths[x]; + if(columnWeights != null && columnWeights.length > x) + info.colWeights[x] = columnWeights[x]; + } + + for (int y = 0; y < max_y; y++) + { + if(rowHeights != null && rowHeights.length > y) + info.rowHeights[y] = rowHeights[y]; + if(rowWeights != null && rowWeights.length > y) + info.rowWeights[y] = rowWeights[y]; + } + + // STEP 2: Fix up any cells with width/height as REMAINDER/RELATIVE. + for (int i = 0; i < components.length; i++) + { + Component component = components [i]; + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + if(constraints.gridwidth == GridBagConstraints.REMAINDER || constraints.gridwidth == GridBagConstraints.RELATIVE) + { + if(constraints.gridwidth == GridBagConstraints.REMAINDER) + { + for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if (lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = (Component) lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + + if (lastConstraints.gridwidth == GridBagConstraints.RELATIVE) + { + constraints.gridx = max_x - 1; + break; + } + else + { + constraints.gridx = Math.max (constraints.gridx, + lastConstraints.gridx + Math.max (1, lastConstraints.gridwidth)); + } + } + } + constraints.gridwidth = max_x - constraints.gridx; + } + else if (constraints.gridwidth == GridBagConstraints.RELATIVE) + { + constraints.gridwidth = max_x - constraints.gridx - 1; + } + + // Re-sort + sortedByWidth.remove(sortedByWidth.indexOf(component)); + sortBySpan(component, constraints.gridwidth, sortedByWidth, true); + } + + if(constraints.gridheight == GridBagConstraints.REMAINDER || constraints.gridheight == GridBagConstraints.RELATIVE) + { + if(constraints.gridheight == GridBagConstraints.REMAINDER) + { + for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if (lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = (Component) lastInRow.get(new Integer(x)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + + if (lastConstraints.gridheight == GridBagConstraints.RELATIVE) + { + constraints.gridy = max_y - 1; + break; + } + else + { + constraints.gridy = Math.max (constraints.gridy, + lastConstraints.gridy + Math.max (1, lastConstraints.gridheight)); + } + } + } + constraints.gridheight = max_y - constraints.gridy; + } + else if (constraints.gridheight == GridBagConstraints.RELATIVE) + { + constraints.gridheight = max_y - constraints.gridy - 1; + } + + // Re-sort + sortedByHeight.remove(sortedByHeight.indexOf(component)); + sortBySpan(component, constraints.gridheight, sortedByHeight, false); + } + } // end of STEP 2 + + // STEP 3: Determine sizes and weights for columns. + for (int i = 0; i < sortedByWidth.size(); i++) + { + Component component = (Component) sortedByWidth.get(i); + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + int width = (sizeflag == PREFERREDSIZE) ? + component.getPreferredSize().width : + component.getMinimumSize().width; + + if(constraints.insets != null) + width += constraints.insets.left + constraints.insets.right; + + width += constraints.ipadx; + + distributeSizeAndWeight(width, + constraints.weightx, + constraints.gridx, + constraints.gridwidth, + info.colWidths, + info.colWeights); + } // end of STEP 3 + + // STEP 4: Determine sizes and weights for rows. + for (int i = 0; i < sortedByHeight.size(); i++) + { + Component component = (Component) sortedByHeight.get(i); + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + int height = (sizeflag == PREFERREDSIZE) ? + component.getPreferredSize().height : + component.getMinimumSize().height; + + if(constraints.insets != null) + height += constraints.insets.top + constraints.insets.bottom; + + height += constraints.ipady; + + distributeSizeAndWeight(height, + constraints.weighty, + constraints.gridy, + constraints.gridheight, + info.rowHeights, + info.rowWeights); + } // end of STEP 4 + + // Adjust cell sizes iff parent size not zero. + if (parentDim.width > 0 && parentDim.height > 0) + { + calcCellSizes (info.colWidths, info.colWeights, parentDim.width); + calcCellSizes (info.rowHeights, info.rowWeights, parentDim.height); + } + + int totalWidth = sumIntArray(info.colWidths); + int totalHeight = sumIntArray(info.rowHeights); + + // Make sure pos_x and pos_y are never negative. + if (totalWidth >= parentDim.width) + info.pos_x = parentInsets.left; + else + info.pos_x = parentInsets.left + (parentDim.width - totalWidth) / 2; + + if (totalHeight >= parentDim.height) + info.pos_y = parentInsets.top; + else + info.pos_y = parentInsets.top + (parentDim.height - totalHeight) / 2; + + // DEBUG + //dumpLayoutInfo (info); + + return info; + } + + /** + * Obsolete. + */ + protected Dimension GetMinSize (Container parent, GridBagLayoutInfo info) + { + if (parent == null || info == null) + return new Dimension (0, 0); + + Insets insets = parent.getInsets(); + int width = sumIntArray (info.colWidths) + insets.left + insets.right; + int height = sumIntArray (info.rowHeights) + insets.top + insets.bottom; + return new Dimension (width, height); + } + + /** + * @since 1.4 + */ + protected Dimension getMinSize (Container parent, GridBagLayoutInfo info) + { + return GetMinSize (parent, info); + } + + /** + * Helper method used by GetLayoutInfo to keep components sorted, either + * by gridwidth or gridheight. + * + * @param component Component to add to the sorted list. + * @param span Either the component's gridwidth or gridheight. + * @param list <code>ArrayList</code> of components, sorted by + * their span. + * @param sortByWidth Flag indicating sorting index. If true, sort by + * width. Otherwise, sort by height. + * FIXME: Use a better sorting algorithm. + */ + private void sortBySpan (Component component, int span, ArrayList list, boolean sortByWidth) + { + if (span == GridBagConstraints.REMAINDER + || span == GridBagConstraints.RELATIVE) + { + // Put all RELATIVE and REMAINDER components at the end. + list.add(component); + } + else + { + int i = 0; + if (list.size() > 0) + { + GridBagConstraints gbc = lookupInternalConstraints((Component) list.get(i)); + int otherspan = sortByWidth ? + gbc.gridwidth : + gbc.gridheight; + while (otherspan != GridBagConstraints.REMAINDER + && otherspan != GridBagConstraints.RELATIVE + && span >= otherspan) + { + i++; + if (i < list.size()) + { + gbc = lookupInternalConstraints((Component) list.get(i)); + otherspan = sortByWidth ? + gbc.gridwidth : + gbc.gridheight; + } + else + break; + } + } + list.add(i, component); + } + } + + /** + * Helper method used by GetLayoutInfo to distribute a component's size + * and weight. + * + * @param size Preferred size of component, with inset and padding + * already added. + * @param weight Weight of component. + * @param start Starting position of component. Either + * constraints.gridx or gridy. + * @param span Span of component. either contraints.gridwidth or + * gridheight. + * @param sizes Sizes of rows or columns. + * @param weights Weights of rows or columns. + */ + private void distributeSizeAndWeight (int size, double weight, + int start, int span, + int[] sizes, double[] weights) + { + if (span == 1) + { + sizes[start] = Math.max(sizes[start], size); + weights[start] = Math.max(weights[start], weight); + } + else + { + int numOccupied = span; + int lastOccupied = -1; + + for(int i = start; i < start + span; i++) + { + if (sizes[i] == 0.0) + numOccupied--; + else + { + size -= sizes[i]; + lastOccupied = i; + } + } + + // A component needs to occupy at least one row. + if(numOccupied == 0) + sizes[start + span - 1] = size; + else if (size > 0) + sizes[lastOccupied] += size; + + calcCellWeights(weight, weights, start, span); + } + } + + /** + * Helper method used by GetLayoutInfo to calculate weight distribution. + * @param weight Weight of component. + * @param weights Weights of rows/columns. + * @param start Starting position of component in grid (gridx/gridy). + * @param span Span of component (gridwidth/gridheight). + */ + private void calcCellWeights (double weight, double[] weights, int start, int span) + { + double totalWeight = 0.0; + for(int k = start; k < start + span; k++) + totalWeight += weights[k]; + + if(weight > totalWeight) + { + if (totalWeight == 0.0) + { + weights[start + span - 1] += weight; + } + else + { + double diff = weight - totalWeight ; + double remaining = diff; + + for(int k = start; k < start + span; k++) + { + double extraWeight = diff * weights[k] / totalWeight; + weights[k] += extraWeight; + remaining -= extraWeight; + } + + if (remaining > 0.0 && weights[start + span - 1] != 0.0) + { + weights[start + span - 1] += remaining; + } + } + } + } + + /** + * Helper method used by GetLayoutInfo to distribute extra space + * based on weight distribution. + * + * @param sizes Sizes of rows/columns. + * @param weights Weights of rows/columns. + * @param range Dimension of container. + */ + private void calcCellSizes (int[] sizes, double[] weights, int range) + { + int totalSize = sumIntArray (sizes); + double totalWeight = sumDoubleArray (weights); + + int diff = range - totalSize; + + if (diff == 0) + return; + + for (int i = 0; i < sizes.length; i++) + { + int newsize = (int) (sizes[i] + (((double) diff) * weights [i] / totalWeight )); + + if (newsize > 0) + sizes[i] = newsize; + } + } + + private void dumpLayoutInfo (GridBagLayoutInfo info) + { + System.out.println ("GridBagLayoutInfo:"); + System.out.println ("cols: " + info.cols + ", rows: " + info.rows); + System.out.print ("colWidths: "); + dumpArray(info.colWidths); + System.out.print ("rowHeights: "); + dumpArray(info.rowHeights); + System.out.print ("colWeights: "); + dumpArray(info.colWeights); + System.out.print ("rowWeights: "); + dumpArray(info.rowWeights); + } + + private void dumpArray(int[] array) + { + String sep = ""; + for(int i = 0; i < array.length; i++) + { + System.out.print(sep); + System.out.print(array[i]); + sep = ", "; + } + System.out.println(); + } + + private void dumpArray(double[] array) + { + String sep = ""; + for(int i = 0; i < array.length; i++) + { + System.out.print(sep); + System.out.print(array[i]); + sep = ", "; + } + System.out.println(); + } + + /** + * @since 1.4 + */ + protected void arrangeGrid (Container parent) + { + ArrangeGrid (parent); + } + + /** + * @since 1.4 + */ + protected GridBagLayoutInfo getLayoutInfo (Container parent, int sizeflag) + { + return GetLayoutInfo (parent, sizeflag); + } + + /** + * @since 1.4 + */ + protected void adjustForGravity (GridBagConstraints gbc, Rectangle rect) + { + AdjustForGravity (gbc, rect); + } +} diff --git a/libjava/classpath/java/awt/GridBagLayoutInfo.java b/libjava/classpath/java/awt/GridBagLayoutInfo.java new file mode 100644 index 00000000000..43ba09d72c1 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagLayoutInfo.java @@ -0,0 +1,70 @@ +/* GridBagLayoutInfo - + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +class GridBagLayoutInfo implements Serializable +{ + private static final long serialVersionUID = -4899416460737170217L; + + int pos_x; + int pos_y; + int cols; + int rows; + int colWidths[]; + int rowHeights[]; + double colWeights[]; + double rowWeights[]; + + GridBagLayoutInfo (int cols, int rows) + { + this.pos_x = 0; + this.pos_y = 0; + this.cols = cols; + this.rows = rows; + this.colWidths = new int [cols]; + this.rowHeights = new int [rows]; + this.colWeights = new double [cols]; + this.rowWeights = new double [rows]; + } +} diff --git a/libjava/classpath/java/awt/GridLayout.java b/libjava/classpath/java/awt/GridLayout.java new file mode 100644 index 00000000000..80d96414249 --- /dev/null +++ b/libjava/classpath/java/awt/GridLayout.java @@ -0,0 +1,360 @@ +/* GridLayout.java -- Grid-based layout engine + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** This class implements a grid-based layout scheme. Components are + * all given the same size and are laid out from left to right and top + * to bottom. A GridLayout is configured with a number of rows and a + * number of columns. If both are specified, then the number of + * columns is ignored and is derived from the number of rows and the + * total number of components. If either is zero then that dimension + * is computed based on the actual size of the container. An + * exception is thrown if an attempt is made to set both the number of + * rows and the number of columns to 0. This class also supports + * horizontal and vertical gaps; these are used as spacing between + * cells. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class GridLayout implements LayoutManager, Serializable +{ + static final long serialVersionUID = -7411804673224730901L; + + /** Add a new component to the layout. This particular implementation + * does nothing. + * @param name The name of the component to add. + * @param comp The component to add. + */ + public void addLayoutComponent (String name, Component comp) + { + // Nothing. + } + + /** Return the number of columns in this layout. */ + public int getColumns () + { + return cols; + } + + /** Return the horizontal gap. */ + public int getHgap () + { + return hgap; + } + + /** Return the number of rows in this layout. */ + public int getRows () + { + return rows; + } + + /** Return the vertical gap. */ + public int getVgap () + { + return vgap; + } + + /** Create a new <code>GridLayout</code> with one row and any number + * of columns. Both gaps are set to 0. + */ + public GridLayout () + { + this (1, 0, 0, 0); + } + + /** Create a new <code>GridLayout</code> with the specified number + * of rows and columns. Both gaps are set to 0. Note that the row + * and column settings cannot both be zero. If both the row and + * column values are non-zero, the rows value takes precedence. + * @param rows Number of rows + * @param cols Number of columns + * @exception IllegalArgumentException If rows and columns are both + * 0, or if either are negative + */ + public GridLayout (int rows, int cols) + { + this (rows, cols, 0, 0); + } + + /** Create a new GridLayout with the specified number of rows and + * columns and the specified gaps. + * Note that the row and column settings cannot both be + * zero. If both the row and column values are non-zero, the rows value + * takes precedence. + * @param rows Number of rows + * @param cols Number of columns + * @param hgap The horizontal gap + * @param vgap The vertical gap + * @exception IllegalArgumentException If rows and columns are both + * 0, if either are negative, or if either gap is negative + */ + public GridLayout (int rows, int cols, int hgap, int vgap) + { + if (rows < 0) + throw new IllegalArgumentException ("number of rows cannot be negative"); + if (cols < 0) + throw new IllegalArgumentException ("number of columns cannot be negative"); + if (rows == 0 && cols == 0) + throw new IllegalArgumentException ("both rows and columns cannot be 0"); + if (hgap < 0) + throw new IllegalArgumentException ("horizontal gap must be nonnegative"); + if (vgap < 0) + throw new IllegalArgumentException ("vertical gap must be nonnegative"); + this.rows = rows; + this.cols = cols; + this.hgap = hgap; + this.vgap = vgap; + } + + /** Lay out the container's components based on current settings. + * The free space in the container is divided evenly into the specified + * number of rows and columns in this object. + * @param parent The container to lay out + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int num = parent.ncomponents; + + // There's no point, and handling this would mean adding special + // cases. + if (num == 0) + return; + + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + int real_rows = rows; + int real_cols = cols; + if (real_rows == 0) + real_rows = (num + real_cols - 1) / real_cols; + else + real_cols = (num + real_rows - 1) / real_rows; + + // We might have less than a single row. In this case we expand + // to fill. + if (num < real_cols) + real_cols = num; + + Dimension d = parent.getSize (); + Insets ins = parent.getInsets (); + + // Compute width and height of each cell in the grid. + int tw = d.width - ins.left - ins.right; + tw = (tw - (real_cols - 1) * hgap) / real_cols; + int th = d.height - ins.top - ins.bottom; + th = (th - (real_rows - 1) * vgap) / real_rows; + + // If the cells are too small, still try to do something. + if (tw < 0) + tw = 1; + if (th < 0) + th = 1; + + int x = ins.left; + int y = ins.top; + int i = 0; + int recount = 0; + + while (i < num) + { + comps[i].setBounds (x, y, tw, th); + + ++i; + ++recount; + if (recount == real_cols) + { + recount = 0; + y += vgap + th; + x = ins.left; + } + else + x += hgap + tw; + } + } + } + + /** Get the minimum layout size of the container. + * @param cont The parent container + */ + public Dimension minimumLayoutSize (Container cont) + { + return getSize (cont, true); + } + + /** Get the preferred layout size of the container. + * @param cont The parent container + */ + public Dimension preferredLayoutSize (Container cont) + { + return getSize (cont, false); + } + + /** Remove the indicated component from this layout manager. + * This particular implementation does nothing. + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + // Nothing. + } + + /** Set the number of columns. + * @param newCols + * @exception IllegalArgumentException If the number of columns is + * negative, or if the number of columns is zero and the number + * of rows is already 0. + */ + public void setColumns (int newCols) + { + if (newCols < 0) + throw new IllegalArgumentException ("number of columns cannot be negative"); + if (newCols == 0 && rows == 0) + throw new IllegalArgumentException ("number of rows is already 0"); + this.cols = newCols; + } + + /** Set the horizontal gap + * @param hgap The horizontal gap + * @exception IllegalArgumentException If the hgap value is less than zero. + */ + public void setHgap (int hgap) + { + if (hgap < 0) + throw new IllegalArgumentException ("horizontal gap must be nonnegative"); + this.hgap = hgap; + } + + /** Set the number of rows + * @param newRows + * @exception IllegalArgumentException If the number of rows is + * negative, or if the number of rows is zero and the number + * of columns is already 0. + */ + public void setRows (int newRows) + { + if (newRows < 0) + throw new IllegalArgumentException ("number of rows cannot be negative"); + if (newRows == 0 && cols == 0) + throw new IllegalArgumentException ("number of columns is already 0"); + this.rows = newRows; + } + + /** Set the vertical gap. + * @param vgap The vertical gap + * @exception IllegalArgumentException If the vgap value is less than zero. + */ + public void setVgap (int vgap) + { + if (vgap < 0) + throw new IllegalArgumentException ("vertical gap must be nonnegative"); + this.vgap = vgap; + } + + /** Return String description of this object. */ + public String toString () + { + return ("[" + getClass ().getName () + + ",hgap=" + hgap + ",vgap=" + vgap + + ",rows=" + rows + ",cols=" + cols + + "]"); + } + + // This method is used to compute the various sizes. + private Dimension getSize (Container parent, boolean is_min) + { + synchronized (parent.getTreeLock ()) + { + int w = 0, h = 0, num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + for (int i = 0; i < num; ++i) + { + Dimension d; + + if (is_min) + d = comps[i].getMinimumSize (); + else + d = comps[i].getPreferredSize (); + + w = Math.max (d.width, w); + h = Math.max (d.height, h); + } + + int real_rows = rows; + int real_cols = cols; + if (real_rows == 0) + real_rows = (num + real_cols - 1) / real_cols; + else + real_cols = (num + real_rows - 1) / real_rows; + + Insets ins = parent.getInsets (); + // We subtract out an extra gap here because the gaps are only + // between cells. + w = ins.left + ins.right + real_cols * (w + hgap) - hgap; + h = ins.top + ins.bottom + real_rows * (h + vgap) - vgap; + return new Dimension (w, h); + } + } + + /** + * @serial The number of columns in the grid. + */ + private int cols; + + /** + * @serial The number of rows in the grid. + */ + private int rows; + + /** + * @serial The horizontal gap between columns + */ + private int hgap; + + /** + * @serial The vertical gap between rows + */ + private int vgap; +} diff --git a/libjava/classpath/java/awt/HeadlessException.java b/libjava/classpath/java/awt/HeadlessException.java new file mode 100644 index 00000000000..b180b1d86f7 --- /dev/null +++ b/libjava/classpath/java/awt/HeadlessException.java @@ -0,0 +1,72 @@ +/* HeadlessException.java -- operation not possible in headless environment + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This exception is thrown when code dependent on a keyboard, mouse, or + * display is executed in a headless environment. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class HeadlessException extends UnsupportedOperationException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 167183644944358563L; + + /** + * Create a new instance with no detailed error message. + */ + public HeadlessException() + { + } + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public HeadlessException(String message) + { + super(message); + } +} // class HeadlessException diff --git a/libjava/classpath/java/awt/IllegalComponentStateException.java b/libjava/classpath/java/awt/IllegalComponentStateException.java new file mode 100644 index 00000000000..4a47f1da95d --- /dev/null +++ b/libjava/classpath/java/awt/IllegalComponentStateException.java @@ -0,0 +1,71 @@ +/* IllegalComponentStateException.java -- bad component state + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This exception is thrown when the requested operation failed because + * a component was not in the proper state. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class IllegalComponentStateException extends IllegalStateException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1889339587208144238L; + + /** + * Create a new instance with no detailed error message. + */ + public IllegalComponentStateException() + { + } + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public IllegalComponentStateException(String message) + { + super(message); + } +} // class IllegalComponentStateException diff --git a/libjava/classpath/java/awt/Image.java b/libjava/classpath/java/awt/Image.java new file mode 100644 index 00000000000..b657ad007d9 --- /dev/null +++ b/libjava/classpath/java/awt/Image.java @@ -0,0 +1,203 @@ +/* Image.java -- superclass for images + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.ReplicateScaleFilter; + +/** + * This is the abstract superclass of all image objects in Java. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Image +{ + /** + * This variable is returned whenever a property that is not defined + * is requested. + */ + // For debug purposes, this might as well be a unique string. + public static final Object UndefinedProperty + = new String("undefined property"); + + /** + * Constant indicating that the default scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_DEFAULT = 1; + + /** + * Constant indicating that a fast scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_FAST = 2; + + /** + * Constant indicating that a smooth scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_SMOOTH = 4; + + /** + * Constant indicating that the <code>ReplicateScaleFilter</code> class + * algorithm should be used for scaling. + * + * @see ReplicateScaleFilter + * @since 1.1 + */ + public static final int SCALE_REPLICATE = 8; + + /** + * Constant indicating that the area averaging scaling algorithm should be + * used. + * + * @see java.awt.image.AreaAveragingScaleFilter + * @since 1.1 + */ + public static final int SCALE_AREA_AVERAGING = 16; + + /** + * A default constructor for subclasses. + */ + public Image() + { + } + + /** + * Returns the width of the image, or -1 if it is unknown. If the + * image width is unknown, the observer object will be notified when + * the value is known. + * + * @param observer the image observer for this object + * @return the width in pixels + * @see #getHeight(ImageObserver) + */ + public abstract int getWidth(ImageObserver observer); + + /** + * Returns the height of the image, or -1 if it is unknown. If the + * image height is unknown, the observer object will be notified when + * the value is known. + * + * @param observer the image observer for this object + * @return the height in pixels + * @see #getWidth(ImageObserver) + */ + public abstract int getHeight(ImageObserver observer); + + /** + * Returns the image producer object for this object. The producer is the + * object which generates pixels for this image. + * + * @return the image producer for this object + */ + public abstract ImageProducer getSource(); + + /** + * Returns a graphics context object for drawing an off-screen object. + * This method is only valid for off-screen objects. + * + * @return a graphics context object for an off-screen object + * @see Graphics#getcreateImage(int, int) + */ + public abstract Graphics getGraphics(); + + /** + * This method requests a named property for an object. The value of the + * property is returned. The value <code>UndefinedProperty</code> is + * returned if there is no property with the specified name. The value + * <code>null</code> is returned if the properties for the object are + * not yet known. In this case, the specified image observer is notified + * when the properties are known. + * + * @param name the requested property name + * @param observer the image observer for this object + * @return the named property, if available + * @see #UndefinedProperty + */ + public abstract Object getProperty(String name, ImageObserver observer); + + /** + * Scales the image to the requested dimension. A new Image with asynchronous + * loading will be produced according to the hints of the algorithm + * requested. If either the width or height is non-positive, it is adjusted + * to preserve the original aspect ratio. + * + * @param width the width of the scaled image + * @param height the height of the scaled image + * @param flags a value indicating the algorithm to use + * @return the scaled <code>Image</code> object + * @see #SCALE_DEFAULT + * @see #SCALE_FAST + * @see #SCALE_SMOOTH + * @see #SCALE_REPLICATE + * @see #SCALE_AREA_AVERAGING + * @since 1.1 + */ + public Image getScaledInstance(int width, int height, int flags) + { + switch (flags) + { + case SCALE_DEFAULT: + case SCALE_FAST: + case SCALE_REPLICATE: + ImageProducer producer = + new FilteredImageSource(this.getSource(), + new ReplicateScaleFilter(width, height)); + return Toolkit.getDefaultToolkit().createImage(producer); + case SCALE_SMOOTH: + case SCALE_AREA_AVERAGING: + default: + throw new Error("not implemented"); + } + } + + /** + * Flushes (that is, destroys) any resources used for this image. This + * includes the actual image data. + */ + public abstract void flush(); +} // class Image diff --git a/libjava/classpath/java/awt/ImageCapabilities.java b/libjava/classpath/java/awt/ImageCapabilities.java new file mode 100644 index 00000000000..2fe71d1e23b --- /dev/null +++ b/libjava/classpath/java/awt/ImageCapabilities.java @@ -0,0 +1,107 @@ +/* ImageCapabilities.java -- the capabilities of an image buffer + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class represents the capabilities of an image buffer. An + * image buffer may be backed by accelerated graphics resources. + * Those resources may or may not be volatile. This class is used to + * describe these image buffer characteristics. + */ +public class ImageCapabilities implements Cloneable +{ + /** + * Whether or not this the image buffer uses accelerated graphics + * resources. + */ + private final boolean accelerated; + + /** + * Create a new image capability descriptor. + * + * @param accelerated true if the image buffer uses accelerated + * graphics resources + */ + public ImageCapabilities(boolean accelerated) + { + this.accelerated = accelerated; + } + + /** + * Returns whether or not the image buffer uses accelerated graphics + * resources. + * + * @return true if the image buffer uses accelerated graphics + * resources; false otherwise + */ + public boolean isAccelerated() + { + return accelerated; + } + + /** + * Returns whether or not the image buffer's resources are volatile, + * meaning that they can be reclaimed by the graphics system at any + * time. + * + * @return true if the image buffer's resources are volatile; false + * otherwise + */ + public boolean isTrueVolatile() + { + return true; + } + + /** + * Clone this image capability descriptor. + * + * @return a clone of this image capability descriptor + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); + } + } +} diff --git a/libjava/classpath/java/awt/Insets.java b/libjava/classpath/java/awt/Insets.java new file mode 100644 index 00000000000..7238a34e22a --- /dev/null +++ b/libjava/classpath/java/awt/Insets.java @@ -0,0 +1,158 @@ +/* Insets.java -- information about a container border + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This class represents the "margin" or space around a container. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status + */ +public class Insets implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2272572637695466749L; + + /** + * The gap from the top. + * + * @serial the top inset + */ + public int top; + + /** + * The gap from the left. + * + * @serial the left inset + */ + public int left; + + /** + * The gap from the bottom. + * + * @serial the bottom inset + */ + public int bottom; + + /** + * The gap from the right. + * + * @serial the right inset + */ + public int right; + + /** + * Initializes a new instance of <code>Inset</code> with the specified + * inset values. + * + * @param top the top inset + * @param left the left inset + * @param bottom the bottom inset + * @param right the right inset + */ + public Insets(int top, int left, int bottom, int right) + { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } + + /** + * Tests whether this object is equal to the specified object. The other + * object must be an instance of Insets with identical field values. + * + * @param obj the object to test against + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Insets)) + return false; + Insets i = (Insets) obj; + return top == i.top && bottom == i.bottom + && left == i.left && right == i.right; + } + + /** + * Returns a hashcode for this instance. The formula is unspecified, but + * appears to be <code>XXX what is it? </code>. + * + * @return the hashcode + */ + public int hashCode() + { + // This can't be right... + return top + bottom + left + right; + } + + /** + * Returns a string representation of this object, which will be non-null. + * The format is unspecified, but appears to be <code>XXX what is it?</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "(top=" + top + ",bottom=" + bottom + + ",left=" + left + ",right=" + right + ')'; + } + + /** + * Returns a copy of this object. + * + * @return a copy of this object + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class Insets diff --git a/libjava/classpath/java/awt/ItemSelectable.java b/libjava/classpath/java/awt/ItemSelectable.java new file mode 100644 index 00000000000..f155f723efe --- /dev/null +++ b/libjava/classpath/java/awt/ItemSelectable.java @@ -0,0 +1,75 @@ +/* ItemSelectable.java -- items that can be selected + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemListener; + +/** + * This interface is for objects that can have one or more items selected. + * For example, radio buttons. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface ItemSelectable +{ + /** + * Returns the list of objects that are selected in this component. + * + * @return the list of selected objects, or null + */ + Object[] getSelectedObjects(); + + /** + * Adds an item listener to this object. It will receive selection events + * for this object by the user (but not programatically). If listener is + * null, it is ignored. + * + * @param listener the item listener to add + */ + void addItemListener(ItemListener listener); + + /** + * Removes an item listener from this object. + * + * @param listener the item listener to remove + */ + void removeItemListener(ItemListener listener); +} // interface ItemSelectable diff --git a/libjava/classpath/java/awt/JobAttributes.java b/libjava/classpath/java/awt/JobAttributes.java new file mode 100644 index 00000000000..2acb3a01ed3 --- /dev/null +++ b/libjava/classpath/java/awt/JobAttributes.java @@ -0,0 +1,500 @@ +/* JobAttributes.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Needs documentation... + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4, lacks documentation + */ +public final class JobAttributes implements Cloneable +{ + public static final class DefaultSelectionType extends AttributeValue + { + private static final String[] NAMES = { "all", "range", "selection" }; + public static final DefaultSelectionType ALL + = new DefaultSelectionType(0); + public static final DefaultSelectionType RANGE + = new DefaultSelectionType(1); + public static final DefaultSelectionType SELECTION + = new DefaultSelectionType(2); + private DefaultSelectionType(int value) + { + super(value, NAMES); + } + } // class DefaultSelectionType + + public static final class DestinationType extends AttributeValue + { + private static final String[] NAMES = { "file", "printer" }; + public static final DestinationType FILE = new DestinationType(0); + public static final DestinationType PRINTER = new DestinationType(1); + private DestinationType(int value) + { + super(value, NAMES); + } + } // class DestinationType + + public static final class DialogType extends AttributeValue + { + private static final String[] NAMES = { "common", "native", "none" }; + public static final DialogType COMMON = new DialogType(0); + public static final DialogType NATIVE = new DialogType(1); + public static final DialogType NONE = new DialogType(2); + private DialogType(int value) + { + super(value, NAMES); + } + } // class DialogType + + public static final class MultipleDocumentHandlingType + extends AttributeValue + { + private static final String[] NAMES = { + "separate-documents-collated-copies", + "separate-documents-uncollated-copies" + }; + public static final MultipleDocumentHandlingType + SEPARATE_DOCUMENTS_COLLATED_COPIES + = new MultipleDocumentHandlingType(0); + public static final MultipleDocumentHandlingType + SEPARATE_DOCUMENTS_UNCOLLATED_COPIES + = new MultipleDocumentHandlingType(1); + private MultipleDocumentHandlingType(int value) + { + super(value, NAMES); + } + } // class MultipleDocumentHandlingType + + public static final class SidesType extends AttributeValue + { + private static final String[] NAMES + = { "one-sided", "two-sided-long-edge", "two-sided-short-edge" }; + public static final SidesType ONE_SIDED = new SidesType(0); + public static final SidesType TWO_SIDED_LONG_EDGE = new SidesType(1); + public static final SidesType TWO_SIDED_SHORT_EDGE = new SidesType(2); + private SidesType(int value) + { + super(value, NAMES); + } + } // class SidesType + + private int copies; + private DefaultSelectionType selection; + private DestinationType destination; + private DialogType dialog; + private String filename; + private int maxPage; + private int minPage; + private MultipleDocumentHandlingType multiple; + private int[][] pageRanges; // null for default value + private int fromPage; // 0 for default value + private int toPage; // 0 for default value + private String printer; + private SidesType sides; + + public JobAttributes() + { + copies = 1; + selection = DefaultSelectionType.ALL; + destination = DestinationType.PRINTER; + dialog = DialogType.NATIVE; + maxPage = Integer.MAX_VALUE; + minPage = 1; + multiple + = MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES; + sides = SidesType.ONE_SIDED; + } + + public JobAttributes(JobAttributes attr) + { + set(attr); + } + + public JobAttributes(int copies, DefaultSelectionType selection, + DestinationType destination, DialogType dialog, + String filename, int max, int min, + MultipleDocumentHandlingType multiple, + int[][] pageRanges, String printer, SidesType sides) + { + if (copies <= 0 || selection == null || destination == null + || dialog == null || max < min || min <= 0 || multiple == null + || sides == null) + throw new IllegalArgumentException(); + this.copies = copies; + this.selection = selection; + this.destination = destination; + this.dialog = dialog; + this.filename = filename; + maxPage = max; + minPage = min; + this.multiple = multiple; + setPageRanges(pageRanges); + this.printer = printer; + this.sides = sides; + } + + public Object clone() + { + return new JobAttributes(this); + } + + public void set(JobAttributes attr) + { + copies = attr.copies; + selection = attr.selection; + destination = attr.destination; + dialog = attr.dialog; + filename = attr.filename; + maxPage = attr.maxPage; + minPage = attr.minPage; + multiple = attr.multiple; + pageRanges = (int[][]) attr.pageRanges.clone(); + printer = attr.printer; + sides = attr.sides; + fromPage = attr.fromPage; + toPage = attr.toPage; + } + + public int getCopies() + { + return copies; + } + + public void setCopies(int copies) + { + if (copies <= 0) + throw new IllegalArgumentException(); + this.copies = copies; + } + + public void setCopiesToDefault() + { + copies = 1; + } + + public DefaultSelectionType getDefaultSelection() + { + return selection; + } + + public void setDefaultSelection(DefaultSelectionType selection) + { + if (selection == null) + throw new IllegalArgumentException(); + this.selection = selection; + } + + public DestinationType getDestination() + { + return destination; + } + + public void setDestination(DestinationType destination) + { + if (destination == null) + throw new IllegalArgumentException(); + this.destination = destination; + } + + public DialogType getDialog() + { + return dialog; + } + + public void setDialog(DialogType dialog) + { + if (dialog == null) + throw new IllegalArgumentException(); + this.dialog = dialog; + } + + public String getFileName() + { + return filename; + } + + public void setFileName(String filename) + { + this.filename = filename; + } + + public int getFromPage() + { + return fromPage != 0 ? fromPage + : pageRanges != null ? pageRanges[0][0] + : toPage != 0 ? toPage : minPage; + } + + public void setFromPage(int fromPage) + { + if (fromPage < minPage || (fromPage > toPage && toPage != 0) + || fromPage > maxPage) + throw new IllegalArgumentException(); + if (pageRanges == null) + this.fromPage = fromPage; + } + + public int getMaxPage() + { + return maxPage; + } + + public void setMaxPage(int maxPage) + { + if (maxPage < minPage) + throw new IllegalArgumentException(); + this.maxPage = maxPage; + if (maxPage < fromPage) + fromPage = maxPage; + if (maxPage < toPage) + toPage = maxPage; + if (pageRanges != null) + { + int i = pageRanges.length - 1; + while (i >= 0 && maxPage < pageRanges[i][1]) + i--; + if (maxPage >= pageRanges[++i][0]) + pageRanges[i++][1] = maxPage; + if (i == 0) + pageRanges = null; + else if (i < pageRanges.length) + { + int[][] tmp = new int[i][]; + System.arraycopy(pageRanges, 0, tmp, 0, i); + pageRanges = tmp; + } + } + } + + public int getMinPage() + { + return minPage; + } + + public void setMinPage(int minPage) + { + if (minPage <= 0 || minPage > maxPage) + throw new IllegalArgumentException(); + this.minPage = minPage; + if (minPage > toPage) + toPage = minPage; + if (minPage > fromPage) + fromPage = minPage; + if (pageRanges != null) + { + int size = pageRanges.length; + int i = 0; + while (i < size && minPage > pageRanges[i][0]) + i++; + if (minPage <= pageRanges[i - 1][1]) + pageRanges[--i][0] = minPage; + if (i == size) + pageRanges = null; + else if (i > 0) + { + int[][] tmp = new int[size - i][]; + System.arraycopy(pageRanges, i, tmp, 0, size - i); + pageRanges = tmp; + } + } + } + + public MultipleDocumentHandlingType getMultipleDocumentHandling() + { + return multiple; + } + + public void setMultipleDocumentHandling + (MultipleDocumentHandlingType multiple) + { + if (multiple == null) + throw new IllegalArgumentException(); + this.multiple = multiple; + } + + public void setMultipleDocumentHandlingToDefault() + { + multiple + = MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES; + } + + public int[][] getPageRanges() + { + if (pageRanges == null) + return new int[][] { { getFromPage(), getToPage() } }; + // Perform a deep clone, so user code cannot affect original arrays. + int i = pageRanges.length; + int[][] result = new int[i][]; + while (--i >= 0) + result[i] = (int[]) pageRanges[i].clone(); + return result; + } + + public void setPageRanges(int[][] pageRanges) + { + int size = pageRanges == null ? 0 : pageRanges.length; + if (size == 0) + throw new IllegalArgumentException(); + while (--size >= 0) + { + int[] range = pageRanges[size]; + if (range == null || range.length != 2 + || range[0] < minPage || range[1] < range[0] || range[1] > maxPage + || (size != 0 && range[0] <= pageRanges[size - 1][1])) + throw new IllegalArgumentException(); + } + size = pageRanges.length; + if (fromPage > 0 && pageRanges[0][0] > fromPage) + fromPage = pageRanges[0][0]; + if (toPage > 0 && pageRanges[size - 1][1] < toPage) + toPage = pageRanges[size - 1][1]; + this.pageRanges = new int[size][]; + while (--size >= 0) + this.pageRanges[size] = (int[]) pageRanges[size].clone(); + } + + public String getPrinter() + { + return printer; + } + + public void setPrinter(String printer) + { + this.printer = printer; + } + + public SidesType getSides() + { + return sides; + } + + public void setSides(SidesType sides) + { + if (sides == null) + throw new IllegalArgumentException(); + this.sides = sides; + } + + public void setSidesToDefault() + { + sides = SidesType.ONE_SIDED; + } + + public int getToPage() + { + return toPage != 0 ? toPage + : pageRanges != null ? pageRanges[pageRanges.length - 1][1] + : fromPage != 0 ? fromPage : maxPage; + } + + public void setToPage(int toPage) + { + if (toPage < minPage || (fromPage > toPage && fromPage != 0) + || toPage > maxPage) + throw new IllegalArgumentException(); + if (pageRanges == null) + this.toPage = toPage; + } + + public boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof JobAttributes)) + return false; + JobAttributes ja = (JobAttributes) o; + if (copies != ja.copies || selection != ja.selection + || destination != ja.destination || dialog != ja.dialog + || ! filename.equals(ja.filename) || maxPage != ja.maxPage + || minPage != ja.minPage || multiple != ja.multiple + || fromPage != ja.fromPage || toPage != ja.toPage + || ! printer.equals(ja.printer) || sides != ja.sides + || (pageRanges == null) != (ja.pageRanges == null)) + return false; + if (pageRanges != ja.pageRanges) + for (int i = pageRanges.length; --i >= 0; ) + if (pageRanges[i][0] != ja.pageRanges[i][0] + || pageRanges[i][1] != ja.pageRanges[i][1]) + return false; + return true; + } + + public int hashCode() + { + int hash = (selection.value << 6) ^ (destination.value << 5) + ^ (dialog.value << 3) ^ (multiple.value << 2) ^ sides.value + ^ (filename == null ? 0 : filename.hashCode()) + ^ (printer == null ? 0 : printer.hashCode()); + // The effect of the above fields on the hashcode match the JDK. However, + // I am unable to reverse engineer the effect of the fields listed below, + // so I am using my own implementation. Note that this still satisfies + // the general contract of hashcode, it just doesn't match the JDK. + hash ^= (copies << 27) ^ (maxPage << 22) ^ (minPage << 17); + if (pageRanges == null) + hash ^= (getFromPage() << 13) ^ (getToPage() << 8); + else + for (int i = pageRanges.length; --i >= 0; ) + hash ^= (pageRanges[i][0] << 13) ^ (pageRanges[i][1] << 8); + return hash; + } + + public String toString() + { + StringBuffer s = new StringBuffer("copies=").append(copies) + .append(",defaultSelection=").append(selection).append(",destination=") + .append(destination).append(",dialog=").append(dialog) + .append(",fileName=").append(filename).append(",fromPage=") + .append(getFromPage()).append(",maxPage=").append(maxPage) + .append(",minPage=").append(minPage) + .append(",multiple-document-handling=").append(multiple) + .append(",page-ranges=["); + if (pageRanges == null) + s.append(minPage).append(':').append(minPage).append(']'); + else + for (int i = 0; i < pageRanges.length; i++) + s.append(pageRanges[i][0]).append(':').append(pageRanges[i][1]) + .append(','); + s.setLength(s.length() - 1); + return s.append("],printer=").append(printer).append(",sides=") + .append(sides).append(",toPage=").append(getToPage()).toString(); + } +} // class JobAttributes diff --git a/libjava/classpath/java/awt/KeyEventDispatcher.java b/libjava/classpath/java/awt/KeyEventDispatcher.java new file mode 100644 index 00000000000..30997278e1b --- /dev/null +++ b/libjava/classpath/java/awt/KeyEventDispatcher.java @@ -0,0 +1,82 @@ +/* KeyEventDispatcher.java -- dispatches key events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; + +/** + * An instance of this interface coordinates with a KeyboardFocusManager to + * target and dispatch all key events. This allows retargeting, consuming, + * changing, or otherwise manipulating the key event before sending it on to + * a target. + * + * <p>By default, the KeyboardFocusManager is the sink for all key events not + * dispatched by other dispatchers. Therefore, it is unnecessary for the user + * to register the focus manager as a dispatcher. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyboardFocusManager#addKeyEventDispatcher(KeyEventDispatcher) + * @see KeyboardFocusManager#removeKeyEventDispatcher(KeyEventDispatcher) + * @since 1.4 + * @status updated to 1.4 + */ +public interface KeyEventDispatcher +{ + /** + * Called by the KeyboardFocusManager to request that a key event be + * dispatched. The dispatcher is free to retarget the event, consume it, + * dispatch it, or make other changes. This is usually done to allow + * delivery of key events to objects other than the window in focus, such + * as for navigating non-focusable components. If this dispatcher chooses + * to dispatch the event itself, it should call <code>redispatchEvent</code> + * to avoid infinite recursion. + * + * <p>If the return value is false, the KeyEvent is passed to the next + * dispatcher in the chain, ending with the KeyboardFocusManager. If the + * return value is true, the event has been consumed (although it might + * have been ignored), and no further action will be taken on the event. Be + * sure to check whether the event was consumed before dispatching it + * further. + * + * @param e the key event + * @return true if the event has been consumed + * @see KeyboardFocusManager#redispatchEvent(Component, AWTEvent) + */ + boolean dispatchKeyEvent(KeyEvent e); +} // interface KeyEventDispatcher diff --git a/libjava/classpath/java/awt/KeyEventPostProcessor.java b/libjava/classpath/java/awt/KeyEventPostProcessor.java new file mode 100644 index 00000000000..0b39dc25075 --- /dev/null +++ b/libjava/classpath/java/awt/KeyEventPostProcessor.java @@ -0,0 +1,81 @@ +/* KeyEventPostProcessor.java -- performs actions after a key event dispatch + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; + +/** + * An instance of this interface coordinates with a KeyboardFocusManager to + * target and dispatch all key events that are otherwise unconsumed. This + * allows events which take place when nothing has focus to still operate, + * such as menu keyboard shortcuts. + * + * <p>By default, the KeyboardFocusManager is the sink for all key events not + * post-processed elsewhere. Therefore, it is unnecessary for the user + * to register the focus manager as a dispatcher. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyboardFocusManager#addKeyEventPostProcessor(KeyEventPostProcessor) + * @see KeyboardFocusManager#removeKeyEventPostProcessor(KeyEventPostProcessor) + * @since 1.4 + * @status updated to 1.4 + */ +public interface KeyEventPostProcessor +{ + /** + * Called by the KeyboardFocusManager to request that a key event be + * post-processed. Typically, the event has already been dispatched and + * handled, unless no object has focus. Thus, this allows global event + * handling for things like menu shortcuts. If this post-processor chooses + * to dispatch the event, it should call <code>redispatchEvent</code> + * to avoid infinite recursion. + * + * <p>If the return value is false, the KeyEvent is passed to the next + * dispatcher in the chain, ending with the KeyboardFocusManager. If the + * return value is true, the event has been consumed (although it might + * have been ignored), and no further action will be taken on the event. Be + * sure to check whether the event was consumed before dispatching it + * further. + * + * @param e the key event + * @return true if the event has been consumed + * @see KeyboardFocusManager#redispatchEvent(Component, AWTEvent) + */ + boolean postProcessKeyEvent(KeyEvent e); +} // interface KeyEventPostProcessor diff --git a/libjava/classpath/java/awt/KeyboardFocusManager.java b/libjava/classpath/java/awt/KeyboardFocusManager.java new file mode 100644 index 00000000000..f64618477bd --- /dev/null +++ b/libjava/classpath/java/awt/KeyboardFocusManager.java @@ -0,0 +1,1478 @@ +/* KeyboardFocusManager.java -- manage component focusing via the keyboard + Copyright (C) 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.applet.Applet; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The <code>KeyboardFocusManager</code> handles the focusing of + * windows for receiving keyboard events. The manager handles + * the dispatch of all <code>FocusEvent</code>s and + * <code>KeyEvent</code>s, along with <code>WindowEvent</code>s + * relating to the focused window. Users can use the manager + * to ascertain the current focus owner and fire events. + * <br /> + * <br /> + * The focus owner is the <code>Component</code> that receives + * key events. The focus owner is either the currently focused + * window or a component within this window. + * <br /> + * <br /> + * The underlying native windowing system may denote the active + * window or its children with special decorations (e.g. a highlighted + * title bar). The active window is always either a <code>Frame</code> + * or <code>Dialog</code>, and is either the currently focused + * window or its owner. + * <br /> + * <br /> + * Applets may be partitioned into different applet contexts, according + * to their code base. In this case, each context has its own + * <code>KeyboardFocusManager</code>, as opposed to the global + * manager maintained by applets which share the same context. + * Each context is insulated from the others, and they don't interact. + * The resulting behaviour, as with context division, depends on the browser + * supporting the applets. Regardless, there can only ever be + * one focused window, one active window and one focus owner + * per <code>ClassLoader</code>. + * <br /> + * <br /> + * To support this separation of focus managers, the manager instances + * and the internal state information is grouped by the + * <code>ThreadGroup</code> to which it pertains. With respect to + * applets, each code base has its own <code>ThreadGroup</code>, so the + * isolation of each context is enforced within the manager. + * <br /> + * <br /> + * By default, the manager defines TAB and Ctrl+TAB as the + * forward focus traversal keys and Shift+TAB and Ctrl+Shift+TAB + * as the backward focus traversal keys. No up or down cycle + * traversal keys are defined by default. Traversal takes effect + * on the firing of a relevant <code>KEY_PRESSED</code> event. + * However, all other key events related to the use of the + * defined focus traversal key sequence are consumed and not + * dispatched. + * <br /> + * <br /> + * These default traversal keys come into effect on all windows + * for which no alternative set of keys is defined. This also + * applies recursively to any child components of such a window, + * which define no traversal keys of their own. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public abstract class KeyboardFocusManager + implements KeyEventDispatcher, KeyEventPostProcessor +{ + /** Identifies {@link AWTKeyStroke}s that move the focus forward in + the focus cycle. */ + public static final int FORWARD_TRAVERSAL_KEYS = 0; + + /** Identifies {@link AWTKeyStroke}s that move the focus backward in + the focus cycle. */ + public static final int BACKWARD_TRAVERSAL_KEYS = 1; + + /** Identifies {@link AWTKeyStroke}s that move the focus up to the + parent focus cycle root. */ + public static final int UP_CYCLE_TRAVERSAL_KEYS = 2; + + /** Identifies {@link AWTKeyStroke}s that move the focus down to the + child focus cycle root. */ + public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3; + + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the next focusable Component in the focus cycle. */ + private static final Set DEFAULT_FORWARD_KEYS; + + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the previous focusable Component in the focus cycle. */ + private static final Set DEFAULT_BACKWARD_KEYS; + + /** Populate the DEFAULT_FORWARD_KEYS and DEFAULT_BACKWARD_KEYS + {@link java.util.Set}s. */ + static + { + Set s = new HashSet(); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0)); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.CTRL_DOWN_MASK)); + DEFAULT_FORWARD_KEYS = Collections.unmodifiableSet(s); + s = new HashSet(); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK)); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK + | KeyEvent.CTRL_DOWN_MASK)); + DEFAULT_BACKWARD_KEYS = Collections.unmodifiableSet(s); + } + + /** The global object {@link java.util.Map}s. */ + + /** For security reasons, {@link java.applet.Applet}s in different + codebases must be insulated from one another. Since {@link + KeyboardFocusManager}s have the ability to return {@link + Component}s from a given {@link java.applet.Applet}, each + codebase must have an independent {@link KeyboardFocusManager}. + Since each codebase has its own {@link ThreadGroup} in which its + {@link Applet}s run, it makes sense to partition {@link + KeyboardFocusManager}s according to {@link + java.lang.ThreadGroup}. Thus, currentKeyboardFocusManagers is a + {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}. */ + private static Map currentKeyboardFocusManagers = new HashMap (); + + /** {@link java.applet.Applet}s in one codebase must not be allowed + to access {@link Component}s in {@link java.applet.Applet}s in + other codebases. To enforce this restriction, we key the + following {@link java.util.Map}s on {@link java.lang.ThreadGroup}s (which + are per-codebase). For example, if {@link + java.lang.ThreadGroup} A calls {@link #setGlobalFocusOwner}, + passing {@link Component} C, currentFocusOwners[A] is assigned + C, and all other currentFocusOwners values are nullified. Then + if {@link java.lang.ThreadGroup} A subsequently calls {@link + #getGlobalFocusOwner}, it will return currentFocusOwners[A], + that is, {@link Component} C. If another {@link + java.lang.ThreadGroup} K calls {@link #getGlobalFocusOwner}, it + will return currentFocusOwners[K], that is, null. + + Since this is a static field, we ensure that there is only one + focused {@link Component} per class loader. */ + private static Map currentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the {@link Component} that owns the permanent + keyboard focus. @see currentFocusOwners */ + private static Map currentPermanentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focused {@link Window}. @see + currentFocusOwners */ + private static Map currentFocusedWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the active {@link Window}. @see + currentFocusOwners */ + private static Map currentActiveWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focus cycle root {@link Container}. @see + currentFocusOwners */ + private static Map currentFocusCycleRoots = new HashMap (); + + /** The default {@link FocusTraveralPolicy} that focus-managing + {@link Container}s will use to define their initial focus + traversal policy. */ + private FocusTraversalPolicy defaultPolicy; + + /** An array that stores the {@link #FORWARD_TRAVERSAL_KEYS}, {@link + #BACKWARD_TRAVERSAL_KEYS}, {@link #UP_CYCLE_TRAVERSAL_KEYS} and + {@link #DOWN_CYCLE_TRAVERSAL_KEYS} {@link AWTKeyStroke}s {@link + java.util.Set}s. */ + private Set[] defaultFocusKeys = new Set[] + { + DEFAULT_FORWARD_KEYS, DEFAULT_BACKWARD_KEYS, + Collections.EMPTY_SET, Collections.EMPTY_SET + }; + + /** + * A utility class to support the handling of events relating to property changes. + */ + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport (this); + + /** + * A utility class to support the handling of events relating to vetoable changes. + */ + private final VetoableChangeSupport vetoableChangeSupport = new VetoableChangeSupport (this); + + /** A list of {@link KeyEventDispatcher}s that process {@link + KeyEvent}s before they are processed the default keyboard focus + manager. */ + private final ArrayList keyEventDispatchers = new ArrayList(); + + /** A list of {@link KeyEventPostProcessor}s that process unconsumed + {@link KeyEvent}s. */ + private final ArrayList keyEventPostProcessors = new ArrayList(); + + /** + * Construct a KeyboardFocusManager. + */ + public KeyboardFocusManager () + { + } + + /** + * Retrieve the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @return the keyboard focus manager associated with the current + * thread group + */ + public static KeyboardFocusManager getCurrentKeyboardFocusManager () + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + + if (currentKeyboardFocusManagers.get (currentGroup) == null) + setCurrentKeyboardFocusManager (null); + + return (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); + } + + /** + * Set the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @param m the keyboard focus manager for the current thread group + */ + public static void setCurrentKeyboardFocusManager (KeyboardFocusManager m) + { + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("replaceKeyboardFocusManager")); + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager manager; + + if (m == null) + manager = createFocusManager(); + else + manager = m; + + currentKeyboardFocusManagers.put (currentGroup, manager); + } + + /** + * Creates a KeyboardFocusManager. The exact class is determined by the + * system property 'gnu.java.awt.FocusManager'. If this is not set, + * we default to DefaultKeyboardFocusManager. + */ + private static KeyboardFocusManager createFocusManager() + { + String fmClassName = System.getProperty("gnu.java.awt.FocusManager", + "java.awt.DefaultKeyboardFocusManager"); + try + { + Class fmClass = Class.forName(fmClassName); + KeyboardFocusManager fm = (KeyboardFocusManager) fmClass.newInstance(); + return fm; + } + catch (ClassNotFoundException ex) + { + System.err.println("The class " + fmClassName + " cannot be found."); + System.err.println("Check the setting of the system property"); + System.err.println("gnu.java.awt.FocusManager"); + return null; + } + catch (InstantiationException ex) + { + System.err.println("The class " + fmClassName + " cannot be"); + System.err.println("instantiated."); + System.err.println("Check the setting of the system property"); + System.err.println("gnu.java.awt.FocusManager"); + return null; + } + catch (IllegalAccessException ex) + { + System.err.println("The class " + fmClassName + " cannot be"); + System.err.println("accessed."); + System.err.println("Check the setting of the system property"); + System.err.println("gnu.java.awt.FocusManager"); + return null; + } + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, or + * null if the focus owner was not set by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getFocusOwner () + { + Component owner = (Component) getObject (currentFocusOwners); + if (owner == null) + owner = (Component) getObject (currentPermanentFocusOwners); + return owner; + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, + * regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. If there is no temporary + * focus owner in effect then this method will return the same value + * as {@link #getGlobalPermanentFocusOwner}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalFocusOwner () + { + // Check if there is a temporary focus owner. + Component focusOwner = (Component) getGlobalObject (currentFocusOwners); + + return (focusOwner == null) ? getGlobalPermanentFocusOwner () : focusOwner; + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getFocusOwner} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusOwner}. This + * method does not actually transfer the keyboard focus. + * + * @param owner the Component to return from getFocusOwner and + * getGlobalFocusOwner + * + * @see Component#requestFocus() + * @see Component#requestFocusInWindow() + */ + protected void setGlobalFocusOwner (Component owner) + { + if (owner == null || owner.focusable) + setGlobalObject (currentFocusOwners, owner, "focusOwner"); + } + + /** + * Clear the global focus owner and deliver a FOCUS_LOST event to + * the previously-focused {@link Component}. Until another {@link + * Component} becomes the keyboard focus owner, key events will be + * discarded by top-level windows. + */ + public void clearGlobalFocusOwner () + { + synchronized (currentFocusOwners) + { + Component focusOwner = getGlobalFocusOwner (); + Component permanentFocusOwner = getGlobalPermanentFocusOwner (); + + setGlobalFocusOwner (null); + setGlobalPermanentFocusOwner (null); + + // Inform the old focus owner that it has lost permanent + // focus. + if (focusOwner != null) + { + // We can't cache the event queue, because of + // bootstrapping issues. We need to set the default + // KeyboardFocusManager in EventQueue before the event + // queue is started. + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + if (focusOwner != permanentFocusOwner) + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, true)); + else + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, false)); + } + + if (focusOwner != permanentFocusOwner) + { + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + q.postEvent (new FocusEvent (permanentFocusOwner, FocusEvent.FOCUS_LOST, false)); + } + } + } + + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, or null if the focus owner was not set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getPermanentFocusOwner () + { + return (Component) getObject (currentPermanentFocusOwners); + } + + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalPermanentFocusOwner () + { + return (Component) getGlobalObject (currentPermanentFocusOwners); + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getPermanentFocusOwner} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalPermanentFocusOwner}. This method does not actually + * transfer the keyboard focus. + * + * @param focusOwner the Component to return from + * getPermanentFocusOwner and getGlobalPermanentFocusOwner + * + * @see Component#requestFocus() + * @see Component#requestFocusInWindow() + */ + protected void setGlobalPermanentFocusOwner (Component focusOwner) + { + if (focusOwner == null || focusOwner.focusable) + setGlobalObject (currentPermanentFocusOwners, focusOwner, + "permanentFocusOwner"); + } + + /** + * Retrieve the {@link Window} that is or contains the keyboard + * focus owner, or null if the focused window was not set by a + * thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window or null + */ + public Window getFocusedWindow () + { + return (Window) getObject (currentFocusedWindows); + } + + /** + * Retrieve the {@link Window} that is or contains the focus owner, + * regardless of whether or not the {@link Window} was set focused + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Window getGlobalFocusedWindow () + { + return (Window) getGlobalObject (currentFocusedWindows); + } + + /** + * Set the {@link Window} that will be returned by {@link + * #getFocusedWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusedWindow}. + * This method does not actually cause <code>window</code> to become + * the focused {@link Window}. + * + * @param window the Window to return from getFocusedWindow and + * getGlobalFocusedWindow + */ + protected void setGlobalFocusedWindow (Window window) + { + if (window == null || window.focusable) + setGlobalObject (currentFocusedWindows, window, "focusedWindow"); + } + + /** + * Retrieve the active {@link Window}, or null if the active window + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the active window or null + */ + public Window getActiveWindow() + { + return (Window) getObject (currentActiveWindows); + } + + /** + * Retrieve the active {@link Window}, regardless of whether or not + * the {@link Window} was made active by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the active window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Window getGlobalActiveWindow() + { + return (Window) getGlobalObject (currentActiveWindows); + } + + /** + * Set the {@link Window} that will be returned by {@link + * #getActiveWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalActiveWindow}. This + * method does not actually cause <code>window</code> to be made + * active. + * + * @param window the Window to return from getActiveWindow and + * getGlobalActiveWindow + */ + protected void setGlobalActiveWindow(Window window) + { + setGlobalObject (currentActiveWindows, window, "activeWindow"); + } + + /** + * Retrieve the default {@link FocusTraversalPolicy}. + * Focus-managing {@link Container}s use the returned object to + * define their initial focus traversal policy. + * + * @return a non-null default FocusTraversalPolicy object + */ + public FocusTraversalPolicy getDefaultFocusTraversalPolicy () + { + if (defaultPolicy == null) + defaultPolicy = new DefaultFocusTraversalPolicy (); + return defaultPolicy; + } + + /** + * Set the {@link FocusTraversalPolicy} returned by {@link + * #getDefaultFocusTraversalPolicy}. Focus-managing {@link + * Container}s created after this call will use policy as their + * initial focus traversal policy. Existing {@link Container}s' + * focus traversal policies will not be affected by calls to this + * method. + * + * @param policy the FocusTraversalPolicy that will be returned by + * subsequent calls to getDefaultFocusTraversalPolicy + * @throws IllegalArgumentException if policy is null + */ + public void setDefaultFocusTraversalPolicy (FocusTraversalPolicy policy) + { + if (policy == null) + throw new IllegalArgumentException (); + firePropertyChange ("defaultFocusTraversalPolicy", defaultPolicy, policy); + defaultPolicy = policy; + } + + /** + * Set the default {@link java.util.Set} of focus traversal keys for + * one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * @param keystrokes set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public void setDefaultFocusTraversalKeys (int id, Set keystrokes) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + if (keystrokes == null) + throw new IllegalArgumentException (); + + Set sa; + Set sb; + Set sc; + String type; + switch (id) + { + case FORWARD_TRAVERSAL_KEYS: + sa = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "forwardDefaultFocusTraversalKeys"; + break; + case BACKWARD_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "backwardDefaultFocusTraversalKeys"; + break; + case UP_CYCLE_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "upCycleDefaultFocusTraversalKeys"; + break; + case DOWN_CYCLE_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + type = "downCycleDefaultFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || sc.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (type, defaultFocusKeys[id], keystrokes); + defaultFocusKeys[id] = keystrokes; + } + + /** + * Retrieve the default {@link java.util.Set} of focus traversal + * keys for one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * + * @return the default set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public Set getDefaultFocusTraversalKeys (int id) + { + if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + return defaultFocusKeys[id]; + } + + /** + * Retrieve the current focus cycle root, or null if the focus owner + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root or null + */ + public Container getCurrentFocusCycleRoot () + { + return (Container) getObject (currentFocusCycleRoots); + } + + /** + * Retrieve the current focus cycle root, regardless of whether or + * not it was made set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Container getGlobalCurrentFocusCycleRoot () + { + return (Container) getGlobalObject (currentFocusCycleRoots); + } + + /** + * Set the {@link Container} that will be returned by {@link + * #getCurrentFocusCycleRoot} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalCurrentFocusCycleRoot}. This method does not actually + * make <code>cycleRoot</code> the current focus cycle root. + * + * @param cycleRoot the focus cycle root to return from + * getCurrentFocusCycleRoot and getGlobalCurrentFocusCycleRoot + */ + public void setGlobalCurrentFocusCycleRoot (Container cycleRoot) + { + setGlobalObject (currentFocusCycleRoots, cycleRoot, "currentFocusCycleRoot"); + } + + /** + * Registers the supplied property change listener for receiving + * events caused by the following property changes: + * + * <ul> + * <li>the current focus owner ("focusOwner")</li> + * <li>the permanent focus owner ("permanentFocusOwner")</li> + * <li>the focused window ("focusedWindow")</li> + * <li>the active window ("activeWindow")</li> + * <li>the default focus traversal policy ("defaultFocusTraversalPolicy")</li> + * <li>the default set of forward traversal keys ("forwardDefaultFocusTraversalKeys")</li> + * <li>the default set of backward traversal keys ("backwardDefaultFocusTraversalKeys")</li> + * <li>the default set of up cycle traversal keys ("upCycleDefaultFocusTraversalKeys")</li> + * <li>the default set of down cycle traversal keys ("downCycleDefaultFocusTraversalKeys")</li> + * <li>the current focus cycle root ("currentFocusCycleRoot")</li> + * </ul> + * + * If the supplied listener is null, nothing occurs. + * + * @param l the new listener to register. + * @see KeyboardFocusManager#addPropertyChangeListener(String, java.beans.PropertyChangeListener) + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes the supplied property change listener from the list + * of registered listeners. If the supplied listener is null, + * nothing occurs. + * + * @param l the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Returns the currently registered property change listeners + * in array form. The returned array is empty if no listeners are + * currently registered. + * + * @return an array of registered property change listeners. + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return propertyChangeSupport.getPropertyChangeListeners(); + } + + /** + * Registers a property change listener for receiving events relating + * to a change to a specified property. The supplied property name can be + * either user-defined or one from the following list of properties + * relevant to this class: + * + * <ul> + * <li>the current focus owner ("focusOwner")</li> + * <li>the permanent focus owner ("permanentFocusOwner")</li> + * <li>the focused window ("focusedWindow")</li> + * <li>the active window ("activeWindow")</li> + * <li>the default focus traversal policy ("defaultFocusTraversalPolicy")</li> + * <li>the default set of forward traversal keys ("forwardDefaultFocusTraversalKeys")</li> + * <li>the default set of backward traversal keys ("backwardDefaultFocusTraversalKeys")</li> + * <li>the default set of up cycle traversal keys ("upCycleDefaultFocusTraversalKeys")</li> + * <li>the default set of down cycle traversal keys ("downCycleDefaultFocusTraversalKeys")</li> + * <li>the current focus cycle root ("currentFocusCycleRoot")</li> + * </ul> + * + * Nothing occurs if a null listener is supplied. null is regarded as a valid property name. + * + * @param name the name of the property to handle change events for. + * @param l the listener to register for changes to the specified property. + * @see KeyboardFocusManager#addPropertyChangeListener(java.beans.PropertyChangeListener) + */ + public void addPropertyChangeListener(String name, PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.addPropertyChangeListener(name, l); + } + + /** + * Removes the supplied property change listener registered for the + * specified property from the list of registered listeners. If the + * supplied listener is null, nothing occurs. + * + * @param name the name of the property the listener is + * monitoring changes to. + * @param l the listener to remove. + */ + public void removePropertyChangeListener(String name, + PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.removePropertyChangeListener(name, l); + } + + /** + * Returns the currently registered property change listeners + * in array form, which listen for changes to the supplied property. + * The returned array is empty, if no listeners are currently registered + * for events pertaining to the supplied property. + * + * @param name The property the returned listeners monitor for changes. + * @return an array of registered property change listeners which + * listen for changes to the supplied property. + */ + public PropertyChangeListener[] getPropertyChangeListeners(String name) + { + return propertyChangeSupport.getPropertyChangeListeners(name); + } + + /** + * Fires a property change event as a response to a change to + * to the specified property. The event is only fired if a + * change has actually occurred (i.e. o and n are different). + * + * @param name The name of the property to which a change occurred. + * @param o The old value of the property. + * @param n The new value of the property. + */ + protected void firePropertyChange(String name, Object o, Object n) + { + propertyChangeSupport.firePropertyChange(name, o, n); + } + + /** + * Registers a vetoable property change listener for receiving events + * relating to the following properties: + * + * <ul> + * <li>the current focus owner ("focusOwner")</li> + * <li>the permanent focus owner ("permanentFocusOwner")</li> + * <li>the focused window ("focusedWindow")</li> + * <li>the active window ("activeWindow")</li> + * </ul> + * + * Nothing occurs if a null listener is supplied. + * + * @param l the listener to register. + * @see KeyboardFocusManager#addVetoableChangeListener(String, java.beans.VetoableChangeListener) + */ + public void addVetoableChangeListener(VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.addVetoableChangeListener(l); + } + + /** + * Removes the supplied vetoable property change listener from + * the list of registered listeners. If the supplied listener + * is null, nothing occurs. + * + * @param l the listener to remove. + */ + public void removeVetoableChangeListener(VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.removeVetoableChangeListener(l); + } + + /** + * Returns the currently registered vetoable property change listeners + * in array form. The returned array is empty if no listeners are + * currently registered. + * + * @return an array of registered vetoable property change listeners. + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners() + { + return vetoableChangeSupport.getVetoableChangeListeners(); + } + + /** + * Registers a vetoable property change listener for receiving events relating + * to a vetoable change to a specified property. The supplied property name can be + * either user-defined or one from the following list of properties + * relevant to this class: + * + * <ul> + * <li>the current focus owner ("focusOwner")</li> + * <li>the permanent focus owner ("permanentFocusOwner")</li> + * <li>the focused window ("focusedWindow")</li> + * <li>the active window ("activeWindow")</li> + * </ul> + * + * Nothing occurs if a null listener is supplied. null is regarded as a valid property name. + * + * @param name the name of the property to handle change events for. + * @param l the listener to register for changes to the specified property. + * @see KeyboardFocusManager#addVetoableChangeListener(java.beans.VetoableChangeListener) + */ + public void addVetoableChangeListener(String name, VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.addVetoableChangeListener(name, l); + } + + /** + * Removes the supplied vetoable property change listener registered + * for the specified property from the list of registered listeners. + * If the supplied listener is null, nothing occurs. + * + * @param name the name of the vetoable property the listener is + * monitoring changes to. + * @param l the listener to remove. + */ + public void removeVetoableChangeListener(String name, + VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.removeVetoableChangeListener(name, l); + } + + /** + * Returns the currently registered vetoable property change listeners + * in array form, which listen for changes to the supplied property. + * The returned array is empty, if no listeners are currently registered + * for events pertaining to the supplied property. + * + * @param name The property the returned listeners monitor for changes. + * @return an array of registered property change listeners which + * listen for changes to the supplied property. + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners(String name) + { + return vetoableChangeSupport.getVetoableChangeListeners(name); + } + + /** + * Fires a property change event as a response to a vetoable change to + * to the specified property. The event is only fired if a + * change has actually occurred (i.e. o and n are different). + * In the event that the property change is vetoed, the following + * occurs: + * + * <ol> + * <li> + * This method throws a <code>PropertyVetoException</code> to + * the proposed change. + * </li> + * <li> + * A new event is fired to reverse the previous change. + * </li> + * <li> + * This method again throws a <code>PropertyVetoException</code> + * in response to the reversion. + * </li> + * </ol> + * + * @param name The name of the property to which a change occurred. + * @param o The old value of the property. + * @param n The new value of the property. + * @throws PropertyVetoException if one of the listeners vetos + * the change by throwing this exception. + */ + protected void fireVetoableChange(String name, Object o, Object n) + throws PropertyVetoException + { + vetoableChangeSupport.fireVetoableChange(name, o, n); + } + + /** + * Adds a key event dispatcher to the list of registered dispatchers. + * When a key event is fired, each dispatcher's <code>dispatchKeyEvent</code> + * method is called in the order that they were added, prior to the manager + * dispatching the event itself. Notifications halt when one of the + * dispatchers returns true. + * <br /> + * <br /> + * The same dispatcher can exist multiple times within the list + * of registered dispatchers, and there is no limit on the length + * of this list. A null dispatcher is simply ignored. + * + * @param dispatcher The dispatcher to register. + */ + public void addKeyEventDispatcher(KeyEventDispatcher dispatcher) + { + if (dispatcher != null) + keyEventDispatchers.add(dispatcher); + } + + /** + * Removes the specified key event dispatcher from the list of + * registered dispatchers. The manager always dispatches events, + * regardless of its existence within the list. The manager + * can be added and removed from the list, as with any other + * dispatcher, but this does not affect its ability to dispatch + * key events. Non-existent and null dispatchers are simply ignored + * by this method. + * + * @param dispatcher The dispatcher to remove. + */ + public void removeKeyEventDispatcher(KeyEventDispatcher dispatcher) + { + keyEventDispatchers.remove(dispatcher); + } + + /** + * Returns the currently registered key event dispatchers in <code>List</code> + * form. At present, this only includes dispatchers explicitly registered + * via the <code>addKeyEventDispatcher()</code> method, but this behaviour + * is subject to change and should not be depended on. The manager itself + * may be a member of the list, but only if explicitly registered. If no + * dispatchers have been registered, the list will be empty. + * + * @return A list of explicitly registered key event dispatchers. + * @see KeyboardFocusManager#addKeyEventDispatcher(java.awt.KeyEventDispatcher) + */ + protected List getKeyEventDispatchers () + { + return (List) keyEventDispatchers.clone (); + } + + /** + * Adds a key event post processor to the list of registered post processors. + * Post processors work in the same way as key event dispatchers, except + * that they are invoked after the manager has dispatched the key event, + * and not prior to this. Each post processor's <code>postProcessKeyEvent</code> + * method is called to see if any post processing needs to be performed. THe + * processors are called in the order in which they were added to the list, + * and notifications continue until one returns true. As with key event + * dispatchers, the manager is implicitly called following this process, + * regardless of whether or not it is present within the list. + * <br /> + * <br /> + * The same post processor can exist multiple times within the list + * of registered post processors, and there is no limit on the length + * of this list. A null post processor is simply ignored. + * + * @param postProcessor the post processor to register. + * @see KeyboardFocusManager#addKeyEventDispatcher(java.awt.KeyEventDispatcher) + */ + public void addKeyEventPostProcessor (KeyEventPostProcessor postProcessor) + { + if (postProcessor != null) + keyEventPostProcessors.add (postProcessor); + } + + /** + * Removes the specified key event post processor from the list of + * registered post processors. The manager always post processes events, + * regardless of its existence within the list. The manager + * can be added and removed from the list, as with any other + * post processor, but this does not affect its ability to post process + * key events. Non-existent and null post processors are simply ignored + * by this method. + * + * @param postProcessor the post processor to remove. + */ + public void removeKeyEventPostProcessor (KeyEventPostProcessor postProcessor) + { + keyEventPostProcessors.remove (postProcessor); + } + + /** + * Returns the currently registered key event post processors in <code>List</code> + * form. At present, this only includes post processors explicitly registered + * via the <code>addKeyEventPostProcessor()</code> method, but this behaviour + * is subject to change and should not be depended on. The manager itself + * may be a member of the list, but only if explicitly registered. If no + * post processors have been registered, the list will be empty. + * + * @return A list of explicitly registered key event post processors. + * @see KeyboardFocusManager#addKeyEventPostProcessor(java.awt.KeyEventPostProcessor) + */ + protected List getKeyEventPostProcessors () + { + return (List) keyEventPostProcessors.clone (); + } + + /** + * The AWT event dispatcher uses this method to request that the manager + * handle a particular event. If the manager fails or refuses to + * dispatch the supplied event (this method returns false), the + * AWT event dispatcher will try to dispatch the event itself. + * <br /> + * <br /> + * The manager is expected to handle all <code>FocusEvent</code>s + * and <code>KeyEvent</code>s, and <code>WindowEvent</code>s + * relating to the focus. Dispatch is done with regard to the + * the focus owner and the currently focused and active windows. + * In handling the event, the source of the event may be overridden. + * <br /> + * <br /> + * The actual dispatching is performed by calling + * <code>redispatchEvent()</code>. This avoids the infinite recursion + * of dispatch requests which may occur if this method is called on + * the target component. + * + * @param e the event to dispatch. + * @return true if the event was dispatched. + * @see KeyboardFocusManager#redispatchEvent(java.awt.Component, java.awt.AWTEvent) + * @see KeyEvent + * @see FocusEvent + * @see WindowEvent + */ + public abstract boolean dispatchEvent (AWTEvent e); + + /** + * Handles redispatching of an event so that recursion of + * dispatch requests does not occur. Event dispatch methods + * within this manager (<code>dispatchEvent()</code>) and + * the key event dispatchers should use this method to handle + * dispatching rather than the dispatch method of the target + * component. + * <br /> + * <br /> + * <strong> + * This method is not intended for general consumption, and is + * only for the use of the aforementioned classes. + * </strong> + * + * @param target the target component to which the event is + * dispatched. + * @param e the event to dispatch. + */ + public final void redispatchEvent (Component target, AWTEvent e) + { + synchronized (e) + { + e.setSource (target); + target.dispatchEvent (e); + } + } + + /** + * Attempts to dispatch key events for which no key event dispatcher + * has so far succeeded. This method is usually called by + * <code>dispatchEvent()</code> following the sending of the key + * event to any registered key event dispatchers. If the key + * event reaches this stage, none of the dispatchers returned + * true. This is, of course, always the case if there are no + * registered dispatchers. + * <br /> + * <br /> + * If this method also fails to handle the key event, then + * false is returned to the caller. In the case of + * <code>dispatchEvent()</code>, the calling method may try + * to handle the event itself or simply forward on the + * false result to its caller. When the event is dispatched + * by this method, a true result is propogated through the + * calling methods. + * + * @param e the key event to dispatch. + * @return true if the event was dispatched successfully. + */ + public abstract boolean dispatchKeyEvent (KeyEvent e); + + /** + * Handles the post processing of key events. By default, + * this method will map unhandled key events to appropriate + * <code>MenuShortcut</code>s. The event is consumed + * in the process and the shortcut is activated. This + * method is usually called by <code>dispatchKeyEvent</code>. + * + * @param e the key event to post process. + * @return true by default, as the event was handled. + */ + public abstract boolean postProcessKeyEvent (KeyEvent e); + + /** + * Handles focus traversal operations for key events which + * represent focus traversal keys in relation to the supplied + * component. The supplied component is assumed to have the + * focus, whether it does so or not, and the operation is + * carried out as appropriate, with this in mind. + * + * @param focused the component on which to perform focus traversal, + * on the assumption that this component has the focus. + * @param e the possible focus traversal key event. + */ + public abstract void processKeyEvent (Component focused, KeyEvent e); + + /** + * Delays all key events following the specified timestamp until the + * supplied component has focus. The AWT calls this method when it is + * determined that a focus change may occur within the native windowing + * system. Any key events which occur following the time specified by + * after are delayed until a <code>FOCUS_GAINED</code> event is received + * for the untilFocused component. The manager is responsible for ensuring + * this takes place. + * + * @param after the timestamp beyond which all key events are delayed until + * the supplied component gains focus. + * @param untilFocused the component to wait on gaining focus. + */ + protected abstract void enqueueKeyEvents (long after, Component untilFocused); + + /** + * Removes the key event block specified by the supplied timestamp and component. + * All delayed key events are released for normal dispatching following its + * removal and subsequent key events that would have been blocked are now + * immediately dispatched. If the specified timestamp is below 0, then + * the request with the oldest timestamp is removed. + * + * @param after the timestamp of the key event block to be removed, or a + * value smaller than 0 if the oldest is to be removed. + * @param untilFocused the component of the key event block to be removed. + */ + protected abstract void dequeueKeyEvents (long after, Component untilFocused); + + /** + * Discards all key event blocks relating to focus requirements for + * the supplied component, regardless of timestamp. + * + * @param comp the component of the key event block(s) to be removed. + */ + protected abstract void discardKeyEvents (Component comp); + + /** + * Moves the current focus to the next component following + * comp, based on the current focus traversal policy. By + * default, only visible, displayable, accepted components + * can receive focus. <code>Canvas</code>es, <code>Panel</code>s, + * <code>Label</code>s, <code>ScrollPane</code>s, <code>Scrollbar</code>s, + * <code>Window</code>s and lightweight components are judged + * to be unacceptable by default. See the + * <code>DefaultFocusTraversalPolicy</code> for more details. + * + * @param comp the component prior to the one which will + * become the focus, following execution of this method. + * @see DefaultFocusTraversalPolicy + */ + public abstract void focusNextComponent(Component comp); + + /** + * Moves the current focus to the previous component, prior to + * comp, based on the current focus traversal policy. By + * default, only visible, displayable, accepted components + * can receive focus. <code>Canvas</code>es, <code>Panel</code>s, + * <code>Label</code>s, <code>ScrollPane</code>s, <code>Scrollbar</code>s, + * <code>Window</code>s and lightweight components are judged + * to be unacceptable by default. See the + * <code>DefaultFocusTraversalPolicy</code> for more details. + * + * @param comp the component following the one which will + * become the focus, following execution of this method. + * @see DefaultFocusTraversalPolicy + */ + public abstract void focusPreviousComponent(Component comp); + + /** + * Moves the current focus upwards by one focus cycle. + * Both the current focus owner and current focus cycle root + * become the focus cycle root of the supplied component. + * However, in the case of a <code>Window</code>, the default + * focus component becomes the focus owner and the focus cycle + * root is not changed. + * + * @param comp the component used as part of the focus traversal. + */ + public abstract void upFocusCycle(Component comp); + + /** + * Moves the current focus downwards by one focus cycle. + * If the supplied container is a focus cycle root, then this + * becomes the current focus cycle root and the focus goes + * to the default component of the specified container. + * Nothing happens for non-focus cycle root containers. + * + * @param cont the container used as part of the focus traversal. + */ + public abstract void downFocusCycle(Container cont); + + /** + * Moves the current focus to the next component, based on the + * current focus traversal policy. By default, only visible, + * displayable, accepted component can receive focus. + * <code>Canvas</code>es, <code>Panel</code>s, + * <code>Label</code>s, <code>ScrollPane</code>s, <code>Scrollbar</code>s, + * <code>Window</code>s and lightweight components are judged + * to be unacceptable by default. See the + * <code>DefaultFocusTraversalPolicy</code> for more details. + * + * @see DefaultFocusTraversalPolicy + */ + public final void focusNextComponent() + { + focusNextComponent (null); + } + + /** + * Moves the current focus to the previous component, based on the + * current focus traversal policy. By default, only visible, + * displayable, accepted component can receive focus. + * <code>Canvas</code>es, <code>Panel</code>s, + * <code>Label</code>s, <code>ScrollPane</code>s, <code>Scrollbar</code>s, + * <code>Window</code>s and lightweight components are judged + * to be unacceptable by default. See the + * <code>DefaultFocusTraversalPolicy</code> for more details. + * + * @see DefaultFocusTraversalPolicy + */ + public final void focusPreviousComponent() + { + focusPreviousComponent (null); + } + + /** + * Moves the current focus upwards by one focus cycle, + * so that the new focus owner is the focus cycle root + * of the current owner. The current focus cycle root then + * becomes the focus cycle root of the new focus owner. + * However, in the case of the focus cycle root of the + * current focus owner being a <code>Window</code>, the default + * component of this window becomes the focus owner and the + * focus cycle root is not changed. + */ + public final void upFocusCycle() + { + upFocusCycle (null); + } + + /** + * Moves the current focus downwards by one focus cycle, + * iff the current focus cycle root is a <code>Container</code>. + * Usually, the new focus owner is set to the default component + * of the container and the current focus cycle root is set + * to the current focus owner. Nothing occurs if the current + * focus cycle root is not a container. + */ + public final void downFocusCycle() + { + Component focusOwner = getGlobalFocusOwner (); + if (focusOwner instanceof Container + && ((Container) focusOwner).isFocusCycleRoot ()) + downFocusCycle ((Container) focusOwner); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, if the object was set by the a thread in the + * current {@link java.lang.ThreadGroup}. Otherwise, return null. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @see getFocusOwner + * @see getPermanentFocusOwner + * @see getFocusedWindow + * @see getActiveWindow + * @see getCurrentFocusCycleRoot + */ + private Object getObject (Map globalMap) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + return globalMap.get (currentGroup); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, regardless of whether or not the object was set + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + * + * @see getGlobalFocusOwner + * @see getGlobalPermanentFocusOwner + * @see getGlobalFocusedWindow + * @see getGlobalActiveWindow + * @see getGlobalCurrentFocusCycleRoot + */ + private Object getGlobalObject (Map globalMap) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager managerForCallingThread + = (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); + + if (this != managerForCallingThread) + throw new SecurityException ("Attempted to retrieve an object from a " + + "keyboard focus manager that isn't " + + "associated with the current thread group."); + + synchronized (globalMap) + { + Collection globalObjects = globalMap.values (); + Iterator i = globalObjects.iterator (); + Component globalObject; + + while (i.hasNext ()) + { + globalObject = (Component) i.next (); + if (globalObject != null) + return globalObject; + } + } + + // No Object was found. + return null; + } + + /** + * Set an object in one of the global object {@link java.util.Map}s, + * that will be returned by subsequent calls to getGlobalObject on + * the same {@link java.util.Map}. + * + * @param globalMap one of the global object Maps + * @param newObject the object to set + * @param property the property that will change + * + * @see setGlobalFocusOwner + * @see setGlobalPermanentFocusOwner + * @see setGlobalFocusedWindow + * @see setGlobalActiveWindow + * @see setGlobalCurrentFocusCycleRoot + */ + private void setGlobalObject (Map globalMap, + Object newObject, + String property) + { + synchronized (globalMap) + { + // Save old object. + Object oldObject = getGlobalObject (globalMap); + + // Nullify old object. + Collection threadGroups = globalMap.keySet (); + Iterator i = threadGroups.iterator (); + while (i.hasNext ()) + { + ThreadGroup oldThreadGroup = (ThreadGroup) i.next (); + if (globalMap.get (oldThreadGroup) != null) + { + globalMap.put (oldThreadGroup, null); + // There should only be one object set at a time, so + // we can short circuit. + break; + } + } + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + firePropertyChange (property, oldObject, newObject); + try + { + fireVetoableChange (property, oldObject, newObject); + // Set new object. + globalMap.put (currentGroup, newObject); + } + catch (PropertyVetoException e) + { + } + } + } +} diff --git a/libjava/classpath/java/awt/Label.java b/libjava/classpath/java/awt/Label.java new file mode 100644 index 00000000000..8867fa12e35 --- /dev/null +++ b/libjava/classpath/java/awt/Label.java @@ -0,0 +1,314 @@ +/* Label.java -- Java label widget + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.LabelPeer; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This component is used for displaying simple text strings that cannot + * be edited by the user. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Label extends Component implements Accessible +{ + +/* + * Static Variables + */ + +/** + * Alignment constant aligning the text to the left of its window. + */ +public static final int LEFT = 0; + +/** + * Alignment constant aligning the text in the center of its window. + */ +public static final int CENTER = 1; + +/** + * Alignment constant aligning the text to the right of its window. + */ +public static final int RIGHT = 2; + +// Serialization version constant: +private static final long serialVersionUID = 3094126758329070636L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial Indicates the alignment of the text within this label's window. + * This is one of the constants in this class. The default value is + * <code>LEFT</code>. + */ +private int alignment; + +/** + * @serial The text displayed in the label + */ +private String text; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Label</code> with no text. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Label() +{ + this("", LEFT); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Label</code> with the specified + * text that is aligned to the left. + * + * @param text The text of the label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Label(String text) +{ + this(text, LEFT); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Label</code> with the specified + * text and alignment. + * + * @param text The text of the label. + * @param alignment The desired alignment for the text in this label, + * which must be one of <code>LEFT</code>, <code>CENTER</code>, or + * <code>RIGHT</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Label(String text, int alignment) +{ + setAlignment (alignment); + setText (text); + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the constant indicating the alignment of the text in this + * label. The value returned will be one of the alignment constants + * from this class. + * + * @return The alignment of the text in the label. + */ +public int +getAlignment() +{ + return(alignment); +} + +/*************************************************************************/ + +/** + * Sets the text alignment of this label to the specified value. + * + * @param alignment The desired alignment for the text in this label, + * which must be one of <code>LEFT</code>, <code>CENTER</code>, or + * <code>RIGHT</code>. + */ +public synchronized void +setAlignment(int alignment) +{ + if (alignment != CENTER && alignment != LEFT && alignment != RIGHT) + throw new IllegalArgumentException ("invalid alignment: " + alignment); + this.alignment = alignment; + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setAlignment (alignment); + } +} + +/*************************************************************************/ + +/** + * Returns the text displayed in this label. + * + * @return The text for this label. + */ +public String +getText() +{ + return(text); +} + +/*************************************************************************/ + +/** + * Sets the text in this label to the specified value. + * + * @param text The new text for this label. + */ +public synchronized void +setText(String text) +{ + this.text = text; + + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setText (text); + } +} + +/*************************************************************************/ + +/** + * Notifies this label that it has been added to a container, causing + * the peer to be created. This method is called internally by the AWT + * system. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createLabel (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Returns a parameter string useful for debugging. + * + * @return A debugging string. + */ +protected String +paramString() +{ + return ("text=" + getText() + ",alignment=" + + getAlignment() + "," + super.paramString()); +} + +/** + * This class provides accessibility support for the label. + */ +protected class AccessibleAWTLabel + extends AccessibleAWTComponent +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + private static final long serialVersionUID = -3568967560160480438L; + + /** + * Constructor for the accessible label. + */ + public AccessibleAWTLabel() + { + } + + /** + * Returns the accessible name for the label. This is + * the text used in the label. + * + * @return a <code>String</code> containing the accessible + * name for this label. + */ + public String getAccessibleName() + { + return getText(); + } + + /** + * Returns the accessible role for the label. + * + * @return an instance of <code>AccessibleRole</code>, describing + * the role of the label. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LABEL; + } + +} + +/** + * Gets the AccessibleContext associated with this <code>Label</code>. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTLabel(); + return accessibleContext; +} + +} // class Label + diff --git a/libjava/classpath/java/awt/LayoutManager.java b/libjava/classpath/java/awt/LayoutManager.java new file mode 100644 index 00000000000..62ff8087e76 --- /dev/null +++ b/libjava/classpath/java/awt/LayoutManager.java @@ -0,0 +1,92 @@ +/* LayoutManager.java -- lay out elements in a Container + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface is for laying out containers in a particular sequence. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Container + * @since 1.0 + * @status updated to 1.4 + */ +public interface LayoutManager +{ + /** + * Adds the specified component to the layout group. + * + * @param name the name of the component to add + * @param component the component to add + */ + void addLayoutComponent(String name, Component component); + + /** + * Removes the specified component from the layout group. + * + * @param component the component to remove + */ + void removeLayoutComponent(Component component); + + /** + * Calculates the preferred size for this container, taking into account + * the components it contains. + * + * @param parent the parent container to lay out + * @return the preferred dimensions of this container + * @see #minimumLayoutSize(Container) + */ + Dimension preferredLayoutSize(Container parent); + + /** + * Calculates the minimum size for this container, taking into account + * the components it contains. + * + * @param parent the parent container to lay out + * @return the minimum dimensions of this container + * @see #preferredLayoutSize(Container) + */ + Dimension minimumLayoutSize(Container parent); + + /** + * Lays out the components in the given container. + * + * @param parent the container to lay out + */ + void layoutContainer(Container parent); +} // interface LayoutManager diff --git a/libjava/classpath/java/awt/LayoutManager2.java b/libjava/classpath/java/awt/LayoutManager2.java new file mode 100644 index 00000000000..45fc5430f29 --- /dev/null +++ b/libjava/classpath/java/awt/LayoutManager2.java @@ -0,0 +1,100 @@ +/* LayoutManager2.java -- enhanced layout manager + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Layout manager for laying out containers based on contraints. The + * constraints control how the layout will proceed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see LayoutManager + * @see Container + * @since 1.0 + * @status updated to 1.4 + */ +public interface LayoutManager2 extends LayoutManager +{ + /** + * Adds the specified component to the layout, with the specified + * constraints object. + * + * @param component the component to add + * @param constraints the constraints to satisfy + */ + void addLayoutComponent(Component component, Object constraints); + + /** + * Determines the maximum size of the specified target container. + * + * @param target the container to lay out + * @return the maximum size of the container + * @see Component#getMaximumSize() + */ + Dimension maximumLayoutSize(Container target); + + /** + * Returns the preferred X axis alignment for the specified target + * container. This value will range from 0 to 1 where 0 is alignment + * closest to the origin, 0.5 is centered, and 1 is aligned furthest + * from the origin. + * + * @param target the target container + * @return the x-axis alignment preference + */ + float getLayoutAlignmentX(Container target); + + /** + * Returns the preferred Y axis alignment for the specified target + * container. This value will range from 0 to 1 where 0 is alignment + * closest to the origin, 0.5 is centered, and 1 is aligned furthest + * from the origin. + * + * @param target the target container + * @return the y-axis alignment preference + */ + float getLayoutAlignmentY(Container target); + + /** + * Forces the layout manager to purge any cached information about the + * layout of the target container. This will force it to be recalculated. + * + * @param target the target container + */ + void invalidateLayout(Container target); +} // interface LayoutManager2 diff --git a/libjava/classpath/java/awt/List.java b/libjava/classpath/java/awt/List.java new file mode 100644 index 00000000000..5fcc79bfcef --- /dev/null +++ b/libjava/classpath/java/awt/List.java @@ -0,0 +1,1263 @@ +/* List.java -- A listbox widget + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.ListPeer; +import java.util.EventListener; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * Class that implements a listbox widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class List extends Component + implements ItemSelectable, Accessible +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = -3304312411574666869L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +// FIXME: Need read/writeObject + +/** + * @serial The items in the list. + */ +private Vector items = new Vector(); + +/** + * @serial Indicates whether or not multiple items can be selected + * simultaneously. + */ +private boolean multipleMode; + +/** + * @serial The number of rows in the list. This is set on creation + * only and cannot be modified. + */ +private int rows; + +/** + * @serial An array of the item indices that are selected. + */ +private int[] selected; + +/** + * @serial An index value used by <code>makeVisible()</code> and + * <code>getVisibleIndex</code>. + */ +private int visibleIndex; + +// The list of ItemListeners for this object. +private ItemListener item_listeners; + +// The list of ActionListeners for this object. +private ActionListener action_listeners; + + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>List</code> with no visible lines + * and multi-select disabled. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +List() +{ + this(4, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>List</code> with the specified + * number of visible lines and multi-select disabled. + * + * @param rows The number of visible rows in the list. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +List(int rows) +{ + this(rows, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>List</code> with the specified + * number of lines and the specified multi-select setting. + * + * @param rows The number of visible rows in the list. + * @param multipleMode <code>true</code> if multiple lines can be selected + * simultaneously, <code>false</code> otherwise. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +List(int rows, boolean multipleMode) +{ + this.rows = rows; + this.multipleMode = multipleMode; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the number of items in this list. + * + * @return The number of items in this list. + */ +public int +getItemCount() +{ + return countItems (); +} + +/*************************************************************************/ + +/** + * Returns the number of items in this list. + * + * @return The number of items in this list. + * + * @deprecated This method is deprecated in favor of + * <code>getItemCount()</code> + */ +public int +countItems() +{ + return items.size (); +} + +/*************************************************************************/ + +/** + * Returns the complete list of items. + * + * @return The complete list of items in the list. + */ +public synchronized String[] +getItems() +{ + String[] l_items = new String[getItemCount()]; + + items.copyInto(l_items); + return(l_items); +} + +/*************************************************************************/ + +/** + * Returns the item at the specified index. + * + * @param index The index of the item to retrieve. + * + * @exception IndexOutOfBoundsException If the index value is not valid. + */ +public String +getItem(int index) +{ + return((String)items.elementAt(index)); +} + +/*************************************************************************/ + +/** + * Returns the number of visible rows in the list. + * + * @return The number of visible rows in the list. + */ +public int +getRows() +{ + return(rows); +} + +/*************************************************************************/ + +/** + * Tests whether or not multi-select mode is enabled. + * + * @return <code>true</code> if multi-select mode is enabled, + * <code>false</code> otherwise. + */ +public boolean +isMultipleMode() +{ + return allowsMultipleSelections (); +} + +/*************************************************************************/ + +/** + * Tests whether or not multi-select mode is enabled. + * + * @return <code>true</code> if multi-select mode is enabled, + * <code>false</code> otherwise. + * + * @deprecated This method is deprecated in favor of + * <code>isMultipleMode()</code>. + */ +public boolean +allowsMultipleSelections() +{ + return multipleMode; +} + +/*************************************************************************/ + +/** + * This method enables or disables multiple selection mode for this + * list. + * + * @param multipleMode <code>true</code> to enable multiple mode, + * <code>false</code> otherwise. + */ +public void +setMultipleMode(boolean multipleMode) +{ + setMultipleSelections (multipleMode); +} + +/*************************************************************************/ + +/** + * This method enables or disables multiple selection mode for this + * list. + * + * @param multipleMode <code>true</code> to enable multiple mode, + * <code>false</code> otherwise. + * + * @deprecated + */ +public void +setMultipleSelections(boolean multipleMode) +{ + this.multipleMode = multipleMode; + + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + peer.setMultipleMode (multipleMode); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of this component. + * + * @return The minimum size of this component. + */ +public Dimension +getMinimumSize() +{ + return getMinimumSize (getRows ()); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of this component. + * + * @return The minimum size of this component. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize</code>. + */ +public Dimension +minimumSize() +{ + return minimumSize (getRows ()); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The minimum size of this component. + */ +public Dimension +getMinimumSize(int rows) +{ + return minimumSize (rows); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The minimum size of this component. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize(int)</code>> + */ +public Dimension +minimumSize(int rows) +{ + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + return peer.minimumSize (rows); + else + return new Dimension (0, 0); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of this component. + * + * @return The preferred size of this component. + */ +public Dimension +getPreferredSize() +{ + return getPreferredSize (getRows ()); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of this component. + * + * @return The preferred size of this component. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize</code>. + */ +public Dimension +preferredSize() +{ + return preferredSize (getRows ()); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The preferred size of this component. + */ +public Dimension +getPreferredSize(int rows) +{ + return preferredSize (rows); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The preferred size of this component. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize(int)</code>> + */ +public Dimension +preferredSize(int rows) +{ + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + return peer.preferredSize (rows); + else + return new Dimension (0, 0); +} + +/*************************************************************************/ + +/** + * This method adds the specified item to the end of the list. + * + * @param item The item to add to the list. + */ +public void +add(String item) +{ + add (item, -1); +} + +/*************************************************************************/ + +/** + * This method adds the specified item to the end of the list. + * + * @param item The item to add to the list. + * + * @deprecated Use add() instead. + */ +public void +addItem(String item) +{ + addItem (item, -1); +} + +/*************************************************************************/ + +/** + * Adds the specified item to the specified location in the list. + * If the desired index is -1 or greater than the number of rows + * in the list, then the item is added to the end. + * + * @param item The item to add to the list. + * @param index The location in the list to add the item, or -1 to add + * to the end. + */ +public void +add(String item, int index) +{ + addItem (item, index); +} + +/*************************************************************************/ + +/** + * Adds the specified item to the specified location in the list. + * If the desired index is -1 or greater than the number of rows + * in the list, then the item is added to the end. + * + * @param item The item to add to the list. + * @param index The location in the list to add the item, or -1 to add + * to the end. + * + * @deprecated Use add() instead. + */ +public void +addItem(String item, int index) +{ + if ((index == -1) || (index >= items.size ())) + items.addElement (item); + else + items.insertElementAt (item, index); + + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + peer.add (item, index); +} + +/*************************************************************************/ + +/** + * Deletes the item at the specified index. + * + * @param index The index of the item to delete. + * + * @exception IllegalArgumentException If the index is not valid + * + * @deprecated + */ +public void +delItem(int index) throws IllegalArgumentException +{ + items.removeElementAt (index); + + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + peer.delItems (index, index); +} + +/*************************************************************************/ + +/** + * Deletes the item at the specified index. + * + * @param index The index of the item to delete. + * + * @exception IllegalArgumentException If the index is not valid + */ +public void +remove(int index) throws IllegalArgumentException +{ + delItem (index); +} + +/*************************************************************************/ + +/** + * Deletes all items in the specified index range. + * + * @param start The beginning index of the range to delete. + * @param end The ending index of the range to delete. + * + * @exception IllegalArgumentException If the indexes are not valid + * + * @deprecated This method is deprecated for some unknown reason. + */ +public synchronized void +delItems(int start, int end) throws IllegalArgumentException +{ + if ((start < 0) || (start >= items.size())) + throw new IllegalArgumentException("Bad list start index value: " + start); + + if ((start < 0) || (start >= items.size())) + throw new IllegalArgumentException("Bad list start index value: " + start); + + if (start > end) + throw new IllegalArgumentException("Start is greater than end!"); + + // We must run the loop in reverse direction. + for (int i = end; i >= start; --i) + items.removeElementAt (i); + if (peer != null) + { + ListPeer l = (ListPeer) peer; + l.delItems (start, end); + } +} + +/*************************************************************************/ + +/** + * Deletes the first occurrence of the specified item from the list. + * + * @param item The item to delete. + * + * @exception IllegalArgumentException If the specified item does not exist. + */ +public synchronized void +remove(String item) throws IllegalArgumentException +{ + int index = items.indexOf(item); + if (index == -1) + throw new IllegalArgumentException("List element to delete not found"); + + remove(index); +} + +/*************************************************************************/ + +/** + * Deletes all of the items from the list. + */ +public synchronized void +removeAll() +{ + clear (); +} + +/*************************************************************************/ + +/** + * Deletes all of the items from the list. + * + * @deprecated This method is deprecated in favor of <code>removeAll()</code>. + */ +public void +clear() +{ + items.clear(); + + ListPeer peer = (ListPeer) getPeer (); + if (peer != null) + peer.removeAll (); +} + +/*************************************************************************/ + +/** + * Replaces the item at the specified index with the specified item. + * + * @param item The new item value. + * @param index The index of the item to replace. + * + * @exception IllegalArgumentException If the index is not valid. + */ +public synchronized void +replaceItem(String item, int index) throws IllegalArgumentException +{ + if ((index < 0) || (index >= items.size())) + throw new IllegalArgumentException("Bad list index: " + index); + + items.insertElementAt(item, index + 1); + items.removeElementAt (index); + + if (peer != null) + { + ListPeer l = (ListPeer) peer; + + /* We add first and then remove so that the selected + item remains the same */ + l.add (item, index + 1); + l.delItems (index, index); + } +} + +/*************************************************************************/ + +/** + * Returns the index of the currently selected item. -1 will be returned + * if there are no selected rows or if there are multiple selected rows. + * + * @return The index of the selected row. + */ +public synchronized int +getSelectedIndex() +{ + if (peer != null) + { + ListPeer l = (ListPeer) peer; + selected = l.getSelectedIndexes (); + } + + if (selected == null || selected.length != 1) + return -1; + return selected[0]; +} + +/*************************************************************************/ + +/** + * Returns an array containing the indexes of the rows that are + * currently selected. + * + * @return A list of indexes of selected rows. + */ +public synchronized int[] +getSelectedIndexes() +{ + if (peer != null) + { + ListPeer l = (ListPeer) peer; + selected = l.getSelectedIndexes (); + } + return selected; +} + +/*************************************************************************/ + +/** + * Returns the item that is currently selected, or <code>null</code> if there + * is no item selected. FIXME: What happens if multiple items selected? + * + * @return The selected item, or <code>null</code> if there is no + * selected item. + */ +public synchronized String +getSelectedItem() +{ + int index = getSelectedIndex(); + if (index == -1) + return(null); + + return((String)items.elementAt(index)); +} + +/*************************************************************************/ + +/** + * Returns the list of items that are currently selected in this list. + * + * @return The list of currently selected items. + */ +public synchronized String[] +getSelectedItems() +{ + int[] indexes = getSelectedIndexes(); + if (indexes == null) + return(new String[0]); + + String[] retvals = new String[indexes.length]; + if (retvals.length > 0) + for (int i = 0 ; i < retvals.length; i++) + retvals[i] = (String)items.elementAt(indexes[i]); + + return(retvals); +} + +/*************************************************************************/ + +/** + * Returns the list of items that are currently selected in this list as + * an array of type <code>Object[]</code> instead of <code>String[]</code>. + * + * @return The list of currently selected items. + */ +public synchronized Object[] +getSelectedObjects() +{ + int[] indexes = getSelectedIndexes(); + if (indexes == null) + return(new Object[0]); + + Object[] retvals = new Object[indexes.length]; + if (retvals.length > 0) + for (int i = 0 ; i < retvals.length; i++) + retvals[i] = items.elementAt(indexes[i]); + + return(retvals); +} + +/*************************************************************************/ + +/** + * Tests whether or not the specified index is selected. + * + * @param index The index to test. + * + * @return <code>true</code> if the index is selected, <code>false</code> + * otherwise. + */ +public boolean +isIndexSelected(int index) +{ + return isSelected (index); +} + +/*************************************************************************/ + +/** + * Tests whether or not the specified index is selected. + * + * @param index The index to test. + * + * @return <code>true</code> if the index is selected, <code>false</code> + * otherwise. + * + * @deprecated This method is deprecated in favor of + * <code>isIndexSelected(int)</code>. + */ +public boolean +isSelected(int index) +{ + int[] indexes = getSelectedIndexes (); + + for (int i = 0; i < indexes.length; i++) + if (indexes[i] == index) + return true; + + return false; +} + +/*************************************************************************/ + +/** + * This method ensures that the item at the specified index is visible. + * + * @exception IllegalArgumentException If the specified index is out of + * range. + */ +public synchronized void +makeVisible(int index) throws IllegalArgumentException +{ + if ((index < 0) || (index >= items.size())) + throw new IllegalArgumentException("Bad list index: " + index); + + visibleIndex = index; + if (peer != null) + { + ListPeer l = (ListPeer) peer; + l.makeVisible (index); + } +} + +/*************************************************************************/ + +/** + * Returns the index of the last item that was made visible via the + * <code>makeVisible()</code> method. + * + * @return The index of the last item made visible via the + * <code>makeVisible()</code> method. + */ +public int +getVisibleIndex() +{ + return(visibleIndex); +} + +/*************************************************************************/ + +/** + * Makes the item at the specified index selected. + * + * @param index The index of the item to select. + */ +public synchronized void +select(int index) +{ + ListPeer lp = (ListPeer)getPeer(); + if (lp != null) + lp.select(index); +} + +/*************************************************************************/ + +/** + * Makes the item at the specified index not selected. + * + * @param index The index of the item to unselect. + */ +public synchronized void +deselect(int index) +{ + ListPeer lp = (ListPeer)getPeer(); + if (lp != null) + lp.deselect(index); +} + +/*************************************************************************/ + +/** + * Notifies this object to create its native peer. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createList (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Notifies this object to destroy its native peer. + */ +public void +removeNotify() +{ + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Adds the specified <code>ActionListener</code> to the list of + * registered listeners for this object. + * + * @param listener The listener to add. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes the specified <code>ActionListener</code> from the list of + * registers listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); +} + +/*************************************************************************/ + +/** + * Adds the specified <code>ItemListener</code> to the list of + * registered listeners for this object. + * + * @param listener The listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes the specified <code>ItemListener</code> from the list of + * registers listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes the specified event for this object. If the event is an + * instance of <code>ActionEvent</code> then the + * <code>processActionEvent()</code> method is called. Similarly, if the + * even is an instance of <code>ItemEvent</code> then the + * <code>processItemEvent()</code> method is called. Otherwise the + * superclass method is called to process this event. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * This method processes the specified event by dispatching it to any + * registered listeners. Note that this method will only get called if + * action events are enabled. This will happen automatically if any + * listeners are added, or it can be done "manually" by calling + * the <code>enableEvents()</code> method. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + action_listeners.actionPerformed(event); +} + +/*************************************************************************/ + +/** + * This method processes the specified event by dispatching it to any + * registered listeners. Note that this method will only get called if + * item events are enabled. This will happen automatically if any + * listeners are added, or it can be done "manually" by calling + * the <code>enableEvents()</code> method. + * + * @param event The event to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return "multiple=" + multipleMode + ",rows=" + rows + super.paramString(); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this <code>List</code>. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == ActionListener.class) + return AWTEventMulticaster.getListeners (action_listeners, listenerType); + + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns all action listeners registered to this object. + */ + public ActionListener[] getActionListeners () + { + return (ActionListener[]) getListeners (ActionListener.class); + } + + /** + * Returns all action listeners registered to this object. + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + // Accessibility internal class + protected class AccessibleAWTList extends AccessibleAWTComponent + implements AccessibleSelection, ItemListener, ActionListener + { + protected class AccessibleAWTListChild extends AccessibleAWTComponent + implements Accessible + { + private int index; + private List parent; + + public AccessibleAWTListChild(List parent, int indexInParent) + { + this.parent = parent; + index = indexInParent; + if (parent == null) + index = -1; + } + + /* (non-Javadoc) + * @see javax.accessibility.Accessible#getAccessibleContext() + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST_ITEM; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (parent.isIndexSelected(index)) + states.add(AccessibleState.SELECTED); + return states; + } + + public int getAccessibleIndexInParent() + { + return index; + } + + } + + public AccessibleAWTList() + { + addItemListener(this); + addActionListener(this); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + states.add(AccessibleState.SELECTABLE); + if (isMultipleMode()) + states.add(AccessibleState.MULTISELECTABLE); + return states; + } + + public int getAccessibleChildrenCount() + { + return getItemCount(); + } + + public Accessible getAccessibleChild(int i) + { + if (i >= getItemCount()) + return null; + return new AccessibleAWTListChild(List.this, i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelectionCount() + */ + public int getAccessibleSelectionCount() + { + return getSelectedIndexes().length; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelection() + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelection(int) + */ + public Accessible getAccessibleSelection(int i) + { + int[] items = getSelectedIndexes(); + if (i >= items.length) + return null; + return new AccessibleAWTListChild(List.this, items[i]); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#isAccessibleChildSelected(int) + */ + public boolean isAccessibleChildSelected(int i) + { + return isIndexSelected(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#addAccessibleSelection(int) + */ + public void addAccessibleSelection(int i) + { + select(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#removeAccessibleSelection(int) + */ + public void removeAccessibleSelection(int i) + { + deselect(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#clearAccessibleSelection() + */ + public void clearAccessibleSelection() + { + for (int i = 0; i < getItemCount(); i++) + deselect(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#selectAllAccessibleSelection() + */ + public void selectAllAccessibleSelection() + { + if (isMultipleMode()) + for (int i = 0; i < getItemCount(); i++) + select(i); + } + + /* (non-Javadoc) + * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) + */ + public void itemStateChanged(ItemEvent event) + { + } + + /* (non-Javadoc) + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent event) + { + } + + } + + /** + * Gets the AccessibleContext associated with this <code>List</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTList(); + return accessibleContext; + } +} // class List diff --git a/libjava/classpath/java/awt/MediaTracker.java b/libjava/classpath/java/awt/MediaTracker.java new file mode 100644 index 00000000000..9abfde6efc2 --- /dev/null +++ b/libjava/classpath/java/awt/MediaTracker.java @@ -0,0 +1,697 @@ +/* MediaTracker.java -- Class used for keeping track of images + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ImageObserver; +import java.util.ArrayList; + +/** + * This class is used for keeping track of the status of various media + * objects. + * + * Media objects are tracked by assigning them an ID. It is possible + * to assign the same ID to mutliple objects, effectivly grouping them + * together. In this case the status flags ({@link #statusID}) and error flag + * (@link #isErrorID} and {@link #getErrorsID}) are ORed together. This + * means that you cannot say exactly which media object has which status, + * at most you can say that there <em>are</em> certain media objects with + * some certain status. + * + * At the moment only images are supported by this class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Bryce McKinlay + */ +public class MediaTracker implements java.io.Serializable +{ + /** Indicates that the media is still loading. */ + public static final int LOADING = 1 << 0; + + /** Indicates that the loading operation has been aborted. */ + public static final int ABORTED = 1 << 1; + + /** Indicates that an error has occured during loading of the media. */ + public static final int ERRORED = 1 << 2; + + /** Indicates that the media has been successfully and completely loaded. */ + public static final int COMPLETE = 1 << 3; + + /** The component on which the media is eventually been drawn. */ + Component target; + + /** The head of the linked list of tracked media objects. */ + MediaEntry head; + + /** Our serialVersionUID for serialization. */ + static final long serialVersionUID = -483174189758638095L; + + /** + * This represents a media object that is tracked by a MediaTracker. + * It also implements a simple linked list. + */ + // FIXME: The serialized form documentation says MediaEntry is a + // serializable field, but the serialized form of MediaEntry itself + // doesn't appear to be documented. + class MediaEntry implements ImageObserver + { + /** The ID of the media object. */ + int id; + + /** The media object. (only images are supported ATM). */ + Image image; + + /** The link to the next entry in the list. */ + MediaEntry next; + + /** The tracking status. */ + int status; + + /** The width of the image. */ + int width; + + /** The height of the image. */ + int height; + + /** + * Receives notification from an {@link java.awt.image.ImageProducer} + * that more data of the image is available. + * + * @param img the image that is updated + * @param flags flags from the ImageProducer that indicate the status + * of the loading process + * @param x the X coordinate of the upper left corner of the image + * @param y the Y coordinate of the upper left corner of the image + * @param width the width of the image + * @param height the height of the image + * + * @return <code>true</code> if more data is needed, <code>false</code> + * otherwise + * + * @see java.awt.image.ImageObserver + */ + public boolean imageUpdate(Image img, int flags, int x, int y, + int width, int height) + { + if ((flags & ABORT) != 0) + status = ABORTED; + else if ((flags & ERROR) != 0) + status = ERRORED; + else if ((flags & ALLBITS) != 0) + status = COMPLETE; + else + status = 0; + + synchronized (MediaTracker.this) + { + MediaTracker.this.notifyAll(); + } + + // If status is not COMPLETE then we need more updates. + return ((status & (COMPLETE | ERRORED | ABORTED)) == 0); + } + } + + /** + * Constructs a new MediaTracker for the component <code>c</code>. The + * component should be the component that uses the media (i.e. draws it). + * + * @param c the Component that wants to use the media + */ + public MediaTracker(Component c) + { + target = c; + } + + /** + * Adds an image to the tracker with the specified <code>ID</code>. + * + * @param image the image to be added + * @param id the ID of the tracker list to which the image is added + */ + public void addImage(Image image, int id) + { + MediaEntry e = new MediaEntry(); + e.id = id; + e.image = image; + synchronized(this) + { + e.next = head; + head = e; + } + } + + /** + * Adds an image to the tracker with the specified <code>ID</code>. + * The image is expected to be rendered with the specified width and + * height. + * + * @param image the image to be added + * @param id the ID of the tracker list to which the image is added + * @param width the width of the image + * @param height the height of the image + */ + public void addImage(Image image, int id, int width, int height) + { + MediaEntry e = new MediaEntry(); + e.id = id; + e.image = image; + e.width = width; + e.height = height; + synchronized(this) + { + e.next = head; + head = e; + } + } + + /** + * Checks if all media objects have finished loading, i.e. are + * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. + * + * If the media objects are not already loading, a call to this + * method does <em>not</em> start loading. This is equivalent to + * a call to <code>checkAll(false)</code>. + * + * @return if all media objects have finished loading either by beeing + * complete, have been aborted or errored. + */ + public boolean checkAll() + { + return checkAll(false); + } + + /** + * Checks if all media objects have finished loading, i.e. are + * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. + * + * If the media objects are not already loading, and <code>load</code> + * is <code>true</code> then a call to this + * method starts loading the media objects. + * + * @param load if <code>true</code> this method starts loading objects + * that are not already loading + * + * @return if all media objects have finished loading either by beeing + * complete, have been aborted or errored. + */ + public boolean checkAll(boolean load) + { + MediaEntry e = head; + boolean result = true; + + while (e != null) + { + if ((e.status & (COMPLETE | ERRORED | ABORTED)) == 0) + { + if (load && ((e.status & LOADING) == 0)) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + boolean complete = (e.status + & (COMPLETE | ABORTED | ERRORED)) != 0; + if (!complete) + result = false; + } + else + result = false; + } + e = e.next; + } + return result; + } + + /** + * Checks if any of the registered media objects has encountered an error + * during loading. + * + * @return <code>true</code> if at least one media object has encountered + * an error during loading, <code>false</code> otherwise + * + */ + public boolean isErrorAny() + { + MediaEntry e = head; + while (e != null) + { + if ((e.status & ERRORED) != 0) + return true; + e = e.next; + } + return false; + } + + /** + * Returns all media objects that have encountered errors during loading. + * + * @return an array of all media objects that have encountered errors + * or <code>null</code> if there were no errors at all + */ + public Object[] getErrorsAny() + { + MediaEntry e = head; + ArrayList result = null; + while (e != null) + { + if ((e.status & ERRORED) != 0) + { + if (result == null) + result = new ArrayList(); + result.add(e.image); + } + e = e.next; + } + if (result == null) + return null; + else + return result.toArray(); + } + + /** + * Waits for all media objects to finish loading, either by completing + * successfully or by aborting or encountering an error. + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public void waitForAll() throws InterruptedException + { + synchronized (this) + { + while (checkAll(true) == false) + wait(); + } + } + + /** + * Waits for all media objects to finish loading, either by completing + * successfully or by aborting or encountering an error. + * + * This method waits at most <code>ms</code> milliseconds. If the + * media objects have not completed loading within this timeframe, this + * method returns <code>false</code>, otherwise <code>true</code>. + * + * @param ms timeframe in milliseconds to wait for the media objects to + * finish + * + * @return <code>true</code> if all media objects have successfully loaded + * within the timeframe, <code>false</code> otherwise + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public boolean waitForAll(long ms) throws InterruptedException + { + long start = System.currentTimeMillis(); + boolean result = checkAll(true); + synchronized (this) + { + while (result == false) + { + wait(ms); + result = checkAll(true); + if ((System.currentTimeMillis() - start) > ms) + break; + } + } + + return result; + } + + /** + * Returns the status flags of all registered media objects ORed together. + * If <code>load</code> is <code>true</code> then media objects that + * are not already loading will be started to load. + * + * @param load if set to <code>true</code> then media objects that are + * not already loading are started + * + * @return the status flags of all tracked media objects ORed together + */ + public int statusAll(boolean load) + { + int result = 0; + MediaEntry e = head; + while (e != null) + { + if (load && e.status == 0) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + } + result |= e.status; + e = e.next; + } + return result; + } + + /** + * Checks if the media objects with <code>ID</code> have completed loading. + * + * @param id the ID of the media objects to check + * + * @return <code>true</code> if all media objects with <code>ID</code> + * have successfully finished + */ + public boolean checkID(int id) + { + return checkID(id, false); + } + + /** + * Checks if the media objects with <code>ID</code> have completed loading. + * If <code>load</code> is <code>true</code> then media objects that + * are not already loading will be started to load. + * + * @param id the ID of the media objects to check + * @param load if set to <code>true</code> then media objects that are + * not already loading are started + * + * @return <code>true</code> if all media objects with <code>ID</code> + * have successfully finished + */ + public boolean checkID(int id, boolean load) + { + MediaEntry e = head; + boolean result = true; + + while (e != null) + { + if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0)) + { + if (load && ((e.status & LOADING) == 0)) + { + e.status = LOADING; + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + boolean complete = (e.status + & (COMPLETE | ABORTED | ERRORED)) != 0; + if (!complete) + result = false; + } + else + result = false; + } + e = e.next; + } + return result; + } + + /** + * Returns <code>true</code> if any of the media objects with <code>ID</code> + * have encountered errors during loading, false otherwise. + * + * @param id the ID of the media objects to check + * + * @return <code>true</code> if any of the media objects with <code>ID</code> + * have encountered errors during loading, false otherwise + */ + public boolean isErrorID(int id) + { + MediaEntry e = head; + while (e != null) + { + if (e.id == id && ((e.status & ERRORED) != 0)) + return true; + e = e.next; + } + return false; + } + + /** + * Returns all media objects with the specified ID that have encountered + * an error. + * + * @param id the ID of the media objects to check + * + * @return an array of all media objects with the specified ID that + * have encountered an error + */ + public Object[] getErrorsID(int id) + { + MediaEntry e = head; + ArrayList result = null; + while (e != null) + { + if (e.id == id && ((e.status & ERRORED) != 0)) + { + if (result == null) + result = new ArrayList(); + result.add(e.image); + } + e = e.next; + } + if (result == null) + return null; + else + return result.toArray(); + } + + /** + * Waits for all media objects with the specified ID to finish loading, + * either by completing successfully or by aborting or encountering an error. + * + * @param id the ID of the media objects to wait for + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public void waitForID(int id) throws InterruptedException + { + MediaEntry e = head; + synchronized (this) + { + while (checkID (id, true) == false) + wait(); + } + } + + /** + * Waits for all media objects with the specified ID to finish loading, + * either by completing successfully or by aborting or encountering an error. + * + * This method waits at most <code>ms</code> milliseconds. If the + * media objects have not completed loading within this timeframe, this + * method returns <code>false</code>, otherwise <code>true</code>. + * + * @param id the ID of the media objects to wait for + * @param ms timeframe in milliseconds to wait for the media objects to + * finish + * + * @return <code>true</code> if all media objects have successfully loaded + * within the timeframe, <code>false</code> otherwise + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public boolean waitForID(int id, long ms) throws InterruptedException + { + MediaEntry e = head; + long start = System.currentTimeMillis(); + boolean result = checkID(id, true); + + synchronized (this) + { + while (result == false) + { + wait(ms); + result = checkID(id, true); + if ((System.currentTimeMillis() - start) > ms) + break; + } + } + + return result; + } + + /** + * Returns the status flags of the media objects with the specified ID + * ORed together. + * + * If <code>load</code> is <code>true</code> then media objects that + * are not already loading will be started to load. + * + * @param load if set to <code>true</code> then media objects that are + * not already loading are started + * + * @return the status flags of all tracked media objects ORed together + */ + public int statusID(int id, boolean load) + { + int result = 0; + MediaEntry e = head; + while (e != null) + { + if (e.id == id) + { + if (load && e.status == 0) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + } + result |= e.status; + } + e = e.next; + } + return result; + } + + /** + * Removes an image from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.image == image) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + prev = e; + e = e.next; + } + } + } + + /** + * Removes an image with the specified ID from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image, int id) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.id == id && e.image == image) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + else + prev = e; + e = e.next; + } + } + } + + /** + * Removes an image with the specified ID and scale from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image, int id, int width, int height) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.id == id && e.image == image + && e.width == width && e.height == height) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + else + prev = e; + e = e.next; + } + } + } +} diff --git a/libjava/classpath/java/awt/Menu.java b/libjava/classpath/java/awt/Menu.java new file mode 100644 index 00000000000..56ceccfc542 --- /dev/null +++ b/libjava/classpath/java/awt/Menu.java @@ -0,0 +1,468 @@ +/* Menu.java -- A Java AWT Menu + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.MenuPeer; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class represents a pull down or tear off menu in Java's AWT. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Menu extends MenuItem implements MenuContainer, Serializable +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = -8809584163345499784L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The actual items in the menu + */ +private Vector items = new Vector(); + +/** + * @serial Flag indicating whether or not this menu is a tear off + */ +private boolean tearOff; + +/** + * @serial Indicates whether or not this is a help menu. + */ +private boolean isHelpMenu; + + /* + * @serial Unused in this implementation, but present in Sun's + * serialization spec. Value obtained via reflection. + */ + private int menuSerializedDataVersion = 1; + +static final transient String separatorLabel = "-"; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>Menu</code> with no label and that + * is not a tearoff; + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Menu() +{ +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Menu</code> that is not a tearoff and + * that has the specified label. + * + * @param label The menu label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Menu(String label) +{ + this(label, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>Menu</code> with the specified + * label and tearoff status. + * + * @param label The label for this menu + * @param isTearOff <code>true</code> if this menu is a tear off menu, + * <code>false</code> otherwise. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +Menu(String label, boolean isTearOff) +{ + super(label); + + tearOff = isTearOff; + + if (label.equals("Help")) + isHelpMenu = true; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Tests whether or not this menu is a tearoff. + * + * @return <code>true</code> if this menu is a tearoff, <code>false</code> + * otherwise. + */ +public boolean +isTearOff() +{ + return(tearOff); +} + +/*************************************************************************/ + +/** + * Returns the number of items in this menu. + * + * @return The number of items in this menu. + */ +public int +getItemCount() +{ + return countItems (); +} + +/** + * Returns the number of items in this menu. + * + * @return The number of items in this menu. + * + * @deprecated As of JDK 1.1, replaced by getItemCount(). + */ +public int countItems () +{ + return items.size (); +} + +/*************************************************************************/ + +/** + * Returns the item at the specified index. + * + * @return The item at the specified index. + * + * @exception ArrayIndexOutOfBoundsException If the index value is not valid. + */ +public MenuItem +getItem(int index) +{ + return((MenuItem)items.elementAt(index)); +} + +/*************************************************************************/ + +/** + * Adds the specified item to this menu. If it was previously part of + * another menu, it is first removed from that menu. + * + * @param item The new item to add. + * + * @return The item that was added. + */ +public MenuItem +add(MenuItem item) +{ + items.addElement(item); + if (item.parent != null) + { + item.parent.remove(item); + } + item.parent = this; + + if (peer != null) + { + MenuPeer mp = (MenuPeer) peer; + mp.addItem(item); + } + + return item; +} + +/*************************************************************************/ + +/** + * Add an item with the specified label to this menu. + * + * @param label The label of the menu item to add. + */ +public void +add(String label) +{ + add(new MenuItem(label)); +} + +/*************************************************************************/ + +/** + * Inserts the specified menu item into this menu at the specified index. + * + * @param item The menu item to add. + * @param index The index of the menu item. + * + * @exception IllegalArgumentException If the index is less than zero. + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ +public void +insert(MenuItem item, int index) +{ + if (index < 0) + throw new IllegalArgumentException("Index is less than zero"); + + MenuPeer peer = (MenuPeer) getPeer(); + if (peer == null) + return; + + int count = getItemCount (); + + if (index >= count) + peer.addItem (item); + else + { + for (int i = count - 1; i >= index; i--) + peer.delItem (i); + + peer.addItem (item); + + for (int i = index; i < count; i++) + peer.addItem ((MenuItem) items.elementAt (i)); + } + + items.insertElementAt(item, index); +} + +/*************************************************************************/ + +/** + * Inserts an item with the specified label into this menu at the specified index. + * + * @param label The label of the item to add. + * @param index The index of the menu item. + * + * @exception IllegalArgumentException If the index is less than zero. + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ +public void +insert(String label, int index) +{ + insert(new MenuItem(label), index); +} + +/*************************************************************************/ + +/** + * Adds a separator bar at the current menu location. + */ +public void +addSeparator() +{ + add(new MenuItem(separatorLabel)); +} + +/*************************************************************************/ + +/** + * Inserts a separator bar at the specified index value. + * + * @param index The index at which to insert a separator bar. + * + * @exception IllegalArgumentException If the index is less than zero. + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ +public void +insertSeparator(int index) +{ + insert(new MenuItem(separatorLabel), index); +} + +/*************************************************************************/ + +/** + * Deletes the item at the specified index from this menu. + * + * @param index The index of the item to remove. + * + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ +public synchronized void +remove(int index) +{ + items.removeElementAt(index); + + MenuPeer mp = (MenuPeer)getPeer(); + if (mp != null) + mp.delItem(index); +} + +/*************************************************************************/ + +/** + * Removes the specifed item from the menu. If the specified component + * does not exist, this method does nothing. + * + * @param item The component to remove. + */ +public void +remove(MenuComponent item) +{ + int index = items.indexOf(item); + if (index == -1) + return; + + remove(index); +} + +/*************************************************************************/ + +/** + * Removes all the elements from this menu. + */ +public synchronized void +removeAll() +{ + int count = getItemCount(); + for(int i = 0; i < count; i++) + { + // We must always remove item 0. + remove(0); + } +} + +/*************************************************************************/ + +/** + * Creates the native peer for this object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit().createMenu(this); + Enumeration e = items.elements(); + while (e.hasMoreElements()) + { + MenuItem mi = (MenuItem)e.nextElement(); + mi.addNotify(); + } + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Destroys the native peer for this object. + */ +public void +removeNotify() +{ + Enumeration e = items.elements(); + while (e.hasMoreElements()) + { + MenuItem mi = (MenuItem) e.nextElement(); + mi.removeNotify(); + } + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this menu. + * + * @return A debugging string for this menu. + */ +public String +paramString() +{ + return (",tearOff=" + tearOff + ",isHelpMenu=" + isHelpMenu + + super.paramString()); +} + + /** + * Basic Accessibility class for Menu. Details get provided in derived + * classes. + */ + protected class AccessibleAWTMenu extends AccessibleAWTMenuItem + { + protected AccessibleAWTMenu() + { + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU; + } + } + + /** + * Gets the AccessibleContext associated with this <code>Menu</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenu(); + return accessibleContext; + } + +} // class Menu diff --git a/libjava/classpath/java/awt/MenuBar.java b/libjava/classpath/java/awt/MenuBar.java new file mode 100644 index 00000000000..4089fe189e0 --- /dev/null +++ b/libjava/classpath/java/awt/MenuBar.java @@ -0,0 +1,423 @@ +/* MenuBar.java -- An AWT menu bar class + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuComponentPeer; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implements a menu bar in the AWT system. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class MenuBar extends MenuComponent + implements MenuContainer, Serializable, Accessible +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = -4930327919388951260L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The menu used for providing help information + */ +private Menu helpMenu; + +/** + * @serial The menus contained in this menu bar. + */ +private Vector menus = new Vector(); + + /** + * The accessible context for this component. + * + * @see #getAccessibleContext() + * @serial ignored. + */ + private transient AccessibleContext accessibleContext; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>MenuBar</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +MenuBar() +{ + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the help menu for this menu bar. This may be <code>null</code>. + * + * @return The help menu for this menu bar. + */ +public Menu +getHelpMenu() +{ + return(helpMenu); +} + +/*************************************************************************/ + +/** + * Sets the help menu for this menu bar. + * + * @param menu The new help menu for this menu bar. + */ +public synchronized void +setHelpMenu(Menu menu) +{ + if (helpMenu != null) + { + helpMenu.removeNotify (); + helpMenu.parent = null; + } + helpMenu = menu; + + if (menu.parent != null) + menu.parent.remove (menu); + menu.parent = this; + + MenuBarPeer peer = (MenuBarPeer) getPeer (); + if (peer != null) + { + menu.addNotify(); + peer.addHelpMenu (menu); + } +} + +/*************************************************************************/ + +/** Add a menu to this MenuBar. If the menu has already has a + * parent, it is first removed from its old parent before being + * added. + * + * @param menu The menu to add. + * + * @return The menu that was added. + */ +public synchronized Menu +add(Menu menu) +{ + if (menu.parent != null) + menu.parent.remove (menu); + + menu.parent = this; + menus.addElement(menu); + + if (peer != null) + { + menu.addNotify(); + } + + return(menu); +} + +/*************************************************************************/ + +/** + * Removes the menu at the specified index. + * + * @param index The index of the menu to remove from the menu bar. + */ +public synchronized void +remove(int index) +{ + Menu m = (Menu) menus.get (index); + menus.remove (index); + m.removeNotify (); + m.parent = null; + + if (peer != null) + { + MenuBarPeer mp = (MenuBarPeer) peer; + mp.delMenu (index); + } +} + +/*************************************************************************/ + +/** + * Removes the specified menu from the menu bar. + * + * @param menu The menu to remove from the menu bar. + */ +public void +remove(MenuComponent menu) +{ + int index = menus.indexOf(menu); + if (index == -1) + return; + + remove(index); +} + +/*************************************************************************/ + +/** + * Returns the number of elements in this menu bar. + * + * @return The number of elements in the menu bar. + */ +public int +getMenuCount() +{ + return countMenus (); +} + +/*************************************************************************/ + +/** + * Returns the number of elements in this menu bar. + * + * @return The number of elements in the menu bar. + * + * @deprecated This method is deprecated in favor of <code>getMenuCount()</code>. + */ +public int +countMenus() +{ + return menus.size () + (getHelpMenu () == null ? 0 : 1); +} + +/*************************************************************************/ + +/** + * Returns the menu at the specified index. + * + * @param index the index of the menu + * + * @return The requested menu. + * + * @exception ArrayIndexOutOfBoundsException If the index is not valid. + */ +public Menu +getMenu(int index) +{ + return((Menu)menus.elementAt(index)); +} + +/*************************************************************************/ + +/** + * Creates this object's native peer. + */ +public void +addNotify() +{ + if (getPeer() == null) + setPeer((MenuComponentPeer)getToolkit().createMenuBar(this)); + Enumeration e = menus.elements(); + while (e.hasMoreElements()) + { + Menu mi = (Menu)e.nextElement(); + mi.addNotify(); + } + if (helpMenu != null) + { + helpMenu.addNotify(); + ((MenuBarPeer) peer).addHelpMenu(helpMenu); + } +} + +/*************************************************************************/ + +/** + * Destroys this object's native peer. + */ +public void +removeNotify() +{ + Enumeration e = menus.elements(); + while (e.hasMoreElements()) + { + Menu mi = (Menu) e.nextElement(); + mi.removeNotify(); + } + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Returns a list of all shortcuts for the menus in this menu bar. + * + * @return A list of all shortcuts for the menus in this menu bar. + */ +public synchronized Enumeration +shortcuts() +{ + Vector shortcuts = new Vector(); + Enumeration e = menus.elements(); + + while (e.hasMoreElements()) + { + Menu menu = (Menu)e.nextElement(); + if (menu.getShortcut() != null) + shortcuts.addElement(menu.getShortcut()); + } + + return(shortcuts.elements()); +} + +/*************************************************************************/ + +/** + * Returns the menu item for the specified shortcut, or <code>null</code> + * if no such item exists. + * + * @param shortcut The shortcut to return the menu item for. + * + * @return The menu item for the specified shortcut. + */ +public MenuItem +getShortcutMenuItem(MenuShortcut shortcut) +{ + Enumeration e = menus.elements(); + + while (e.hasMoreElements()) + { + Menu menu = (Menu)e.nextElement(); + MenuShortcut s = menu.getShortcut(); + if ((s != null) && (s.equals(shortcut))) + return(menu); + } + + return(null); +} + +/*************************************************************************/ + +/** + * Deletes the specified menu shortcut. + * + * @param shortcut The shortcut to delete. + */ +public void +deleteShortcut(MenuShortcut shortcut) +{ + MenuItem it; + // This is a slow implementation, but it probably doesn't matter. + while ((it = getShortcutMenuItem (shortcut)) != null) + it.deleteShortcut (); +} + +/** + * Gets the AccessibleContext associated with this <code>MenuBar</code>. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenuBar(); + return accessibleContext; +} + +/** + * This class provides accessibility support for AWT menu bars. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +protected class AccessibleAWTMenuBar + extends AccessibleAWTMenuComponent +{ + + /** + * Compatible with JDK 1.4.2 revision 5 + */ + private static final long serialVersionUID = -8577604491830083815L; + + /** + * This is the default constructor, which simply calls the default + * constructor of the superclass. + */ + protected AccessibleAWTMenuBar() + { + super(); + } + + /** + * Returns the accessible role relating to the menu bar. + * + * @return <code>AccessibleRole.MENU_BAR</code>. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_BAR; + } + +} // class AccessibleAWTMenuBar + +} // class MenuBar diff --git a/libjava/classpath/java/awt/MenuComponent.java b/libjava/classpath/java/awt/MenuComponent.java new file mode 100644 index 00000000000..ec6980e10ca --- /dev/null +++ b/libjava/classpath/java/awt/MenuComponent.java @@ -0,0 +1,1324 @@ +/* MenuComponent.java -- Superclass of all AWT menu components + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.peer.MenuComponentPeer; +import java.io.Serializable; +import java.util.Locale; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; + +/** + * This is the superclass of all menu AWT widgets. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public abstract class MenuComponent implements Serializable +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = -4536902356223894379L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The font for this component. + * + * @see #getFont() + * @see #setFont(java.awt.Font) + * @serial the component's font. + */ + private Font font; + + /** + * The name of the component. + * + * @see #getName() + * @see #setName(String) + * @serial the component's name. + */ + private String name; + + /** + * The parent of this component. + * + * @see #getParent() + * @see #setParent(java.awt.MenuContainer) + * @serial ignored. + */ + transient MenuContainer parent; + + /** + * The native peer for this component. + * + * @see #getPeer() + * @see #setPeer(java.awt.peer.MenuComponentPeer) + * @serial ignored. + */ + transient MenuComponentPeer peer; + + /** + * The synchronization locking object for this component. + * + * @serial ignored. + */ + private transient Object tree_lock = this; + + /** + * The toolkit for this object. + * + * @see #getToolkit() + * @serial ignored. + */ + private static transient Toolkit toolkit = Toolkit.getDefaultToolkit(); + + /** + * The accessible context for this component. + * + * @see #getAccessibleContext() + * @serial the accessibility information for this component. + */ + AccessibleContext accessibleContext; + + /** + * Was the name of the component set? This value defaults + * to false and becomes true after a call to <code>setName()</code>. + * Please note that this does not guarantee that name will then + * be non-null, as this may be the value passed to <code>setName()</code>. + * + * @see #setName(String) + * @serial true if the name value has been explicitly set by calling + * <code>setName()</code>. + */ + private boolean nameExplicitlySet; + + /** + * Does this component handle new events? Events will be handled + * by this component if this is true. Otherwise, they will be forwarded + * up the component hierarchy. This implementation does not use this + * variable; it is merely provided for serialization compatability. + * + * @see #dispatchEvent(AWTEvent) + * @serial true if events are to be processed locally. Unused. + */ + private boolean newEventsOnly; + + /** + * The focus listener chain handler which deals with focus events for + * the accessible context of this component. + * + * @see AccessibleAWTMenuComponent#addFocusListener(java.awt.event.FocusListener) + * @serial ignored. + * This is package-private to avoid an accessor method. + */ + transient FocusListener focusListener; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Default constructor for subclasses. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +MenuComponent() +{ + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the font in use for this component. + * + * @return The font for this component. + */ +public Font +getFont() +{ + if (font != null) + return font; + + if (parent != null) + return parent.getFont (); + + return null; +} + +/*************************************************************************/ + +/** + * Sets the font for this component to the specified font. + * + * @param font The new font for this component. + */ +public void +setFont(Font font) +{ + this.font = font; +} + +/*************************************************************************/ + +/** + * Returns the name of this component. + * + * @return The name of this component. + */ +public String +getName() +{ + return(name); +} + +/*************************************************************************/ + +/** + * Sets the name of this component to the specified name. + * + * @param name The new name of this component. + */ +public void +setName(String name) +{ + this.name = name; + nameExplicitlySet = true; +} + +/*************************************************************************/ + +/** + * Returns the parent of this component. + * + * @return The parent of this component. + */ +public MenuContainer +getParent() +{ + return(parent); +} + +/*************************************************************************/ + +// Sets the parent of this component. +final void +setParent(MenuContainer parent) +{ + this.parent = parent; +} + +/*************************************************************************/ + +/** + * Returns the native windowing system peer for this component. + * + * @return The peer for this component. + * + * @deprecated + */ +public MenuComponentPeer +getPeer() +{ + return(peer); +} + +/*************************************************************************/ + +// Sets the peer for this component. +final void +setPeer(MenuComponentPeer peer) +{ + this.peer = peer; +} + +/*************************************************************************/ + +/** + * Destroys this component's native peer + */ +public void +removeNotify() +{ + if (peer != null) + peer.dispose(); + peer = null; +} + +/*************************************************************************/ + +/** + * Returns the toolkit in use for this component. + * + * @return The toolkit for this component. + */ +final Toolkit +getToolkit() +{ + return(toolkit); +} + +/*************************************************************************/ + +/** + * Returns the object used for synchronization locks on this component + * when performing tree and layout functions. + * + * @return The synchronization lock for this component. + */ +protected final Object +getTreeLock() +{ + return(tree_lock); +} + +/*************************************************************************/ + +// The sync lock object for this component. +final void +setTreeLock(Object tree_lock) +{ + this.tree_lock = tree_lock; +} + +/*************************************************************************/ + +/** + * AWT 1.0 event dispatcher. + * + * @deprecated Deprecated in favor of <code>dispatchEvent()</code>. + * @return true if the event was dispatched, false otherwise. + */ +public boolean +postEvent(Event event) +{ + // This is overridden by subclasses that support events. + return false; +} +/*************************************************************************/ + +/** + * Sends this event to this component or a subcomponent for processing. + * + * @param event The event to dispatch + */ +public final void dispatchEvent(AWTEvent event) +{ + // See comment in Component.dispatchEvent(). + dispatchEventImpl(event); +} + + +/** + * Implementation of dispatchEvent. Allows trusted package classes + * to dispatch additional events first. This implementation first + * translates <code>event</code> to an AWT 1.0 event and sends the + * result to {@link #postEvent}. The event is then + * passed on to {@link #processEvent} for local processing. + * + * @param event the event to dispatch. + */ +void dispatchEventImpl(AWTEvent event) +{ + Event oldStyleEvent; + + // This is overridden by subclasses that support events. + /* Convert AWT 1.1 event to AWT 1.0 event */ + oldStyleEvent = Component.translateEvent(event); + if (oldStyleEvent != null) + { + postEvent(oldStyleEvent); + } + /* Do local processing */ + processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes the specified event. In this class, this method simply + * calls one of the more specific event handlers. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + /* + Pass a focus event to the focus listener for + the accessibility context. + */ + if (event instanceof FocusEvent) + { + if (focusListener != null) + { + switch (event.id) + { + case FocusEvent.FOCUS_GAINED: + focusListener.focusGained((FocusEvent) event); + break; + case FocusEvent.FOCUS_LOST: + focusListener.focusLost((FocusEvent) event); + break; + } + } + } +} + +/*************************************************************************/ + +/** + * Returns a string representation of this component. + * + * @return A string representation of this component + */ +public String +toString() +{ + return this.getClass().getName() + "[" + paramString() + "]"; +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this component + */ +protected String +paramString() +{ + return "name=" + getName(); +} + +/** + * Gets the AccessibleContext associated with this <code>MenuComponent</code>. + * As an abstract class, we return null. Concrete subclasses should return + * their implementation of the accessibility context. + * + * @return null. + */ + +public AccessibleContext getAccessibleContext() +{ + return null; +} + +/** + * This class provides a base for the accessibility support of menu + * components. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +protected abstract class AccessibleAWTMenuComponent + extends AccessibleContext + implements Serializable, AccessibleComponent, AccessibleSelection +{ + + /** + * Compatible with JDK 1.4.2 revision 5 + */ + private static final long serialVersionUID = -4269533416223798698L; + + /** + * This is the default constructor. It should be called by + * concrete subclasses to ensure necessary groundwork is completed. + */ + protected AccessibleAWTMenuComponent() + { + } + + /** + * Replaces or supplements the component's selection with the + * <code>Accessible</code> child at the supplied index. If + * the component supports multiple selection, the child is + * added to the current selection. Otherwise, the current + * selection becomes the specified child. If the child is + * already selected, nothing happens. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the specified child within a + * zero-based list of the component's children. + */ + public void addAccessibleSelection(int index) + { + /* Subclasses with children should implement this */ + } + + /** + * Registers the specified focus listener to receive + * focus events from this component. + * + * @param listener the new focus listener. + */ + public void addFocusListener(FocusListener listener) + { + /* + * Chain the new focus listener to the existing chain + * of focus listeners. Each new focus listener is + * coupled via multicasting to the existing chain. + */ + focusListener = AWTEventMulticaster.add(focusListener, listener); + } + + /** + * Clears the component's current selection. Following + * the calling of this method, no children of the component + * will be selected. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + */ + public void clearAccessibleSelection() + { + } + + /** + * Returns true if the specified point lies within the + * component. The supplied co-ordinates are assumed to + * be relative to the co-ordinate system of the component + * itself. Thus, the point (0,0) is the upper left corner + * of this component. + * <br /> + * <br /> + * Please note that this method depends on a correctly implemented + * version of the <code>getBounds()</code> method. Subclasses + * must provide the bounding rectangle via <code>getBounds()</code> + * in order for this method to work. + * + * @param point the point to check against this component. + * @return true if the point is within this component. + * @see #getBounds() + */ + public boolean contains(Point point) + { + /* + We can simply return the result of a + test for containment in the bounding rectangle + */ + return getBounds().contains(point); + } + + /** + * Returns the <code>Accessible</code> child of this component present + * at the specified point. The supplied co-ordinates are + * assumed to be relative to the co-ordinate system of this + * component (the parent of any returned accessible). Thus, + * the point (0,0) is the upper left corner of this menu + * component. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param point the point at which the returned accessible + * is located. + * @return null. + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns the <code>Accessible</code> child at the supplied + * index within the list of children of this component. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the <code>Accessible</code> child + * to retrieve. + * @return null. + */ + public Accessible getAccessibleChild(int index) + { + return null; + } + + /** + * Returns the number of children of this component which + * implement the <code>Accessible</code> interface. If + * all children of this component are accessible, then + * the returned value will be the same as the number of + * children. + * <br /> + * <br /> + * + * @return 0. + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Retrieves the <code>AccessibleComponent</code> associated + * with this accessible context and its component. As the + * context itself implements <code>AccessibleComponent</code>, + * this is the return value. + * + * @return the context itself. + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Returns the accessible name for this menu component. This + * is the name given to the component, which may be null if + * not set using <code>setName()</code>. + * <br /> + * <br /> + * The name is not the most appropriate description of this + * object. Subclasses should preferably provide a more + * accurate description. For example, a File menu could + * have the description `Lists commands related to the + * file system'. + * + * @return a description of the component. Currently, + * this is just the contents of the name property. + * @see MenuComponent#setName(String) + */ + public String getAccessibleDescription() + { + return MenuComponent.this.getName(); + } + + /** + * Retrieves the index of this component within its parent. + * If no parent exists, -1 is returned. + * + * @return -1 as the parent, a <code>MenuContainer</code> + * is not <code>Accessible</code>. + */ + public int getAccessibleIndexInParent() + { + return -1; + } + + /** + * Returns the accessible name of this component. This + * is the name given to the component, which may be null if + * not set using <code>setName()</code>. + * <br /> + * <br /> + * The name property is not the most suitable string to return + * for this method. The string should be localized, and + * relevant to the operation of the component. For example, + * it could be the text of a menu item. However, this can + * not be used at this level of abstraction, so it is the + * responsibility of subclasses to provide a more appropriate + * name. + * + * @return a localized name for this component. Currently, this + * is just the contents of the name property. + * @see MenuComponent#setName(String) + */ + public String getAccessibleName() + { + return MenuComponent.this.getName(); + } + + /** + * Returns the <code>Accessible</code> parent of this component. + * As the parent of a <code>MenuComponent</code> is a + * <code>MenuContainer</code>, which doesn't implement + * <code>Accessible</code>, this method returns null. + * + * @return null. + */ + public Accessible getAccessibleParent() + { + return null; + } + + /** + * Returns the accessible role of this component. + * <br /> + * <br /> + * The abstract implementation of this method returns + * <code>AccessibleRole.AWT_COMPONENT</code>, + * as the abstract component has no specific role. This + * method should be overridden by concrete subclasses, so + * as to return an appropriate role for the component. + * + * @return <code>AccessibleRole.AWT_COMPONENT</code>. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.AWT_COMPONENT; + } + + /** + * Retrieves the <code>AccessibleSelection</code> associated + * with this accessible context and its component. As the + * context itself implements <code>AccessibleSelection</code>, + * this is the return value. + * + * @return the context itself. + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Retrieves the <code>Accessible</code> selected child + * at the specified index. If there are no selected children + * or the index is outside the range of selected children, + * null is returned. Please note that the index refers + * to the index of the child in the list of <strong>selected + * children</strong>, and not the index of the child in + * the list of all <code>Accessible</code> children. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the selected <code>Accessible</code> + * child. + */ + public Accessible getAccessibleSelection(int index) + { + return null; + } + + /** + * Returns a count of the number of <code>Accessible</code> + * children of this component which are currently selected. + * If there are no children currently selected, 0 is returned. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @return 0. + */ + public int getAccessibleSelectionCount() + { + return 0; + } + + /** + * Retrieves the current state of this component + * in an accessible form. For example, a given component + * may be visible, selected, disabled, etc. + * <br /> + * <br /> + * As this class tells us virtually nothing about the component, + * except for its name and font, no state information can be + * provided. This implementation thus returns an empty + * state set, and it is left to concrete subclasses to provide + * a more acceptable and relevant state set. Changes to these + * properties also need to be handled using + * <code>PropertyChangeListener</code>s. + * + * @return an empty <code>AccessibleStateSet</code>. + */ + public AccessibleStateSet getAccessibleStateSet() + { + return new AccessibleStateSet(); + } + + /** + * Returns the background color of the component, or null + * if this property is unsupported. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the + * default system background color used for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the default system background color for menus. + * @see #setBackground(java.awt.Color) + */ + public Color getBackground() + { + return SystemColor.menu; + } + + /** + * Returns a <code>Rectangle</code> which represents the + * bounds of this component. The returned rectangle has the + * height and width of the component's bounds, and is positioned + * at a location relative to this component's parent, the + * <code>MenuContainer</code>. null is returned if bounds + * are not supported by the component. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns null. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return null. + * @see #setBounds(java.awt.Rectangle) + */ + public Rectangle getBounds() + { + return null; + } + + /** + * Returns the <code>Cursor</code> displayed when the pointer + * is positioned over this component. Alternatively, null + * is returned if the component doesn't support the cursor + * property. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the default + * system cursor. Concrete subclasses which handle the drawing + * of an onscreen menu component may override this method and provide + * the appropriate information. + * + * @return the default system cursor. + * @see #setCursor(java.awt.Cursor) + */ + public Cursor getCursor() + { + return Cursor.getDefaultCursor(); + } + + /** + * Returns the <code>Font</code> used for text created by this component. + * + * @return the current font. + * @see #setFont(java.awt.Font) + */ + public Font getFont() + { + return MenuComponent.this.getFont(); + } + + /** + * Retrieves information on the rendering and metrics of the supplied + * font. If font metrics are not supported by this component, null + * is returned. + * <br /> + * <br /> + * The abstract implementation of this method simply uses the toolkit + * to obtain the <code>FontMetrics</code>. Concrete subclasses may + * find it more efficient to invoke their peer class directly, if one + * is available. + * + * @param font the font about which to retrieve rendering and metric + * information. + * @return the metrics of the given font, as provided by the system + * toolkit. + * @throws NullPointerException if the supplied font was null. + */ + public FontMetrics getFontMetrics(Font font) + { + return MenuComponent.this.getToolkit().getFontMetrics(font); + } + + /** + * Returns the foreground color of the component, or null + * if this property is unsupported. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the + * default system text color used for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the default system text color for menus. + * @see #setForeground(java.awt.Color) + */ + public Color getForeground() + { + return SystemColor.menuText; + } + + /** + * Returns the locale currently in use by this component. + * <br /> + * <br /> + * This abstract class has no property relating to the + * locale used by the component, so this method simply + * returns the default locale for the current instance + * of the Java Virtual Machine (JVM). Concrete subclasses + * which maintain such a property should override this method + * and provide the locale information more accurately. + * + * @return the default locale for this JVM instance. + */ + public Locale getLocale() + { + return Locale.getDefault(); + } + + /** + * Returns the location of the component, with co-ordinates + * relative to the parent component and using the co-ordinate + * space of the screen. Thus, the point (0,0) is the upper + * left corner of the parent component. + * <br /> + * <br /> + * Please note that this method depends on a correctly implemented + * version of the <code>getBounds()</code> method. Subclasses + * must provide the bounding rectangle via <code>getBounds()</code> + * in order for this method to work. + * + * @return the location of the component, relative to its parent. + * @see #setLocation(java.awt.Point) + */ + public Point getLocation() + { + /* Simply return the location of the bounding rectangle */ + return getBounds().getLocation(); + } + + /** + * Returns the location of the component, with co-ordinates + * relative to the screen. Thus, the point (0,0) is the upper + * left corner of the screen. null is returned if the component + * is either not on screen or if this property is unsupported. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns null. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the location of the component, relative to the screen. + */ + public Point getLocationOnScreen() + { + return null; + } + + /** + * Returns the size of the component. + * <br /> + * <br /> + * Please note that this method depends on a correctly implemented + * version of the <code>getBounds()</code> method. Subclasses + * must provide the bounding rectangle via <code>getBounds()</code> + * in order for this method to work. + * + * @return the size of the component. + * @see #setSize(java.awt.Dimension) + */ + public Dimension getSize() + { + /* Simply return the size of the bounding rectangle */ + return getBounds().getSize(); + } + + /** + * Returns true if the accessible child specified by the supplied index + * is currently selected. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the accessible child to check for selection. + * @return false. + */ + public boolean isAccessibleChildSelected(int index) + { + return false; + } + + /** + * Returns true if this component is currently enabled. + * <br /> + * <br /> + * As this abstract component has no properties related to + * its enabled or disabled state, the implementation of this + * method is left to subclasses. + * + * @return false. + * @see #setEnabled(boolean) + */ + public boolean isEnabled() + { + return false; + } + + /** + * Returns true if this component is included in the traversal + * of the current focus from one component to the other. + * <br /> + * <br /> + * As this abstract component has no properties related to + * its ability to accept the focus, the implementation of this + * method is left to subclasses. + * + * @return false. + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Returns true if the component is being shown on screen. + * A component is determined to be shown if it is visible, + * and each parent component is also visible. Please note + * that, even when a component is showing, it may still be + * obscured by other components in front. This method only + * determines if the component is being drawn on the screen. + * <br /> + * <br /> + * As this abstract component and its parent have no properties + * relating to visibility, the implementation of this method is + * left to subclasses. + * + * @return false. + * @see #isVisible() + */ + public boolean isShowing() + { + return false; + } + + /** + * Returns true if the component is visible. A component may + * be visible but not drawn on the screen if one of its parent + * components is not visible. To determine if the component is + * actually drawn on screen, <code>isShowing()</code> should be + * used. + * <br /> + * <br /> + * As this abstract component has no properties relating to its + * visibility, the implementation of this method is left to subclasses. + * + * @return false. + * @see #isShowing() + * @see #setVisible(boolean) + */ + public boolean isVisible() + { + return false; + } + + /** + * Removes the accessible child specified by the supplied index from + * the list of currently selected children. If the child specified + * is not selected, nothing happens. + * <br /> + * <br /> + * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the <code>Accessible</code> child. + */ + public void removeAccessibleSelection(int index) + { + /* Subclasses with children should implement this */ + } + + /** + * Removes the specified focus listener from the list of registered + * focus listeners for this component. + * + * @param listener the listener to remove. + */ + public void removeFocusListener(FocusListener listener) + { + /* Remove the focus listener from the chain */ + focusListener = AWTEventMulticaster.remove(focusListener, listener); + } + + /** + * Requests that this component gains focus. This depends on the + * component being focus traversable. + * <br /> + * <br /> + * As this abstract component has no properties relating to its + * focus traversability, or access to a peer with request focusing + * abilities, the implementation of this method is left to subclasses. + */ + public void requestFocus() + { + /* Ignored */ + } + + /** + * Selects all <code>Accessible</code> children of this component which + * it is possible to select. The component needs to support multiple + * selections. + * <br /> + * <br /> + * This abstract component provides a simplistic implementation of this + * method, which ignores the ability of the component to support multiple + * selections and simply uses <code>addAccessibleSelection</code> to + * add each <code>Accessible</code> child to the selection. The last + * <code>Accessible</code> component is thus selected for components + * which don't support multiple selections. Concrete implementations should + * override this with a more appopriate and efficient implementation, which + * properly takes into account the ability of the component to support multiple + * selections. + */ + public void selectAllAccessibleSelection() + { + /* Simply call addAccessibleSelection() on all accessible children */ + for (int a = 0; a < getAccessibleChildrenCount(); ++a) + { + addAccessibleSelection(a); + } + } + + /** + * Sets the background color of the component to that specified. + * Unspecified behaviour occurs when null is given as the new + * background color. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the supplied + * color and continues to use the default system color. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param color the new color to use for the background. + * @see getBackground() + */ + public void setBackground(Color color) + { + /* Ignored */ + } + + /** + * Sets the height and width of the component, and its position + * relative to this component's parent, to the values specified + * by the supplied rectangle. Unspecified behaviour occurs when + * null is given as the new bounds. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the new + * rectangle and continues to return null from <code>getBounds()</code>. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param rectangle a rectangle which specifies the new bounds of + * the component. + * @see #getBounds() + */ + public void setBounds(Rectangle rectangle) + { + /* Ignored */ + } + + /** + * Sets the <code>Cursor</code> used when the pointer is positioned over the + * component. Unspecified behaviour occurs when null is given as the new + * cursor. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the new cursor + * and continues to return the default system cursor. Concrete + * subclasses which handle the drawing of an onscreen menu component + * may override this method and provide the appropriate information. + * + * @param cursor the new cursor to use. + * @see #getCursor() + */ + public void setCursor(Cursor cursor) + { + /* Ignored */ + } + + /** + * Sets the enabled/disabled state of this component. + * <br /> + * <br /> + * As this abstract component has no properties related to + * its enabled or disabled state, the implementation of this + * method is left to subclasses. + * + * @param enabled true if the component should be enabled, + * false otherwise. + * @see #getEnabled() + */ + public void setEnabled(boolean enabled) + { + /* Ignored */ + } + + /** + * Sets the <code>Font</code> used for text created by this component. + * Unspecified behaviour occurs when null is given as the new + * font. + * + * @param font the new font to use for text. + * @see #getFont() + */ + public void setFont(Font font) + { + /* Call the method of the enclosing component */ + MenuComponent.this.setFont(font); + } + + /** + * Sets the foreground color of the component to that specified. + * Unspecified behaviour occurs when null is given as the new + * background color. + * <br /> + * <br /> + * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the supplied + * color and continues to return the default system text color used + * for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param color the new foreground color. + * @see #getForeground() + */ + public void setForeground(Color color) + { + /* Ignored */ + } + + /** + * Sets the location of the component, with co-ordinates + * relative to the parent component and using the co-ordinate + * space of the screen. Thus, the point (0,0) is the upper + * left corner of the parent component. + * <br /> + * <br /> + * Please note that this method depends on a correctly implemented + * version of the <code>getBounds()</code> method. Subclasses + * must provide the bounding rectangle via <code>getBounds()</code> + * in order for this method to work. + * + * @param point the location of the component, relative to its parent. + * @see #getLocation() + */ + public void setLocation(Point point) + { + getBounds().setLocation(point); + } + + /** + * Sets the size of the component. + * <br /> + * <br /> + * Please note that this method depends on a correctly implemented + * version of the <code>getBounds()</code> method. Subclasses + * must provide the bounding rectangle via <code>getBounds()</code> + * in order for this method to work. + * + * @param size the new size of the component. + * @see #getSize() + */ + public void setSize(Dimension size) + { + getBounds().setSize(size); + } + + /** + * Sets the visibility state of the component. A component may + * be visible but not drawn on the screen if one of its parent + * components is not visible. To determine if the component is + * actually drawn on screen, <code>isShowing()</code> should be + * used. + * <br /> + * <br /> + * As this abstract component has no properties relating to its + * visibility, the implementation of this method is left to subclasses. + * + * @param visibility the new visibility of the component -- true if + * the component is visible, false if not. + * @see #isShowing() + * @see #isVisible() + */ + public void setVisible(boolean visibility) + { + /* Ignored */ + } + +} /* class AccessibleAWTMenuComponent */ + + +} // class MenuComponent diff --git a/libjava/classpath/java/awt/MenuContainer.java b/libjava/classpath/java/awt/MenuContainer.java new file mode 100644 index 00000000000..c76ec96c20b --- /dev/null +++ b/libjava/classpath/java/awt/MenuContainer.java @@ -0,0 +1,71 @@ +/* MenuContainer.java -- container for menu items + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface is a container for menu components. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface MenuContainer +{ + /** + * Returns the font in use by this container. + * + * @return the menu font + */ + Font getFont(); + + /** + * Removes the specified menu component from the menu. + * + * @param component the menu component to remove + */ + void remove(MenuComponent component); + + /** + * Posts an event to the listeners. + * + * @param event the event to dispatch + * @deprecated use {@link MenuComponent#dispatchEvent(AWTEvent)} instead + */ + boolean postEvent(Event event); +} // interface MenuContainer diff --git a/libjava/classpath/java/awt/MenuItem.java b/libjava/classpath/java/awt/MenuItem.java new file mode 100644 index 00000000000..66798c8800c --- /dev/null +++ b/libjava/classpath/java/awt/MenuItem.java @@ -0,0 +1,603 @@ +/* MenuItem.java -- An item in a menu + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.MenuItemPeer; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleValue; + +/** + * This class represents an item in a menu. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MenuItem extends MenuComponent + implements Serializable, Accessible +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = -21757335363267194L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The name of the action command generated by this item. + * This is package-private to avoid an accessor method. + */ +String actionCommand; + +/** + * @serial Indicates whether or not this menu item is enabled. + * This is package-private to avoid an accessor method. + */ +boolean enabled = true; + +/** + * @serial The mask of events that are enabled for this menu item. + */ +long eventMask; + +/** + * @serial This menu item's label + * This is package-private to avoid an accessor method. + */ +String label = ""; + +/** + * @serial The shortcut for this menu item, if any + */ +private MenuShortcut shortcut; + +// The list of action listeners for this menu item. +private transient ActionListener action_listeners; + + protected class AccessibleAWTMenuItem + extends MenuComponent.AccessibleAWTMenuComponent + implements AccessibleAction, AccessibleValue + { + /** Constructor */ + public AccessibleAWTMenuItem() + { + super(); + } + + + + public String getAccessibleName() + { + return label; + } + + public AccessibleAction getAccessibleAction() + { + return this; + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_ITEM; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + return 1; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + if (i == 0) + return label; + else + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i != 0) + return false; + processActionEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand)); + return true; + } + + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + return (enabled) ? new Integer(1) : new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + if (number.intValue() == 0) + { + setEnabled(false); + return false; + } + + setEnabled(true); + return true; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(0); + } + + } + + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>MenuItem</code> with no label + * and no shortcut. + */ +public +MenuItem() +{ +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>MenuItem</code> with the specified + * label and no shortcut. + * + * @param label The label for this menu item. + */ +public +MenuItem(String label) +{ + this.label = label; +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>MenuItem</code> with the specified + * label and shortcut. + * + * @param label The label for this menu item. + * @param shortcut The shortcut for this menu item. + */ +public +MenuItem(String label, MenuShortcut shortcut) +{ + this.label = label; + this.shortcut = shortcut; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the label for this menu item, which may be <code>null</code>. + * + * @return The label for this menu item. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * This method sets the label for this menu to the specified value. + * + * @param label The new label for this menu item. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + if (peer != null) + { + MenuItemPeer mp = (MenuItemPeer) peer; + mp.setLabel (label); + } +} + +/*************************************************************************/ + +/** + * Tests whether or not this menu item is enabled. + * + * @return <code>true</code> if this menu item is enabled, <code>false</code> + * otherwise. + */ +public boolean +isEnabled() +{ + return(enabled); +} + +/*************************************************************************/ + +/** + * Sets the enabled status of this menu item. + * + * @param enabled <code>true</code> to enable this menu item, + * <code>false</code> otherwise. + */ +public synchronized void +setEnabled(boolean enabled) +{ + enable (enabled); +} + +/*************************************************************************/ + +/** + * Sets the enabled status of this menu item. + * + * @param enabled <code>true</code> to enable this menu item, + * <code>false</code> otherwise. + * + * @deprecated This method is deprecated in favor of <code>setEnabled()</code>. + */ +public void +enable(boolean enabled) +{ + if (enabled) + enable (); + else + disable (); +} + +/*************************************************************************/ + +/** + * Enables this menu item. + * + * @deprecated This method is deprecated in favor of <code>setEnabled()</code>. + */ +public void +enable() +{ + if (enabled) + return; + + this.enabled = true; + if (peer != null) + ((MenuItemPeer) peer).setEnabled (true); +} + +/*************************************************************************/ + +/** + * Disables this menu item. + * + * @deprecated This method is deprecated in favor of <code>setEnabled()</code>. + */ +public void +disable() +{ + if (!enabled) + return; + + this.enabled = false; + if (peer != null) + ((MenuItemPeer) peer).setEnabled (false); +} + +/*************************************************************************/ + +/** + * Returns the shortcut for this menu item, which may be <code>null</code>. + * + * @return The shortcut for this menu item. + */ +public MenuShortcut +getShortcut() +{ + return(shortcut); +} + +/*************************************************************************/ + +/** + * Sets the shortcut for this menu item to the specified value. This + * must be done before the native peer is created. + * + * @param shortcut The new shortcut for this menu item. + */ +public void +setShortcut(MenuShortcut shortcut) +{ + this.shortcut = shortcut; +} + +/*************************************************************************/ + +/** + * Deletes the shortcut for this menu item if one exists. This must be + * done before the native peer is created. + */ +public void +deleteShortcut() +{ + shortcut = null; +} + +/*************************************************************************/ + +/** + * Returns the name of the action command in the action events + * generated by this menu item. + * + * @return The action command name + */ +public String +getActionCommand() +{ + if (actionCommand == null) + return label; + else + return actionCommand; +} + +/*************************************************************************/ + +/** + * Sets the name of the action command in the action events generated by + * this menu item. + * + * @param actionCommand The new action command name. + */ +public void +setActionCommand(String actionCommand) +{ + this.actionCommand = actionCommand; +} + +/*************************************************************************/ + +/** + * Enables the specified events. This is done automatically when a + * listener is added and does not normally need to be done by + * application code. + * + * @param events The events to enable, which should be the bit masks + * from <code>AWTEvent</code>. + */ +protected final void +enableEvents(long events) +{ + eventMask |= events; + // TODO: see comment in Component.enableEvents(). +} + +/*************************************************************************/ + +/** + * Disables the specified events. + * + * @param events The events to enable, which should be the bit masks + * from <code>AWTEvent</code>. + */ +protected final void +disableEvents(long events) +{ + eventMask &= ~events; +} + +/*************************************************************************/ + +/** + * Creates the native peer for this object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createMenuItem (this); +} + +/*************************************************************************/ + +/** + * Adds the specified listener to the list of registered action listeners + * for this component. + * + * @param listener The listener to add. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); + + enableEvents(AWTEvent.ACTION_EVENT_MASK); +} + +public synchronized void +removeActionListener(ActionListener l) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, l); +} + + public synchronized ActionListener[] getActionListeners() + { + return (ActionListener[]) + AWTEventMulticaster.getListeners(action_listeners, + ActionListener.class); + } + +/** Returns all registered EventListers of the given listenerType. + * listenerType must be a subclass of EventListener, or a + * ClassClassException is thrown. + * @since 1.3 + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == ActionListener.class) + return getActionListeners(); + return (EventListener[]) Array.newInstance(listenerType, 0); + } + +/*************************************************************************/ + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + + // Send the event to the parent menu if it has not yet been + // consumed. + if (!e.isConsumed ()) + ((Menu) getParent ()).processEvent (e); +} + +/** + * Processes the specified event by calling <code>processActionEvent()</code> + * if it is an instance of <code>ActionEvent</code>. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); +} + +/*************************************************************************/ + +/** + * Processes the specified event by dispatching it to any registered listeners. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + { + event.setSource(this); + action_listeners.actionPerformed(event); + } +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +public String +paramString() +{ + return ("label=" + label + ",enabled=" + enabled + + ",actionCommand=" + actionCommand + "," + super.paramString()); +} + +/** + * Gets the AccessibleContext associated with this <code>MenuItem</code>. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenuItem(); + return accessibleContext; +} + +} // class MenuItem diff --git a/libjava/classpath/java/awt/MenuShortcut.java b/libjava/classpath/java/awt/MenuShortcut.java new file mode 100644 index 00000000000..adfd1d3187a --- /dev/null +++ b/libjava/classpath/java/awt/MenuShortcut.java @@ -0,0 +1,207 @@ +/* MenuShortcut.java -- A class for menu accelerators + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class implements a keyboard accelerator for a menu item. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MenuShortcut implements java.io.Serializable +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = 143448358473180225L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The virtual keycode for the shortcut. + */ +private int key; + +/** + * @serial <code>true</code> if the shift key was used with this shortcut, + * or <code>false</code> otherwise. + */ +private boolean usesShift; + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>MenuShortcut</code> with the + * specified virtual key value. + * + * @param key The virtual keycode for the shortcut. + */ +public +MenuShortcut(int key) +{ + this(key, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>MenuShortcut</code> with the + * specified virtual key value and shift setting. + * + * @param key The virtual keycode for the shortcut. + * @param usesShift <code>true</code> if the shift key was pressed, + * <code>false</code> otherwise. + */ +public +MenuShortcut(int key, boolean usesShift) +{ + this.key = key; + this.usesShift = usesShift; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the virtual keycode for this shortcut. + * + * @return The virtual keycode for this shortcut. + */ +public int +getKey() +{ + return(key); +} + +/*************************************************************************/ + +/** + * Returns the shift setting for this shortcut. + * + * @return <code>true</code> if the shift key was pressed, <code>false</code> + * otherwise. + */ +public boolean +usesShiftModifier() +{ + return(usesShift); +} + +/*************************************************************************/ + +/** + * Tests this object for equality against the specified object. The two + * objects will be considered equal if and only if the specified object + * is an instance of <code>MenuShortcut</code> and has the same key value + * and shift setting as this object. + * + * @param obj The object to test for equality against. + * + * @return <code>true</code> if the two objects are equal, <code>false</code> + * otherwise. + */ +public boolean +equals(MenuShortcut obj) +{ + if (obj == null) + return(false); + + if (obj.key != this.key) + return(false); + + if (obj.usesShift != this.usesShift) + return(false); + + return(true); +} + +public boolean +equals(Object obj) +{ + if (obj instanceof MenuShortcut) + { + MenuShortcut ms = (MenuShortcut) obj; + return (ms.key == key && ms.usesShift == usesShift); + } + return false; +} + +/*************************************************************************/ + +/** + * Returns a string representation of this shortcut. + * + * @return A string representation of this shortcut. + */ +public String +toString() +{ + return(getClass().getName() + "[" + paramString () + "]"); +} + +public int +hashCode() +{ + // Arbitrary. + return key + (usesShift ? 23 : 57); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return "key=" + key + ",usesShift=" + usesShift; +} + +} // class MenuShortcut diff --git a/libjava/classpath/java/awt/PageAttributes.java b/libjava/classpath/java/awt/PageAttributes.java new file mode 100644 index 00000000000..38fb696e339 --- /dev/null +++ b/libjava/classpath/java/awt/PageAttributes.java @@ -0,0 +1,482 @@ +/* PageAttributes.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Locale; + +/** + * Missing Documentation + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4, but missing documentation + */ +public final class PageAttributes implements Cloneable +{ + public static final class ColorType extends AttributeValue + { + private static final String[] NAMES = { "color", "monochrome" }; + public static final ColorType COLOR = new ColorType(0); + public static final ColorType MONOCHROME = new ColorType(1); + private ColorType(int value) + { + super(value, NAMES); + } + } // class ColorType + public static final class MediaType extends AttributeValue + { + private static final String[] NAMES + = { "iso-4a0", "iso-2a0", "iso-a0", "iso-a1", "iso-a2", "iso-a3", + "iso-a4", "iso-a5", "iso-a6", "iso-a7", "iso-a8", "iso-a9", + "iso-a10", "iso-b0", "iso-b1", "iso-b2", "iso-b3", "iso-b4", + "iso-b5", "iso-b6", "iso-b7", "iso-b8", "iso-b9", "iso-b10", + "jis-b0", "jis-b1", "jis-b2", "jis-b3", "jis-b4", "jis-b5", + "jis-b6", "jis-b7", "jis-b8", "jis-b9", "jis-b10", "iso-c0", + "iso-c1", "iso-c2", "iso-c3", "iso-c4", "iso-c5", "iso-c6", + "iso-c7", "iso-c8", "iso-c9", "iso-c10", "iso-designated-long", + "executive", "folio", "invoice", "ledger", "na-letter", "na-legal", + "quarto", "a", "b", "c", "d", "e", "na-10x15-envelope", + "na-10x14-envelope", "na-10x13-envelope", "na-9x12-envelope", + "na-9x11-envelope", "na-7x9-envelope", "na-6x9-envelope", + "na-number-9-envelope", "na-number-10-envelope", + "na-number-11-envelope", "na-number-12-envelope", + "na-number-14-envelope", "invite-envelope", "italy-envelope", + "monarch-envelope", "personal-envelope" }; + public static final MediaType ISO_4A0 = new MediaType(0); + public static final MediaType ISO_2A0 = new MediaType(1); + public static final MediaType ISO_A0 = new MediaType(2); + public static final MediaType ISO_A1 = new MediaType(3); + public static final MediaType ISO_A2 = new MediaType(4); + public static final MediaType ISO_A3 = new MediaType(5); + public static final MediaType ISO_A4 = new MediaType(6); + public static final MediaType ISO_A5 = new MediaType(7); + public static final MediaType ISO_A6 = new MediaType(8); + public static final MediaType ISO_A7 = new MediaType(9); + public static final MediaType ISO_A8 = new MediaType(10); + public static final MediaType ISO_A9 = new MediaType(11); + public static final MediaType ISO_A10 = new MediaType(12); + public static final MediaType ISO_B0 = new MediaType(13); + public static final MediaType ISO_B1 = new MediaType(14); + public static final MediaType ISO_B2 = new MediaType(15); + public static final MediaType ISO_B3 = new MediaType(16); + public static final MediaType ISO_B4 = new MediaType(17); + public static final MediaType ISO_B5 = new MediaType(18); + public static final MediaType ISO_B6 = new MediaType(19); + public static final MediaType ISO_B7 = new MediaType(20); + public static final MediaType ISO_B8 = new MediaType(21); + public static final MediaType ISO_B9 = new MediaType(22); + public static final MediaType ISO_B10 = new MediaType(23); + public static final MediaType JIS_B0 = new MediaType(24); + public static final MediaType JIS_B1 = new MediaType(25); + public static final MediaType JIS_B2 = new MediaType(26); + public static final MediaType JIS_B3 = new MediaType(27); + public static final MediaType JIS_B4 = new MediaType(28); + public static final MediaType JIS_B5 = new MediaType(29); + public static final MediaType JIS_B6 = new MediaType(30); + public static final MediaType JIS_B7 = new MediaType(31); + public static final MediaType JIS_B8 = new MediaType(32); + public static final MediaType JIS_B9 = new MediaType(33); + public static final MediaType JIS_B10 = new MediaType(34); + public static final MediaType ISO_C0 = new MediaType(35); + public static final MediaType ISO_C1 = new MediaType(36); + public static final MediaType ISO_C2 = new MediaType(37); + public static final MediaType ISO_C3 = new MediaType(38); + public static final MediaType ISO_C4 = new MediaType(39); + public static final MediaType ISO_C5 = new MediaType(40); + public static final MediaType ISO_C6 = new MediaType(41); + public static final MediaType ISO_C7 = new MediaType(42); + public static final MediaType ISO_C8 = new MediaType(43); + public static final MediaType ISO_C9 = new MediaType(44); + public static final MediaType ISO_C10 = new MediaType(45); + public static final MediaType ISO_DESIGNATED_LONG = new MediaType(46); + public static final MediaType EXECUTIVE = new MediaType(47); + public static final MediaType FOLIO = new MediaType(48); + public static final MediaType INVOICE = new MediaType(49); + public static final MediaType LEDGER = new MediaType(50); + public static final MediaType NA_LETTER = new MediaType(51); + public static final MediaType NA_LEGAL = new MediaType(52); + public static final MediaType QUARTO = new MediaType(53); + public static final MediaType A = new MediaType(54); + public static final MediaType B = new MediaType(55); + public static final MediaType C = new MediaType(56); + public static final MediaType D = new MediaType(57); + public static final MediaType E = new MediaType(58); + public static final MediaType NA_10X15_ENVELOPE = new MediaType(59); + public static final MediaType NA_10X14_ENVELOPE = new MediaType(60); + public static final MediaType NA_10X13_ENVELOPE = new MediaType(61); + public static final MediaType NA_9X12_ENVELOPE = new MediaType(62); + public static final MediaType NA_9X11_ENVELOPE = new MediaType(63); + public static final MediaType NA_7X9_ENVELOPE = new MediaType(64); + public static final MediaType NA_6X9_ENVELOPE = new MediaType(65); + public static final MediaType NA_NUMBER_9_ENVELOPE = new MediaType(66); + public static final MediaType NA_NUMBER_10_ENVELOPE = new MediaType(67); + public static final MediaType NA_NUMBER_11_ENVELOPE = new MediaType(68); + public static final MediaType NA_NUMBER_12_ENVELOPE = new MediaType(69); + public static final MediaType NA_NUMBER_14_ENVELOPE = new MediaType(70); + public static final MediaType INVITE_ENVELOPE = new MediaType(71); + public static final MediaType ITALY_ENVELOPE = new MediaType(72); + public static final MediaType MONARCH_ENVELOPE = new MediaType(73); + public static final MediaType PERSONAL_ENVELOPE = new MediaType(74); + public static final MediaType A0 = ISO_A0; + public static final MediaType A1 = ISO_A1; + public static final MediaType A2 = ISO_A2; + public static final MediaType A3 = ISO_A3; + public static final MediaType A4 = ISO_A4; + public static final MediaType A5 = ISO_A5; + public static final MediaType A6 = ISO_A6; + public static final MediaType A7 = ISO_A7; + public static final MediaType A8 = ISO_A8; + public static final MediaType A9 = ISO_A9; + public static final MediaType A10 = ISO_A10; + public static final MediaType B0 = ISO_B0; + public static final MediaType B1 = ISO_B1; + public static final MediaType B2 = ISO_B2; + public static final MediaType B3 = ISO_B3; + public static final MediaType B4 = ISO_B4; + public static final MediaType ISO_B4_ENVELOPE = ISO_B4; + public static final MediaType B5 = ISO_B5; + public static final MediaType ISO_B5_ENVELOPE = ISO_B4; + public static final MediaType B6 = ISO_B6; + public static final MediaType B7 = ISO_B7; + public static final MediaType B8 = ISO_B8; + public static final MediaType B9 = ISO_B9; + public static final MediaType B10 = ISO_B10; + public static final MediaType C0 = ISO_B0; + public static final MediaType ISO_C0_ENVELOPE = ISO_C0; + public static final MediaType C1 = ISO_C1; + public static final MediaType ISO_C1_ENVELOPE = ISO_C1; + public static final MediaType C2 = ISO_C2; + public static final MediaType ISO_C2_ENVELOPE = ISO_C2; + public static final MediaType C3 = ISO_C3; + public static final MediaType ISO_C3_ENVELOPE = ISO_C3; + public static final MediaType C4 = ISO_C4; + public static final MediaType ISO_C4_ENVELOPE = ISO_C4; + public static final MediaType C5 = ISO_C5; + public static final MediaType ISO_C5_ENVELOPE = ISO_C5; + public static final MediaType C6 = ISO_C6; + public static final MediaType ISO_C6_ENVELOPE = ISO_C6; + public static final MediaType C7 = ISO_C7; + public static final MediaType ISO_C7_ENVELOPE = ISO_C7; + public static final MediaType C8 = ISO_C8; + public static final MediaType ISO_C8_ENVELOPE = ISO_C8; + public static final MediaType C9 = ISO_C9; + public static final MediaType ISO_C9_ENVELOPE = ISO_C9; + public static final MediaType C10 = ISO_C10; + public static final MediaType ISO_C10_ENVELOPE = ISO_C10; + public static final MediaType ISO_DESIGNATED_LONG_ENVELOPE + = ISO_DESIGNATED_LONG; + public static final MediaType STATEMENT = INVOICE; + public static final MediaType TABLOID = LEDGER; + public static final MediaType LETTER = NA_LETTER; + public static final MediaType NOTE = NA_LETTER; + public static final MediaType LEGAL = NA_LEGAL; + public static final MediaType ENV_10X15 = NA_10X15_ENVELOPE; + public static final MediaType ENV_10X14 = NA_10X14_ENVELOPE; + public static final MediaType ENV_10X13 = NA_10X13_ENVELOPE; + public static final MediaType ENV_9X12 = NA_9X12_ENVELOPE; + public static final MediaType ENV_9X11 = NA_9X11_ENVELOPE; + public static final MediaType ENV_7X9 = NA_7X9_ENVELOPE; + public static final MediaType ENV_6X9 = NA_6X9_ENVELOPE; + public static final MediaType ENV_9 = NA_NUMBER_9_ENVELOPE; + public static final MediaType ENV_10 = NA_NUMBER_10_ENVELOPE; + public static final MediaType ENV_11 = NA_NUMBER_11_ENVELOPE; + public static final MediaType ENV_12 = NA_NUMBER_12_ENVELOPE; + public static final MediaType ENV_14 = NA_NUMBER_14_ENVELOPE; + public static final MediaType ENV_INVITE = INVITE_ENVELOPE; + public static final MediaType ENV_ITALY = ITALY_ENVELOPE; + public static final MediaType ENV_MONARCH = MONARCH_ENVELOPE; + public static final MediaType ENV_PERSONAL = PERSONAL_ENVELOPE; + public static final MediaType INVITE = INVITE_ENVELOPE; + public static final MediaType ITALY = ITALY_ENVELOPE; + public static final MediaType MONARCH = MONARCH_ENVELOPE; + public static final MediaType PERSONAL = PERSONAL_ENVELOPE; + private MediaType(int value) + { + super(value, NAMES); + } + } // class MediaType + public static final class OrientationRequestedType extends AttributeValue + { + private static final String[] NAMES = { "portrait", "landscape" }; + public static final OrientationRequestedType PORTRAIT + = new OrientationRequestedType(0); + public static final OrientationRequestedType LANDSCAPE + = new OrientationRequestedType(1); + private OrientationRequestedType(int value) + { + super(value, NAMES); + } + } // class OrientationRequestedType + public static final class OriginType extends AttributeValue + { + private static final String[] NAMES = { "physical", "printable" }; + public static final OriginType PHYSICAL = new OriginType(0); + public static final OriginType PRINTABLE = new OriginType(1); + private OriginType(int value) + { + super(value, NAMES); + } + } // class OriginType + public static final class PrintQualityType extends AttributeValue + { + private static final String[] NAMES = { "high", "normal", "draft" }; + public static final PrintQualityType HIGH = new PrintQualityType(0); + public static final PrintQualityType NORMAL = new PrintQualityType(1); + public static final PrintQualityType DRAFT = new PrintQualityType(2); + private PrintQualityType(int value) + { + super(value, NAMES); + } + } // class PrintQualityType + + + private ColorType color; + private MediaType media; + private OrientationRequestedType orientation; + private OriginType origin; + private PrintQualityType quality; + private int resolutionX; + private int resolutionY; + private int resolutionScale; + public PageAttributes() + { + color = ColorType.MONOCHROME; + setMediaToDefault(); + orientation = OrientationRequestedType.PORTRAIT; + origin = OriginType.PHYSICAL; + quality = PrintQualityType.NORMAL; + setPrinterResolutionToDefault(); + } + + public PageAttributes(PageAttributes attr) + { + set(attr); + } + + public PageAttributes(ColorType color, MediaType media, + OrientationRequestedType orientation, + OriginType origin, PrintQualityType quality, + int[] resolution) + { + if (color == null || media == null || orientation == null + || origin == null || quality == null) + throw new IllegalArgumentException(); + setPrinterResolution(resolution); + this.color = color; + this.media = media; + this.orientation = orientation; + this.origin = origin; + this.quality = quality; + } + + public Object clone() + { + return new PageAttributes(this); + } + + public void set(PageAttributes attr) + { + color = attr.color; + media = attr.media; + orientation = attr.orientation; + origin = attr.origin; + quality = attr.quality; + resolutionX = attr.resolutionX; + resolutionY = attr.resolutionY; + resolutionScale = attr.resolutionScale; + } + + public ColorType getColor() + { + return color; + } + + public void setColor(ColorType color) + { + if (color == null) + throw new IllegalArgumentException(); + this.color = color; + } + + public MediaType getMedia() + { + return media; + } + + public void setMedia(MediaType media) + { + if (media == null) + throw new IllegalArgumentException(); + this.media = media; + } + + public void setMediaToDefault() + { + String country = Locale.getDefault().getCountry(); + media = ("US".equals(country) || "CA".equals(country)) ? MediaType.LETTER + : MediaType.A4; + } + + public OrientationRequestedType getOrientationRequested() + { + return orientation; + } + + public void setOrientationRequested(OrientationRequestedType orientation) + { + if (orientation == null) + throw new IllegalArgumentException(); + this.orientation = orientation; + } + + public void setOrientationRequested(int orientation) + { + if (orientation == 3) + this.orientation = OrientationRequestedType.PORTRAIT; + else if (orientation == 4) + this.orientation = OrientationRequestedType.LANDSCAPE; + else + throw new IllegalArgumentException(); + } + + public void setOrientationRequestedToDefault() + { + orientation = OrientationRequestedType.PORTRAIT; + } + + public OriginType getOrigin() + { + return origin; + } + + public void setOrigin(OriginType origin) + { + if (origin == null) + throw new IllegalArgumentException(); + this.origin = origin; + } + + public PrintQualityType getPrintQuality() + { + return quality; + } + + public void setPrintQuality(PrintQualityType quality) + { + if (quality == null) + throw new IllegalArgumentException(); + this.quality = quality; + } + + public void setPrintQuality(int quality) + { + if (quality == 3) + this.quality = PrintQualityType.DRAFT; + else if (quality == 4) + this.quality = PrintQualityType.NORMAL; + else if (quality == 5) + this.quality = PrintQualityType.HIGH; + else + throw new IllegalArgumentException(); + } + + public void setPrintQualityToDefault() + { + quality = PrintQualityType.NORMAL; + } + + public int[] getPrinterResolution() + { + return new int[] { resolutionX, resolutionY, resolutionScale }; + } + + public void setPrinterResolution(int[] resolution) + { + if (resolution == null || resolution.length != 3 || resolution[0] <= 0 + || resolution[1] <= 0 || resolution[2] < 3 || resolution[2] > 4) + throw new IllegalArgumentException(); + resolutionX = resolution[0]; + resolutionY = resolution[1]; + resolutionScale = resolution[2]; + } + + public void setPrinterResolution(int resolution) + { + if (resolution <= 0) + throw new IllegalArgumentException(); + resolutionX = resolution; + resolutionY = resolution; + resolutionScale = 3; + } + + public void setPrinterResolutionToDefault() + { + resolutionX = 72; + resolutionY = 72; + resolutionScale = 3; + } + + public boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof PageAttributes)) + return false; + PageAttributes pa = (PageAttributes) o; + return color == pa.color && media == pa.media + && orientation == pa.orientation && origin == pa.origin + && quality == pa.quality && resolutionX == pa.resolutionX + && resolutionY == pa.resolutionY + && resolutionScale == pa.resolutionScale; + } + public int hashCode() + { + return (color.value << 31) ^ (media.value << 24) + ^ (orientation.value << 23) ^ (origin.value << 22) + ^ (quality.value << 20) ^ (resolutionScale << 19) + ^ (resolutionY << 10) ^ resolutionX; + } + public String toString() + { + return "color=" + color + ",media=" + media + ",orientation-requested=" + + orientation + ",origin=" + origin + ",print-quality=" + quality + + ",printer-resolution=[" + resolutionX + ',' + resolutionY + ',' + + resolutionScale + ']'; + } +} // class PageAttributes diff --git a/libjava/classpath/java/awt/Paint.java b/libjava/classpath/java/awt/Paint.java new file mode 100644 index 00000000000..0f099cc0b80 --- /dev/null +++ b/libjava/classpath/java/awt/Paint.java @@ -0,0 +1,79 @@ +/* Paint.java -- generate colors for Graphics2D operations + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; + +/** + * Defines how color patterns are generated for Graphics2D operations. This + * is used to perform the <code>draw</code> and <code>fill</code> methods + * of the graphics object. Instances must be immutable, because the graphics + * object does not clone them. + * + * @author Warren Levy (warrenl@cygnus.com) + * @see PaintContext + * @see Color + * @see GradientPaint + * @see TexturePaint + * @see Graphics2D#setPaint(Paint) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Paint extends Transparency +{ + /** + * Create the context necessary for performing the color pattern generation. + * The color model is a hint, and may be null for Classpath implementations; + * however some legacy code may throw a NullPointerException when passed a + * null. Leaving the color model null provides the most efficiency and leeway + * in the generation of the color pattern. + * + * @param cm the color model, used as a hint + * @param deviceBounds the device space bounding box of the painted area + * @param userBounds the user space bounding box of the painted area + * @param xform the transformation from user space to device space + * @param hints any hints for choosing between rendering alternatives + * @return the context for performing the paint + */ + PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform xform, + RenderingHints hints); +} // interface Paint diff --git a/libjava/classpath/java/awt/PaintContext.java b/libjava/classpath/java/awt/PaintContext.java new file mode 100644 index 00000000000..3d5fdcdf0e4 --- /dev/null +++ b/libjava/classpath/java/awt/PaintContext.java @@ -0,0 +1,76 @@ +/* PaintContext.java -- the environment for performing a paint operation + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * @author Warren Levy (warrenl@cygnus.com) + * @see Paint + * @since 1.1 + * @status updated to 1.4 + */ +public interface PaintContext +{ + /** + * Release the resources allocated for the paint. + */ + void dispose(); + + /** + * Return the color model of this context. It may be different from the + * hint specified during createContext, as not all contexts can generate + * color patterns in an arbitrary model. + * + * @return the context color model + */ + ColorModel getColorModel(); + + /** + * Return a raster containing the colors for the graphics operation. + * + * @param x the x-coordinate, in device space + * @param y the y-coordinate, in device space + * @param w the width, in device space + * @param h the height, in device space + * @return a raster for the given area and color + */ + Raster getRaster(int x, int y, int w, int h); +} // interface PaintContext diff --git a/libjava/classpath/java/awt/Panel.java b/libjava/classpath/java/awt/Panel.java new file mode 100644 index 00000000000..cc17eef2285 --- /dev/null +++ b/libjava/classpath/java/awt/Panel.java @@ -0,0 +1,173 @@ +/* Panel.java -- Simple container object + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * A panel is a simple container class. It's default layout is the + * <code>FlowLayout</code> manager. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see FlowLayout + * @since 1.0 + * @status updated to 1.4 + */ +public class Panel extends Container implements Accessible +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2728009084054400034L; + + /** The cached accessible context. */ + private transient AccessibleContext context; + + /** Flag set when the first system-requested paint event is + dispatched. */ + private transient boolean initialSystemUpdateDone; + + /** Flag set when the first application-requested paint event is + consumed. */ + private transient boolean initialUpdateConsumed; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_panel_number; + + /** + * Initializes a new instance of <code>Panel</code> that has a default + * layout manager of <code>FlowLayout</code>. + */ + public Panel() + { + this(new FlowLayout()); + } + + /** + * Initializes a new instance of <code>Panel</code> with the specified + * layout manager. + * + * @param layoutManager the layout manager for this object + * @since 1.1 + */ + public Panel(LayoutManager layoutManager) + { + setLayout(layoutManager); + } + + /** + * Notifies this object to create its native peer. + * + * @see #isDisplayable() + * @see #removeNotify() + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createPanel(this); + super.addNotify(); + } + + /** + * Gets the AccessibleContext associated with this panel, creating one if + * necessary. This always returns an instance of {@link AccessibleAWTPanel}. + * + * @return the accessibility context of this panel + * @since 1.3 + */ + public AccessibleContext getAccessibleContext() + { + if (context == null) + context = new AccessibleAWTPanel(); + return context; + } + + /** + * This class provides accessibility support for Panels, and is the + * runtime type returned by {@link #getAccessibleContext()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ + protected class AccessibleAWTPanel extends AccessibleAWTContainer + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -6409552226660031050L; + + /** + * The default constructor. + */ + protected AccessibleAWTPanel() + { + } + + /** + * Get the role of this accessible object, a panel. + * + * @return the role of the object + * @see AccessibleRole#PANEL + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PANEL; + } + } + + /** + * Generate a unique name for this panel. + * + * @return A unique name for this panel. + */ + String generateName () + { + return "panel" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_panel_number++; + } +} diff --git a/libjava/classpath/java/awt/Point.java b/libjava/classpath/java/awt/Point.java new file mode 100644 index 00000000000..492749b8dc3 --- /dev/null +++ b/libjava/classpath/java/awt/Point.java @@ -0,0 +1,245 @@ +/* Point.java -- represents a point in 2-D space + Copyright (C) 1999, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Point2D; +import java.io.Serializable; + +/** + * This class represents a point on the screen using cartesian coordinates. + * Remember that in screen coordinates, increasing x values go from left to + * right, and increasing y values go from top to bottom. + * + * <p>There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get invalid results. Also, this + * class is not threadsafe. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Point extends Point2D implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5276940640259749850L; + + /** + * The x coordinate. + * + * @see #getLocation() + * @see #move(int, int) + * @serial the X coordinate of the point + */ + public int x; + + /** + * The y coordinate. + * + * @see #getLocation() + * @see #move(int, int) + * @serial The Y coordinate of the point + */ + public int y; + + /** + * Initializes a new instance of <code>Point</code> representing the + * coordiates (0,0). + * + * @since 1.1 + */ + public Point() + { + } + + /** + * Initializes a new instance of <code>Point</code> with coordinates + * identical to the coordinates of the specified points. + * + * @param p the point to copy the coordinates from + * @throws NullPointerException if p is null + */ + public Point(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Initializes a new instance of <code>Point</code> with the specified + * coordinates. + * + * @param x the X coordinate + * @param y the Y coordinate + */ + public Point(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Get the x coordinate. + * + * @return the value of x, as a double + */ + public double getX() + { + return x; + } + + /** + * Get the y coordinate. + * + * @return the value of y, as a double + */ + public double getY() + { + return y; + } + + /** + * Returns the location of this point. A pretty useless method, as this + * is already a point. + * + * @return a copy of this point + * @see #setLocation(Point) + * @since 1.1 + */ + public Point getLocation() + { + return new Point(x, y); + } + + /** + * Sets this object's coordinates to match those of the specified point. + * + * @param p the point to copy the coordinates from + * @throws NullPointerException if p is null + * @since 1.1 + */ + public void setLocation(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Sets this object's coordinates to the specified values. This method + * is identical to the <code>move()</code> method. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void setLocation(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Sets this object's coordinates to the specified values. This method + * performs normal casting from double to int, so you may lose precision. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void setLocation(double x, double y) + { + this.x = (int) x; + this.y = (int) y; + } + + /** + * Sets this object's coordinates to the specified values. This method + * is identical to the <code>setLocation(int, int)</code> method. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void move(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Changes the coordinates of this point such that the specified + * <code>dx</code> parameter is added to the existing X coordinate and + * <code>dy</code> is added to the existing Y coordinate. + * + * @param dx the amount to add to the X coordinate + * @param dy the amount to add to the Y coordinate + */ + public void translate(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * Tests whether or not this object is equal to the specified object. + * This will be true if and only if the specified object is an instance + * of Point2D and has the same X and Y coordinates. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Point2D)) + return false; + Point2D p = (Point2D) obj; + return x == p.getX() && y == p.getY(); + } + + /** + * Returns a string representation of this object. The format is: + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ']'; + } +} // class Point diff --git a/libjava/classpath/java/awt/Polygon.java b/libjava/classpath/java/awt/Polygon.java new file mode 100644 index 00000000000..a72522cb089 --- /dev/null +++ b/libjava/classpath/java/awt/Polygon.java @@ -0,0 +1,613 @@ +/* Polygon.java -- class representing a polygon + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * This class represents a polygon, a closed, two-dimensional region in a + * coordinate space. The region is bounded by an arbitrary number of line + * segments, between (x,y) coordinate vertices. The polygon has even-odd + * winding, meaning that a point is inside the shape if it crosses the + * boundary an odd number of times on the way to infinity. + * + * <p>There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get NullPointerException, + * ArrayIndexOutOfBoundsException, or invalid results. Also, this class is + * not threadsafe. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Polygon implements Shape, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6460061437900069969L; + + /** + * This total number of endpoints. + * + * @serial the number of endpoints, possibly less than the array sizes + */ + public int npoints; + + /** + * The array of X coordinates of endpoints. This should not be null. + * + * @see #addPoint(int, int) + * @serial the x coordinates + */ + public int[] xpoints; + + /** + * The array of Y coordinates of endpoints. This should not be null. + * + * @see #addPoint(int, int) + * @serial the y coordinates + */ + public int[] ypoints; + + /** + * The bounding box of this polygon. This is lazily created and cached, so + * it must be invalidated after changing points. + * + * @see #getBounds() + * @serial the bounding box, or null + */ + protected Rectangle bounds; + + /** A big number, but not so big it can't survive a few float operations */ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + + /** + * Initializes an empty polygon. + */ + public Polygon() + { + // Leave room for growth. + xpoints = new int[4]; + ypoints = new int[4]; + } + + /** + * Create a new polygon with the specified endpoints. The arrays are copied, + * so that future modifications to the parameters do not affect the polygon. + * + * @param xpoints the array of X coordinates for this polygon + * @param ypoints the array of Y coordinates for this polygon + * @param npoints the total number of endpoints in this polygon + * @throws NegativeArraySizeException if npoints is negative + * @throws IndexOutOfBoundsException if npoints exceeds either array + * @throws NullPointerException if xpoints or ypoints is null + */ + public Polygon(int[] xpoints, int[] ypoints, int npoints) + { + this.xpoints = new int[npoints]; + this.ypoints = new int[npoints]; + System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); + System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); + this.npoints = npoints; + } + + /** + * Reset the polygon to be empty. The arrays are left alone, to avoid object + * allocation, but the number of points is set to 0, and all cached data + * is discarded. If you are discarding a huge number of points, it may be + * more efficient to just create a new Polygon. + * + * @see #invalidate() + * @since 1.4 + */ + public void reset() + { + npoints = 0; + invalidate(); + } + + /** + * Invalidate or flush all cached data. After direct manipulation of the + * public member fields, this is necessary to avoid inconsistent results + * in methods like <code>contains</code>. + * + * @see #getBounds() + * @since 1.4 + */ + public void invalidate() + { + bounds = null; + } + + /** + * Translates the polygon by adding the specified values to all X and Y + * coordinates. This updates the bounding box, if it has been calculated. + * + * @param dx the amount to add to all X coordinates + * @param dy the amount to add to all Y coordinates + * @since 1.1 + */ + public void translate(int dx, int dy) + { + int i = npoints; + while (--i >= 0) + { + xpoints[i] += dx; + ypoints[i] += dy; + } + if (bounds != null) + { + bounds.x += dx; + bounds.y += dy; + } + } + + /** + * Adds the specified endpoint to the polygon. This updates the bounding + * box, if it has been created. + * + * @param x the X coordinate of the point to add + * @param y the Y coordiante of the point to add + */ + public void addPoint(int x, int y) + { + if (npoints + 1 > xpoints.length) + { + int[] newx = new int[npoints + 1]; + System.arraycopy(xpoints, 0, newx, 0, npoints); + xpoints = newx; + } + if (npoints + 1 > ypoints.length) + { + int[] newy = new int[npoints + 1]; + System.arraycopy(ypoints, 0, newy, 0, npoints); + ypoints = newy; + } + xpoints[npoints] = x; + ypoints[npoints] = y; + npoints++; + if (bounds != null) + { + if (npoints == 1) + { + bounds.x = x; + bounds.y = y; + } + else + { + if (x < bounds.x) + { + bounds.width += bounds.x - x; + bounds.x = x; + } + else if (x > bounds.x + bounds.width) + bounds.width = x - bounds.x; + if (y < bounds.y) + { + bounds.height += bounds.y - y; + bounds.y = y; + } + else if (y > bounds.y + bounds.height) + bounds.height = y - bounds.y; + } + } + } + + /** + * Returns the bounding box of this polygon. This is the smallest + * rectangle with sides parallel to the X axis that will contain this + * polygon. + * + * @return the bounding box for this polygon + * @see #getBounds2D() + * @since 1.1 + */ + public Rectangle getBounds() + { + return getBoundingBox(); + } + + /** + * Returns the bounding box of this polygon. This is the smallest + * rectangle with sides parallel to the X axis that will contain this + * polygon. + * + * @return the bounding box for this polygon + * @see #getBounds2D() + * @deprecated use {@link #getBounds()} instead + */ + public Rectangle getBoundingBox() + { + if (bounds == null) + { + if (npoints == 0) + return bounds = new Rectangle(); + int i = npoints - 1; + int minx = xpoints[i]; + int maxx = minx; + int miny = ypoints[i]; + int maxy = miny; + while (--i >= 0) + { + int x = xpoints[i]; + int y = ypoints[i]; + if (x < minx) + minx = x; + else if (x > maxx) + maxx = x; + if (y < miny) + miny = y; + else if (y > maxy) + maxy = y; + } + bounds = new Rectangle(minx, miny, maxx - minx, maxy - miny); + } + return bounds; + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param p the point to test + * @return true if the point is inside this polygon + * @throws NullPointerException if p is null + * @see #contains(double, double) + */ + public boolean contains(Point p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @see #contains(double, double) + * @since 1.1 + */ + public boolean contains(int x, int y) + { + return contains((double) x, (double) y); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @see #contains(double, double) + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return contains((double) x, (double) y); + } + + /** + * Returns a high-precision bounding box of this polygon. This is the + * smallest rectangle with sides parallel to the X axis that will contain + * this polygon. + * + * @return the bounding box for this polygon + * @see #getBounds() + * @since 1.2 + */ + public Rectangle2D getBounds2D() + { + // For polygons, the integer version is exact! + return getBounds(); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @since 1.2 + */ + public boolean contains(double x, double y) + { + return ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param p the point to test + * @return true if the point is inside this polygon + * @throws NullPointerException if p is null + * @see #contains(double, double) + * @since 1.2 + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape. This implementation is + * precise. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, treated as point if negative + * @param h the height of the rectangle, treated as point if negative + * @return true if the rectangle intersects this shape + * @since 1.2 + */ + public boolean intersects(double x, double y, double w, double h) + { + /* Does any edge intersect? */ + if (evaluateCrossings(x, y, false, w) != 0 /* top */ + || evaluateCrossings(x, y + h, false, w) != 0 /* bottom */ + || evaluateCrossings(x + w, y, true, h) != 0 /* right */ + || evaluateCrossings(x, y, true, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape. This implementation is + * precise. + * + * @param r the rectangle + * @return true if the rectangle intersects this shape + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + * @since 1.2 + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape. This implementation + * is precise. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, treated as point if negative + * @param h the height of the rectangle, treated as point if negative + * @return true if the rectangle is contained in this shape + * @since 1.2 + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (evaluateCrossings(x, y, false, w) != 0 /* top */ + || evaluateCrossings(x, y + h, false, w) != 0 /* bottom */ + || evaluateCrossings(x + w, y, true, h) != 0 /* right */ + || evaluateCrossings(x, y, true, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape. This implementation + * is precise. + * + * @param r the rectangle + * @return true if the rectangle is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + * @since 1.2 + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This class is not + * threadsafe to begin with, so the path iterator is not either. + * + * @param transform an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(final AffineTransform transform) + { + return new PathIterator() + { + /** The current vertex of iteration. */ + private int vertex; + + public int getWindingRule() + { + return WIND_EVEN_ODD; + } + + public boolean isDone() + { + return vertex > npoints; + } + + public void next() + { + vertex++; + } + + public int currentSegment(float[] coords) + { + if (vertex >= npoints) + return SEG_CLOSE; + coords[0] = xpoints[vertex]; + coords[1] = ypoints[vertex]; + if (transform != null) + transform.transform(coords, 0, coords, 0, 1); + return vertex == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + if (vertex >= npoints) + return SEG_CLOSE; + coords[0] = xpoints[vertex]; + coords[1] = ypoints[vertex]; + if (transform != null) + transform.transform(coords, 0, coords, 0, 1); + return vertex == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; + } + + /** + * Return an iterator along the flattened version of the shape boundary. + * Since polygons are already flat, the flatness parameter is ignored, and + * the resulting iterator only has SEG_MOVETO, SEG_LINETO and SEG_CLOSE + * points. If the optional transform is provided, the iterator is + * transformed accordingly. Each call returns a new object, independent + * from others in use. This class is not threadsafe to begin with, so the + * path iterator is not either. + * + * @param transform an optional transform to apply to the iterator + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform transform, + double flatness) + { + return getPathIterator(transform); + } + + /** + * Helper for contains, intersects, calculates the number of intersections + * between the polygon and a line extending from the point (x, y) along + * the positive X, or Y axis, within a given interval. + * + * @return the winding number. + * @see #condensed + * @see #contains(double, double) + */ + private int evaluateCrossings(double x, double y, boolean useYaxis, + double distance) + { + double x0; + double x1; + double y0; + double y1; + double epsilon = 0.0; + int crossings = 0; + int[] xp; + int[] yp; + + if (useYaxis) + { + xp = ypoints; + yp = xpoints; + double swap; + swap = y; + y = x; + x = swap; + } + else + { + xp = xpoints; + yp = ypoints; + } + + /* Get a value which is small but not insignificant relative the path. */ + epsilon = 1E-7; + + x0 = xp[0] - x; + y0 = yp[0] - y; + for (int i = 1; i < npoints; i++) + { + x1 = xp[i] - x; + y1 = yp[i] - y; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (y0 * y1 < 0) + if (Line2D.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0)) + ++crossings; + + x0 = xp[i] - x; + y0 = yp[i] - y; + } + + // end segment + x1 = xp[0] - x; + y1 = yp[0] - y; + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (y0 * y1 < 0) + if (Line2D.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0)) + ++crossings; + + return crossings; + } +} // class Polygon + diff --git a/libjava/classpath/java/awt/PopupMenu.java b/libjava/classpath/java/awt/PopupMenu.java new file mode 100644 index 00000000000..90d48d903b9 --- /dev/null +++ b/libjava/classpath/java/awt/PopupMenu.java @@ -0,0 +1,169 @@ +/* PopupMenu.java -- An AWT popup menu + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.PopupMenuPeer; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implement an AWT popup menu widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PopupMenu extends Menu +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = -4620452533522760060L; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>PopupMenu</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +PopupMenu() +{ +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>PopupMenu</code> with the specified + * label. + * + * @param label The label for this popup menu. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +PopupMenu(String label) +{ + super(label); + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Creates this object's native peer. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createPopupMenu (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Displays this popup menu at the specified coordinates relative to + * the specified component. + * + * @param component The component to which the display coordinates are relative. + * @param x The X coordinate of the menu. + * @param y The Y coordinate of the menu. + */ +public void +show(Component component, int x, int y) +{ + if (getPeer() == null) + this.addNotify(); + PopupMenuPeer pmp = (PopupMenuPeer)getPeer(); + if (pmp != null) + { + /* XXX + Event e = new Event (component, Event.ACTION_EVENT, component); + e.x = x; + e.y = y;*/ + pmp.show (component, x, y); + } +} + + protected class AccessibleAWTPopupMenu extends AccessibleAWTMenu + { + protected AccessibleAWTPopupMenu() + { + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.POPUP_MENU; + } + + } + + /** + * Gets the AccessibleContext associated with this <code>PopupMenu</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTPopupMenu(); + return accessibleContext; + } + +} // class PopupMenu + diff --git a/libjava/classpath/java/awt/PrintGraphics.java b/libjava/classpath/java/awt/PrintGraphics.java new file mode 100644 index 00000000000..e7f857797d3 --- /dev/null +++ b/libjava/classpath/java/awt/PrintGraphics.java @@ -0,0 +1,57 @@ +/* PrintGraphics.java -- a print graphics context + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface allows the originating print job to be obtained. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface PrintGraphics +{ + /** + * Returns the <code>PrintJob</code> that this object is being + * managed by. + * + * @return the print job for this object + */ + PrintJob getPrintJob(); +} // interface PrintGraphics diff --git a/libjava/classpath/java/awt/PrintJob.java b/libjava/classpath/java/awt/PrintJob.java new file mode 100644 index 00000000000..62aa8b1956e --- /dev/null +++ b/libjava/classpath/java/awt/PrintJob.java @@ -0,0 +1,104 @@ +/* PrintJob.java -- A print job class + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Properties; + +/** + * This abstract class represents a print job. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Toolkit#getPrintJob(Frame, String, Properties) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class PrintJob +{ + /** + * Create a new PrintJob. + */ + public PrintJob() + { + } + + /** + * Returns a graphics context suitable for rendering the next page. The + * return must also implement {@link PrintGraphics}. + * + * @return a graphics context for printing the next page + */ + public abstract Graphics getGraphics(); + + /** + * Returns the dimension of the page in pixels. The resolution will be + * chosen to be similar to the on screen image. + * + * @return the page dimensions + */ + public abstract Dimension getPageDimension(); + + /** + * Returns the resolution of the page in pixels per inch. Note that this is + * not necessarily the printer's resolution. + * + * @return the resolution of the page in pixels per inch + */ + public abstract int getPageResolution(); + + /** + * Tests whether or not the last page will be printed first. + * + * @return true if the last page prints first + */ + public abstract boolean lastPageFirst(); + + /** + * Informs the print job that printing is complete or should be aborted. + */ + public abstract void end(); + + /** + * This method explicitly ends the print job in the event the job + * becomes un-referenced without the application having done so. + */ + public void finalize() + { + end(); + } +} // class PrintJob diff --git a/libjava/classpath/java/awt/Rectangle.java b/libjava/classpath/java/awt/Rectangle.java new file mode 100644 index 00000000000..0f21d495cd8 --- /dev/null +++ b/libjava/classpath/java/awt/Rectangle.java @@ -0,0 +1,749 @@ +/* Rectangle.java -- represents a graphics rectangle + Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * This class represents a rectangle and all the interesting things you + * might want to do with it. Note that the coordinate system uses + * the origin (0,0) as the top left of the screen, with the x and y + * values increasing as they move to the right and down respectively. + * + * <p>It is valid for a rectangle to have negative width or height; but it + * is considered to have no area or internal points. Therefore, the behavior + * in methods like <code>contains</code> or <code>intersects</code> is + * undefined unless the rectangle has positive width and height. + * + * <p>There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get NullPointerException, + * ArrayIndexOutOfBoundsException, or invalid results. Also, this class is + * not threadsafe. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Rectangle extends Rectangle2D implements Shape, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4345857070255674764L; + + /** + * The X coordinate of the top-left corner of the rectangle. + * + * @see #setLocation(int, int) + * @see #getLocation() + * @serial the x coordinate + */ + public int x; + + /** + * The Y coordinate of the top-left corner of the rectangle. + * + * @see #setLocation(int, int) + * @see #getLocation() + * @serial the y coordinate + */ + public int y; + + /** + * The width of the rectangle. + * + * @see #setSize(int, int) + * @see #getSize() + * @serial + */ + public int width; + + /** + * The height of the rectangle. + * + * @see #setSize(int, int) + * @see #getSize() + * @serial + */ + public int height; + + /** + * Initializes a new instance of <code>Rectangle</code> with a top + * left corner at (0,0) and a width and height of 0. + */ + public Rectangle() + { + } + + /** + * Initializes a new instance of <code>Rectangle</code> from the + * coordinates of the specified rectangle. + * + * @param r the rectangle to copy from + * @throws NullPointerException if r is null + * @since 1.1 + */ + public Rectangle(Rectangle r) + { + x = r.x; + y = r.y; + width = r.width; + height = r.height; + } + + /** + * Initializes a new instance of <code>Rectangle</code> from the specified + * inputs. + * + * @param x the X coordinate of the top left corner + * @param y the Y coordinate of the top left corner + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public Rectangle(int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * Initializes a new instance of <code>Rectangle</code> with the specified + * width and height. The upper left corner of the rectangle will be at + * the origin (0,0). + * + * @param width the width of the rectangle + * @param height the height of the rectange + */ + public Rectangle(int width, int height) + { + this.width = width; + this.height = height; + } + + /** + * Initializes a new instance of <code>Rectangle</code> with a top-left + * corner represented by the specified point and the width and height + * represented by the specified dimension. + * + * @param p the upper left corner of the rectangle + * @param d the width and height of the rectangle + * @throws NullPointerException if p or d is null + */ + public Rectangle(Point p, Dimension d) + { + x = p.x; + y = p.y; + width = d.width; + height = d.height; + } + + /** + * Initializes a new instance of <code>Rectangle</code> with a top left + * corner at the specified point and a width and height of zero. + * + * @param p the upper left corner of the rectangle + */ + public Rectangle(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Initializes a new instance of <code>Rectangle</code> with an + * upper left corner at the origin (0,0) and a width and height represented + * by the specified dimension. + * + * @param d the width and height of the rectangle + */ + public Rectangle(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Get the X coordinate of the upper-left corner. + * + * @return the value of x, as a double + */ + public double getX() + { + return x; + } + + /** + * Get the Y coordinate of the upper-left corner. + * + * @return the value of y, as a double + */ + public double getY() + { + return y; + } + + /** + * Get the width of the rectangle. + * + * @return the value of width, as a double + */ + public double getWidth() + { + return width; + } + + /** + * Get the height of the rectangle. + * + * @return the value of height, as a double + */ + public double getHeight() + { + return height; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle; it is included to mimic the + * <code>getBounds</code> method in Component. + * + * @return a copy of this rectangle + * @see #setBounds(Rectangle) + * @since 1.1 + */ + public Rectangle getBounds() + { + return new Rectangle(this); + } + + /** + * Returns the high-precision bounds of this rectangle. A pretty useless + * method, as this is already a rectangle. + * + * @return a copy of this rectangle + * @see #setBounds(Rectangle) + * @since 1.2 + */ + public Rectangle2D getBounds2D() + { + return new Rectangle(x, y, width, height); + } + + /** + * Updates this rectangle to match the dimensions of the specified + * rectangle. + * + * @param r the rectangle to update from + * @throws NullPointerException if r is null + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setBounds(Rectangle r) + { + setBounds (r.x, r.y, r.width, r.height); + } + + /** + * Updates this rectangle to have the specified dimensions. + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @since 1.1 + */ + public void setBounds(int x, int y, int width, int height) + { + reshape (x, y, width, height); + } + + /** + * Updates this rectangle to have the specified dimensions, as rounded to + * integers. + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @since 1.2 + */ + public void setRect(double x, double y, double width, double height) + { + this.x = (int) x; + this.y = (int) y; + this.width = (int) width; + this.height = (int) height; + } + + /** + * Updates this rectangle to have the specified dimensions. + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @deprecated use {@link #setBounds(int, int, int, int)} instead + */ + public void reshape(int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * Returns the location of this rectangle, which is the coordinates of + * its upper left corner. + * + * @return the point where this rectangle is located + * @see #setLocation(Point) + * @since 1.1 + */ + public Point getLocation() + { + return new Point(x,y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified point. + * + * @param p the point to move the rectangle to + * @throws NullPointerException if p is null + * @see #getLocation() + * @since 1.1 + */ + public void setLocation(Point p) + { + setLocation (p.x, p.y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified coordinates. + * + * @param x the new X coordinate for this rectangle + * @param y the new Y coordinate for this rectangle + * @since 1.1 + */ + public void setLocation(int x, int y) + { + move (x, y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified coordinates. + * + * @param x the new X coordinate for this rectangle + * @param y the new Y coordinate for this rectangle + * @deprecated use {@link #setLocation(int, int)} instead + */ + public void move(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Translate the location of this rectangle by the given amounts. + * + * @param dx the x distance to move by + * @param dy the y distance to move by + * @see #setLocation(int, int) + */ + public void translate(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * Returns the size of this rectangle. + * + * @return the size of this rectangle + * @see #setSize(Dimension) + * @since 1.1 + */ + public Dimension getSize() + { + return new Dimension(width, height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param d the new dimensions of the rectangle + * @throws NullPointerException if d is null + * @see #getSize() + * @since 1.1 + */ + public void setSize(Dimension d) + { + setSize (d.width, d.height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param width the new width of the rectangle + * @param height the new height of the rectangle + * @since 1.1 + */ + public void setSize(int width, int height) + { + resize (width, height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param width the new width of the rectangle + * @param height the new height of the rectangle + * @deprecated use {@link #setSize(int, int)} instead + */ + public void resize(int width, int height) + { + this.width = width; + this.height = height; + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * According to the contract of Shape, a point on the border is in only if + * it has an adjacent point inside the rectangle in either the increasing + * x or y direction. + * + * @param p the point to test + * @return true if the point is inside the rectangle + * @throws NullPointerException if p is null + * @see #contains(int, int) + * @since 1.1 + */ + public boolean contains(Point p) + { + return contains (p.x, p.y); + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * According to the contract of Shape, a point on the border is in only if + * it has an adjacent point inside the rectangle in either the increasing + * x or y direction. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside the rectangle + * @since 1.1 + */ + public boolean contains(int x, int y) + { + return inside (x, y); + } + + /** + * Checks whether all points in the given rectangle are contained in this + * rectangle. + * + * @param r the rectangle to check + * @return true if r is contained in this rectangle + * @throws NullPointerException if r is null + * @see #contains(int, int, int, int) + * @since 1.1 + */ + public boolean contains(Rectangle r) + { + return contains (r.x, r.y, r.width, r.height); + } + + /** + * Checks whether all points in the given rectangle are contained in this + * rectangle. + * + * @param x the x coordinate of the rectangle to check + * @param y the y coordinate of the rectangle to check + * @param w the width of the rectangle to check + * @param h the height of the rectangle to check + * @return true if the parameters are contained in this rectangle + * @since 1.1 + */ + public boolean contains(int x, int y, int w, int h) + { + return width > 0 && height > 0 && w > 0 && h > 0 + && x >= this.x && x + w <= this.x + this.width + && y >= this.y && y + h <= this.y + this.height; + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside the rectangle + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return width > 0 && height > 0 + && x >= this.x && x < this.x + width + && y >= this.y && y < this.y + height; + } + + /** + * Tests whether or not the specified rectangle intersects this rectangle. + * This means the two rectangles share at least one internal point. + * + * @param r the rectangle to test against + * @return true if the specified rectangle intersects this one + * @throws NullPointerException if r is null + * @since 1.2 + */ + public boolean intersects(Rectangle r) + { + return r.width > 0 && r.height > 0 && width > 0 && height > 0 + && r.x < x + width && r.x + r.width > x + && r.y < y + height && r.y + r.height > y; + } + + /** + * Determines the rectangle which is formed by the intersection of this + * rectangle with the specified rectangle. If the two do not intersect, + * an empty rectangle will be returned (meaning the width and/or height + * will be non-positive). + * + * @param r the rectange to calculate the intersection with + * @return a new rectangle bounding the intersection + * @throws NullPointerException if r is null + */ + public Rectangle intersection(Rectangle r) + { + Rectangle res = new Rectangle(); + intersect(this, r, res); + return res; + } + + /** + * Returns the smallest rectangle that contains both this rectangle + * and the specified rectangle. + * + * @param r the rectangle to compute the union with + * @return the smallest rectangle containing both rectangles + * @throws NullPointerException if r is null + */ + public Rectangle union(Rectangle r) + { + Rectangle res = new Rectangle(); + union(this, r, res); + return res; + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param x the X coordinate of the point to add to this rectangle + * @param y the Y coordinate of the point to add to this rectangle + */ + public void add(int x, int y) + { + add((double) x, (double) y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param p the point to add to this rectangle + * @throws NullPointerException if p is null + */ + public void add(Point p) + { + add((double) p.x, (double) p.y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified rectangle. + * + * @param r the rectangle to add to this rectangle + * @throws NullPointerException if r is null + * @see #union(Rectangle) + */ + public void add(Rectangle r) + { + union(this, r, this); + } + + /** + * Expands the rectangle by the specified amount. The horizontal + * and vertical expansion values are applied both to the X,Y coordinate + * of this rectangle, and its width and height. Thus the width and + * height will increase by 2h and 2v accordingly. + * + * @param h the horizontal expansion value + * @param v the vertical expansion value + */ + public void grow(int h, int v) + { + x -= h; + y -= v; + width += h + h; + height += v + v; + } + + /** + * Tests whether or not this rectangle is empty. An empty rectangle + * has a non-positive width or height. + * + * @return true if the rectangle is empty + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Determines the rectangle which is formed by the intersection of this + * rectangle with the specified rectangle. If the two do not intersect, + * an empty rectangle will be returned (meaning the width and/or height + * will be non-positive). + * + * @param r the rectange to calculate the intersection with + * @return a new rectangle bounding the intersection + * @throws NullPointerException if r is null + * @since 1.2 + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + // Favor runtime type of other rectangle. + Rectangle2D res = r.getBounds2D(); + intersect(this, r, res); + return res; + } + + /** + * Returns the smallest rectangle that contains both this rectangle + * and the specified rectangle. + * + * @param r the rectangle to compute the union with + * @return the smallest rectangle containing both rectangles + * @throws NullPointerException if r is null + * @since 1.2 + */ + public Rectangle2D createUnion(Rectangle2D r) + { + // Favor runtime type of other rectangle. + Rectangle2D res = r.getBounds2D(); + union(this, r, res); + return res; + } + + /** + * Tests this rectangle for equality against the specified object. This + * will be true if an only if the specified object is an instance of + * Rectangle2D with the same coordinates and dimensions. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Rectangle2D)) + return false; + Rectangle2D r = (Rectangle2D) obj; + return r.getX() == x && r.getY() == y + && r.getWidth() == width && r.getHeight() == height; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + * + ",height=" + height + ']'</code>. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + + ",height=" + height + ']'; + } +} // class Rectangle diff --git a/libjava/classpath/java/awt/RenderingHints.java b/libjava/classpath/java/awt/RenderingHints.java new file mode 100644 index 00000000000..0e1db72b75f --- /dev/null +++ b/libjava/classpath/java/awt/RenderingHints.java @@ -0,0 +1,803 @@ +/* RenderingHints.java -- + Copyright (C) 2000, 2001, 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A collection of (key, value) items that provide 'hints' for the + * {@link java.awt.Graphics2D} rendering pipeline. Because these + * items are hints only, they may be ignored by a particular + * {@link java.awt.Graphics2D} implementation. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Eric Blake (ebb9@email.byu.edu) + */ +public class RenderingHints implements Map, Cloneable +{ + /** + * The base class used to represent keys. + */ + public abstract static class Key + { + private final int key; + + /** + * Creates a new key. + * + * @param privateKey the private key. + */ + protected Key(int privateKey) + { + key = privateKey; + } + + /** + * Returns <code>true</code> if the specified value is compatible with + * this key, and <code>false</code> otherwise. + * + * @param value the value (<code>null</code> permitted). + * + * @return A boolean. + */ + public abstract boolean isCompatibleValue(Object value); + + /** + * Returns the private key for this instance. + * + * @return The private key. + */ + protected final int intKey() + { + return key; + } + + /** + * Returns a hash code for the key. + * + * @return A hash code. + */ + public final int hashCode() + { + return System.identityHashCode(this); + } + + /** + * Checks this key for equality with an arbitrary object. + * + * @param other the object (<code>null</code> permitted) + * + * @return A boolean. + */ + public final boolean equals(Object other) + { + return this == other; + } + } // class Key + + private static final class KeyImpl extends Key + { + final String description; + final Object v1; + final Object v2; + final Object v3; + + KeyImpl(int privateKey, String description, + Object v1, Object v2, Object v3) + { + super(privateKey); + this.description = description; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + } + + /** + * Returns <code>true</code> if the specified value is compatible with + * this key, and <code>false</code> otherwise. + * + * @param value the value (<code>null</code> permitted). + * + * @return A boolean. + */ + public boolean isCompatibleValue(Object value) + { + return value == v1 || value == v2 || value == v3; + } + + /** + * Returns a string representation of the key. + * + * @return A string. + */ + public String toString() + { + return description; + } + } // class KeyImpl + + private HashMap hintMap = new HashMap(); + + /** + * A key for the 'antialiasing' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_ANTIALIAS_OFF}</td> + * <td>Render without antialiasing (better speed).</td> + * </tr> + * <tr> + * <td>{@link #VALUE_ANTIALIAS_ON}</td> + * <td>Render with antialiasing (better quality).</td> + * </tr> + * <tr> + * <td>{@link #VALUE_ANTIALIAS_DEFAULT}</td> + * <td>Use the default value for antialiasing.</td> + * </tr> + * </table> + */ + public static final Key KEY_ANTIALIASING; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_ON + = "Antialiased rendering mode"; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_OFF + = "Nonantialiased rendering mode"; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_DEFAULT + = "Default antialiasing rendering mode"; + + /** + * A key for the 'rendering' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_RENDER_SPEED}</td> + * <td>Prefer speed over quality when rendering.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_RENDER_QUALITY}</td> + * <td>Prefer quality over speed when rendering.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_RENDER_DEFAULT}</td> + * <td>Use the default value for quality vs. speed when rendering.</td> + * </tr> + * </table> + */ + public static final Key KEY_RENDERING; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_SPEED + = "Fastest rendering methods"; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_QUALITY + = "Highest quality rendering methods"; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_DEFAULT + = "Default rendering methods"; + + /** + * A key for the 'dithering' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_DITHER_DISABLE}</td> + * <td>Disable dithering.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_DITHER_ENABLE}</td> + * <td>Enable dithering.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_DITHER_DEFAULT}</td> + * <td>Use the default value for dithering.</td> + * </tr> + * </table> + */ + public static final Key KEY_DITHERING; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_DISABLE + = "Nondithered rendering mode"; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_ENABLE + = "Dithered rendering mode"; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_DEFAULT + = "Default dithering mode"; + + /** + * A key for the 'text antialiasing' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_TEXT_ANTIALIAS_ON}</td> + * <td>Render text with antialiasing (better quality usually).</td> + * </tr> + * <tr> + * <td>{@link #VALUE_TEXT_ANTIALIAS_OFF}</td> + * <td>Render test without antialiasing (better speed).</td> + * </tr> + * <tr> + * <td>{@link #VALUE_TEXT_ANTIALIAS_DEFAULT}</td> + * <td>Use the default value for text antialiasing.</td> + * </tr> + * </table> + */ + public static final Key KEY_TEXT_ANTIALIASING; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_ON + = "Antialiased text mode"; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_OFF + = "Nonantialiased text mode"; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_DEFAULT + = "Default antialiasing text mode"; + + /** + * A key for the 'fractional metrics' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_FRACTIONALMETRICS_OFF}</td> + * <td>Render text with fractional metrics off.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_FRACTIONALMETRICS_ON}</td> + * <td>Render text with fractional metrics on.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_FRACTIONALMETRICS_DEFAULT}</td> + * <td>Use the default value for fractional metrics.</td> + * </tr> + * </table> + */ + public static final Key KEY_FRACTIONALMETRICS; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_OFF + = "Integer text metrics mode"; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_ON + = "Fractional text metrics mode"; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_DEFAULT + = "Default fractional text metrics mode"; + + /** + * A key for the 'interpolation' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR}</td> + * <td>Use nearest neighbour interpolation.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_INTERPOLATION_BILINEAR}</td> + * <td>Use bilinear interpolation.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_INTERPOLATION_BICUBIC}</td> + * <td>Use bicubic interpolation.</td> + * </tr> + * </table> + */ + public static final Key KEY_INTERPOLATION; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_NEAREST_NEIGHBOR + = "Nearest Neighbor image interpolation mode"; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_BILINEAR + = "Bilinear image interpolation mode"; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_BICUBIC + = "Bicubic image interpolation mode"; + + /** + * A key for the 'alpha interpolation' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_ALPHA_INTERPOLATION_SPEED}</td> + * <td>Prefer speed over quality.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_ALPHA_INTERPOLATION_QUALITY}</td> + * <td>Prefer quality over speed.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_ALPHA_INTERPOLATION_DEFAULT}</td> + * <td>Use the default setting.</td> + * </tr> + * </table> + */ + public static final Key KEY_ALPHA_INTERPOLATION; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_SPEED + = "Fastest alpha blending methods"; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_QUALITY + = "Highest quality alpha blending methods"; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_DEFAULT + = "Default alpha blending methods"; + + /** + * A key for the 'color rendering' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_COLOR_RENDER_SPEED}</td> + * <td>Prefer speed over quality.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_COLOR_RENDER_QUALITY}</td> + * <td>Prefer quality over speed.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_COLOR_RENDER_DEFAULT}</td> + * <td>Use the default setting.</td> + * </tr> + * </table> + */ + public static final Key KEY_COLOR_RENDERING; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_SPEED + = "Fastest color rendering mode"; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_QUALITY + = "Highest quality color rendering mode"; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_DEFAULT + = "Default color rendering mode"; + + /** + * A key for the 'stroke control' hint. Permitted values are: + * <p> + * <table> + * <tr> + * <td>{@link #VALUE_STROKE_DEFAULT}</td> + * <td>Use the default setting.</td> + * </tr> + * <tr> + * <td>{@link #VALUE_STROKE_NORMALIZE}</td> + * <td>XXX</td> + * </tr> + * <tr> + * <td>{@link #VALUE_STROKE_PURE}</td> + * <td>XXX</td> + * </tr> + * </table> + */ + public static final Key KEY_STROKE_CONTROL; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_DEFAULT + = "Default stroke normalization"; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_NORMALIZE + = "Normalize strokes for consistent rendering"; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_PURE + = "Pure stroke conversion for accurate paths"; + + static + { + KEY_ANTIALIASING = new KeyImpl(1, "Global antialiasing enable key", + VALUE_ANTIALIAS_ON, + VALUE_ANTIALIAS_OFF, + VALUE_ANTIALIAS_DEFAULT); + KEY_RENDERING = new KeyImpl(2, "Global rendering quality key", + VALUE_RENDER_SPEED, + VALUE_RENDER_QUALITY, + VALUE_RENDER_DEFAULT); + KEY_DITHERING = new KeyImpl(3, "Dithering quality key", + VALUE_DITHER_DISABLE, + VALUE_DITHER_ENABLE, + VALUE_DITHER_DEFAULT); + KEY_TEXT_ANTIALIASING + = new KeyImpl(4, "Text-specific antialiasing enable key", + VALUE_TEXT_ANTIALIAS_ON, + VALUE_TEXT_ANTIALIAS_OFF, + VALUE_TEXT_ANTIALIAS_DEFAULT); + KEY_FRACTIONALMETRICS = new KeyImpl(5, "Fractional metrics enable key", + VALUE_FRACTIONALMETRICS_OFF, + VALUE_FRACTIONALMETRICS_ON, + VALUE_FRACTIONALMETRICS_DEFAULT); + KEY_INTERPOLATION = new KeyImpl(6, "Image interpolation method key", + VALUE_INTERPOLATION_NEAREST_NEIGHBOR, + VALUE_INTERPOLATION_BILINEAR, + VALUE_INTERPOLATION_BICUBIC); + KEY_ALPHA_INTERPOLATION + = new KeyImpl(7, "Alpha blending interpolation method key", + VALUE_ALPHA_INTERPOLATION_SPEED, + VALUE_ALPHA_INTERPOLATION_QUALITY, + VALUE_ALPHA_INTERPOLATION_DEFAULT); + KEY_COLOR_RENDERING = new KeyImpl(8, "Color rendering quality key", + VALUE_COLOR_RENDER_SPEED, + VALUE_COLOR_RENDER_QUALITY, + VALUE_COLOR_RENDER_DEFAULT); + KEY_STROKE_CONTROL = new KeyImpl(9, "Stroke normalization control key", + VALUE_STROKE_DEFAULT, + VALUE_STROKE_NORMALIZE, + VALUE_STROKE_PURE); + } + + /** + * Creates a new collection of hints containing all the (key, value) pairs + * in the specified map. + * + * @param init a map containing a collection of hints (<code>null</code> + * permitted). + */ + public RenderingHints(Map init) + { + if (init != null) + putAll(init); + } + + /** + * Creates a new collection containing a single (key, value) pair. + * + * @param key the key. + * @param value the value. + */ + public RenderingHints(Key key, Object value) + { + put(key, value); + } + + /** + * Returns the number of hints in the collection. + * + * @return The number of hints. + */ + public int size() + { + return hintMap.size(); + } + + /** + * Returns <code>true</code> if there are no hints in the collection, + * and <code>false</code> otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return hintMap.isEmpty(); + } + + /** + * Returns <code>true</code> if the collection of hints contains the + * specified key, and <code>false</code> otherwise. + * + * @param key the key (<code>null</code> not permitted). + * + * @return A boolean. + * + * @throws NullPointerException if <code>key</code> is <code>null</code>. + * @throws ClassCastException if <code>key</code> is not a {@link Key}. + */ + public boolean containsKey(Object key) + { + if (key == null) + throw new NullPointerException(); + // don't remove the cast, it is necessary to throw the required exception + return hintMap.containsKey((Key) key); + } + + /** + * Returns <code>true</code> if the collection of hints contains the + * specified value, and <code>false</code> otherwise. + * + * @param value the value. + * + * @return A boolean. + */ + public boolean containsValue(Object value) + { + return hintMap.containsValue(value); + } + + /** + * Returns the value associated with the specified key, or <code>null</code> + * if there is no value defined for the key. + * + * @param key the key (<code>null</code> permitted). + * + * @return The value (possibly <code>null</code>). + * + * @throws ClassCastException if <code>key</code> is not a {@link Key}. + * + * @see #containsKey(Object) + */ + public Object get(Object key) + { + // don't remove the cast, it is necessary to throw the required exception + return hintMap.get((Key) key); + } + + /** + * Adds a (key, value) pair to the collection of hints (if the + * collection already contains the specified key, then the + * value is updated). + * + * @param key the key. + * @param value the value. + * + * @return the previous value of the key or <code>null</code> if the key + * didn't have a value yet. + */ + public Object put(Object key, Object value) + { + if (key == null || value == null) + throw new NullPointerException(); + if (! ((Key) key).isCompatibleValue(value)) + throw new IllegalArgumentException(); + return hintMap.put(key, value); + } + + /** + * Adds all the hints from a collection to this collection. + * + * @param hints the hint collection. + */ + public void add(RenderingHints hints) + { + hintMap.putAll(hints); + } + + /** + * Clears all the hints from this collection. + */ + public void clear() + { + hintMap.clear(); + } + + /** + * Removes a hint from the collection. + * + * @param key the key. + * + * @return The value that was associated with the key, or <code>null</code> if + * the key was not part of the collection + * + * @throws ClassCastException if the key is not a subclass of + * {@link RenderingHints.Key}. + */ + public Object remove(Object key) + { + // don't remove the (Key) cast, it is necessary to throw the exception + // required by the spec + return hintMap.remove((Key) key); + } + + /** + * Adds a collection of (key, value) pairs to the collection. + * + * @param m a map containing (key, value) items. + * + * @throws ClassCastException if the map contains a key that is not + * a subclass of {@link RenderingHints.Key}. + * @throws IllegalArgumentException if the map contains a value that is + * not compatible with its key. + */ + public void putAll(Map m) + { + // preprocess map to generate appropriate exceptions + Iterator iterator = m.keySet().iterator(); + while (iterator.hasNext()) + { + Key key = (Key) iterator.next(); + if (!key.isCompatibleValue(m.get(key))) + throw new IllegalArgumentException(); + } + // map is OK, update + hintMap.putAll(m); + } + + /** + * Returns a set containing the keys from this collection. + * + * @return A set of keys. + */ + public Set keySet() + { + return hintMap.keySet(); + } + + /** + * Returns a collection of the values from this hint collection. The + * collection is backed by the <code>RenderingHints</code> instance, + * so updates to one will affect the other. + * + * @return A collection of values. + */ + public Collection values() + { + return hintMap.values(); + } + + /** + * Returns a set of entries from the collection. + * + * @return A set of entries. + */ + public Set entrySet() + { + return Collections.unmodifiableSet(hintMap.entrySet()); + } + + /** + * Checks this collection for equality with an arbitrary object. + * + * @param o the object (<code>null</code> permitted) + * + * @return A boolean. + */ + public boolean equals(Object o) + { + return hintMap.equals(o); + } + + /** + * Returns a hash code for the collection of hints. + * + * @return A hash code. + */ + public int hashCode() + { + return hintMap.hashCode(); + } + + /** + * Creates a clone of this instance. + * + * @return A clone. + */ + public Object clone() + { + try + { + RenderingHints copy = (RenderingHints) super.clone(); + copy.hintMap = (HashMap) hintMap.clone(); + return copy; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Returns a string representation of this instance. + * + * @return A string. + */ + public String toString() + { + return hintMap.toString(); + } +} // class RenderingHints diff --git a/libjava/classpath/java/awt/Robot.java b/libjava/classpath/java/awt/Robot.java new file mode 100644 index 00000000000..485a14e1e0f --- /dev/null +++ b/libjava/classpath/java/awt/Robot.java @@ -0,0 +1,423 @@ +/* Robot.java -- a native input event generator + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; + +import java.lang.reflect.InvocationTargetException; +import java.awt.event.InputEvent; +import java.awt.image.BufferedImage; +import java.awt.peer.RobotPeer; + +/** + * The Robot class is used to simulate user interaction with graphical + * programs. It can generate native windowing system input events and + * retrieve image data from the current screen. Robot is used to test + * the AWT and Swing library implementations; it can also be used to + * create self-running demo programs. + * + * Since Robot generates native windowing system events, rather than + * simply inserting {@link AWTEvent}s on the AWT event queue, its use + * is not restricted to Java programs. It can be used to + * programatically drive any graphical application. + * + * This implementation requires an X server that supports the XTest + * extension. + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * + * @since 1.3 + */ +public class Robot +{ + private boolean waitForIdle; + private int autoDelay; + private RobotPeer peer; + + /** + * Construct a Robot object that operates on the default screen. + * + * @exception AWTException if GraphicsEnvironment.isHeadless() + * returns true or if the X server does not support the XTest + * extension + * @exception SecurityException if createRobot permission is not + * granted + */ + public Robot () throws AWTException + { + if (GraphicsEnvironment.isHeadless ()) + throw new AWTException ("Robot: headless graphics environment"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("createRobot")); + + ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit (); + + // createRobot will throw AWTException if XTest is not supported. + peer = tk.createRobot (GraphicsEnvironment.getLocalGraphicsEnvironment () + .getDefaultScreenDevice ()); + } + + /** + * Construct a Robot object that operates on the specified screen. + * + * @exception AWTException if GraphicsEnvironment.isHeadless() + * returns true or if the X server does not support the XTest + * extension + * @exception IllegalArgumentException if screen is not a screen + * GraphicsDevice + * @exception SecurityException if createRobot permission is not + * granted + */ + public Robot (GraphicsDevice screen) throws AWTException + { + if (GraphicsEnvironment.isHeadless ()) + throw new AWTException ("Robot: headless graphics environment"); + + if (screen.getType () != GraphicsDevice.TYPE_RASTER_SCREEN) + throw new IllegalArgumentException ("Robot: graphics" + + " device is not a screen"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("createRobot")); + + ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit (); + + // createRobot will throw AWTException if XTest is not supported. + peer = tk.createRobot (screen); + } + + /** + * Move the mouse pointer to absolute coordinates (x, y). + * + * @param x the destination x coordinate + * @param y the destination y coordinate + */ + public void mouseMove(int x, int y) + { + peer.mouseMove (x, y); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Press one or more mouse buttons. + * + * @param buttons the buttons to press; a bitmask of one or more of + * these {@link InputEvent} fields: + * + * <ul> + * <li>BUTTON1_MASK</li> + * <li>BUTTON2_MASK</li> + * <li>BUTTON3_MASK</li> + * </ul> + * + * @exception IllegalArgumentException if the button mask is invalid + */ + public void mousePress (int buttons) + { + if ((buttons & InputEvent.BUTTON1_MASK) == 0 + && (buttons & InputEvent.BUTTON2_MASK) == 0 + && (buttons & InputEvent.BUTTON3_MASK) == 0) + throw new IllegalArgumentException ("Robot: mousePress:" + + " invalid button mask"); + + peer.mousePress (buttons); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Release one or more mouse buttons. + * + * @param buttons the buttons to release; a bitmask of one or more + * of these {@link InputEvent} fields: + * + * <ul> + * <li>BUTTON1_MASK</li> + * <li>BUTTON2_MASK</li> + * <li>BUTTON3_MASK</li> + * </ul> + * + * @exception IllegalArgumentException if the button mask is invalid + */ + public void mouseRelease(int buttons) + { + if ((buttons & InputEvent.BUTTON1_MASK) == 0 + && (buttons & InputEvent.BUTTON2_MASK) == 0 + && (buttons & InputEvent.BUTTON3_MASK) == 0) + throw new IllegalArgumentException ("Robot: mouseRelease:" + + " invalid button mask"); + + peer.mouseRelease (buttons); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Rotate the mouse scroll wheel. + * + * @param wheelAmt number of steps to rotate mouse wheel. negative + * to rotate wheel up (away from the user), positive to rotate wheel + * down (toward the user). + * + * @since 1.4 + */ + public void mouseWheel (int wheelAmt) + { + peer.mouseWheel (wheelAmt); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Press a key. + * + * @param keycode key to press, a {@link java.awt.event.KeyEvent} VK_ constant + * + * @exception IllegalArgumentException if keycode is not a valid key + */ + public void keyPress (int keycode) + { + peer.keyPress (keycode); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Release a key. + * + * @param keycode key to release, a {@link java.awt.event.KeyEvent} VK_ + * constant + * + * @exception IllegalArgumentException if keycode is not a valid key + */ + public void keyRelease (int keycode) + { + peer.keyRelease (keycode); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Return the color of the pixel at the given screen coordinates. + * + * @param x the x coordinate of the pixel + * @param y the y coordinate of the pixel + * + * @return the Color of the pixel at screen coodinates <code>(x, y)</code> + */ + public Color getPixelColor (int x, int y) + { + return new Color (peer.getRGBPixel (x, y)); + } + + /** + * Create an image containing pixels read from the screen. The + * image does not include the mouse pointer. + * + * @param screenRect the rectangle of pixels to capture, in screen + * coordinates + * + * @return a BufferedImage containing the requested pixels + * + * @exception IllegalArgumentException if requested width and height + * are not both greater than zero + * @exception SecurityException if readDisplayPixels permission is + * not granted + */ + public BufferedImage createScreenCapture (Rectangle screenRect) + { + if (screenRect.width <= 0) + throw new IllegalArgumentException ("Robot: capture width is <= 0"); + + if (screenRect.height <= 0) + throw new IllegalArgumentException ("Robot: capture height is <= 0"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("readDisplayPixels")); + + int[] pixels = peer.getRGBPixels (screenRect); + + BufferedImage bufferedImage = + new BufferedImage (screenRect.width, screenRect.height, + BufferedImage.TYPE_INT_ARGB); + + bufferedImage.setRGB (0, 0, screenRect.width, screenRect.height, + pixels, 0, screenRect.width); + + return bufferedImage; + } + + /** + * Check if this Robot automatically calls {@link #waitForIdle()} after + * generating an event. + * + * @return true if waitForIdle is automatically called + */ + public boolean isAutoWaitForIdle () + { + return waitForIdle; + } + + /** + * Set whether or not this Robot automatically calls {@link + * #waitForIdle()} after generating an event. + * + * @param isOn true if waitForIdle should be called automatically + */ + public void setAutoWaitForIdle (boolean isOn) + { + waitForIdle = isOn; + } + + /** + * Retrieve the length of time this Robot sleeps after generating an + * event. + * + * @return the length of time in milliseconds + */ + public int getAutoDelay () + { + return autoDelay; + } + + /** + * Set the length of time this Robot sleeps after generating an + * event. + * + * @param ms the length of time in milliseconds + * + * @exception IllegalArgumentException if ms is not between 0 and + * 60,000 milliseconds inclusive + */ + public void setAutoDelay (int ms) + { + if (ms <= 0 || ms >= 60000) + throw new IllegalArgumentException ("Robot: delay length out-of-bounds"); + + autoDelay = ms; + } + + /** + * Sleep for a specified length of time. + * + * @param ms the length of time in milliseconds + * + * @exception IllegalArgumentException if ms is not between 0 and + * 60,000 milliseconds inclusive + */ + public void delay (int ms) + { + if (ms < 0 || ms > 60000) + throw new IllegalArgumentException ("Robot: delay length out-of-bounds"); + + try + { + Thread.sleep (ms); + } + catch (InterruptedException e) + { + System.err.println ("Robot: delay interrupted"); + } + } + + /** + * Wait until all events currently on the event queue have been + * dispatched. + */ + public void waitForIdle () + { + if (EventQueue.isDispatchThread ()) + throw new IllegalThreadStateException ("Robot: waitForIdle called from " + + "the event dispatch thread"); + + try + { + EventQueue.invokeAndWait (new Runnable () { public void run () { } }); + } + catch (InterruptedException e) + { + System.err.println ("Robot: waitForIdle interrupted"); + } + catch (InvocationTargetException e) + { + System.err.println ("Robot: waitForIdle cannot invoke target"); + } + } + + /** + * Return a string representation of this Robot. + * + * @return a string representation + */ + public String toString () + { + return getClass ().getName () + + "[ autoDelay = " + autoDelay + ", autoWaitForIdle = " + + waitForIdle + " ]"; + } +} diff --git a/libjava/classpath/java/awt/ScrollPane.java b/libjava/classpath/java/awt/ScrollPane.java new file mode 100644 index 00000000000..b3ecc59fcd5 --- /dev/null +++ b/libjava/classpath/java/awt/ScrollPane.java @@ -0,0 +1,615 @@ +/* ScrollPane.java -- Scrolling window + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ScrollPanePeer; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This widget provides a scrollable region that allows a single + * subcomponent to be viewed through a smaller window. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ScrollPane extends Container implements Accessible +{ + +/* + * Static Variables + */ + +/** + * Constant indicating that scrollbars are created as needed in this + * windows. + */ +public static final int SCROLLBARS_AS_NEEDED = 0; + +/** + * Constant indicating that scrollbars are always displayed in this + * window. + */ +public static final int SCROLLBARS_ALWAYS = 1; + +/** + * Constant indicating that scrollbars are never displayed in this window. + */ +public static final int SCROLLBARS_NEVER = 2; + +// Serialization constant +private static final long serialVersionUID = 7956609840827222915L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The horizontal scrollbar for this window. The methods + * <code>setMinimum()</code>, <code>setMaximum</code>, and + * <code>setVisibleAmount</code> must not be called on this scrollbar. + */ +private ScrollPaneAdjustable hAdjustable; + +/** + * @serial The vertical scrollbar for this window. The methods + * <code>setMinimum()</code>, <code>setMaximum</code>, and + * <code>setVisibleAmount</code> must not be called on this scrollbar. + */ +private ScrollPaneAdjustable vAdjustable; + +/** + * @serial Indicates when scrollbars are displayed in this window, will + * be one of the constants from this class. + */ +private int scrollbarDisplayPolicy; + +// Current scroll position +private Point scrollPosition = new Point(0, 0); + +private boolean wheelScrollingEnabled; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>ScrollPane</code> with a default + * scrollbar policy of <code>SCROLLBARS_AS_NEEDED</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +ScrollPane() +{ + this(SCROLLBARS_AS_NEEDED); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>ScrollPane</code> with the + * specified scrollbar policy. + * + * @param scrollbarDisplayPolicy When to display scrollbars, which must + * be one of the constants defined in this class. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +ScrollPane(int scrollbarDisplayPolicy) +{ + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + this.scrollbarDisplayPolicy = scrollbarDisplayPolicy; + + if (scrollbarDisplayPolicy != SCROLLBARS_ALWAYS + && scrollbarDisplayPolicy != SCROLLBARS_AS_NEEDED + && scrollbarDisplayPolicy != SCROLLBARS_NEVER) + throw new IllegalArgumentException("Bad scrollbarDisplayPolicy: " + + scrollbarDisplayPolicy); + + if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) + { + hAdjustable = new ScrollPaneAdjustable (this, Scrollbar.HORIZONTAL); + vAdjustable = new ScrollPaneAdjustable (this, Scrollbar.VERTICAL); + } + + wheelScrollingEnabled = true; + + // Default size. + setSize(100,100); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the current scrollbar display policy. + * + * @return The current scrollbar display policy. + */ +public int +getScrollbarDisplayPolicy() +{ + return(scrollbarDisplayPolicy); +} + +/*************************************************************************/ + +/** + * Returns the horizontal scrollbar for this object. If the scrollbar + * display policy is set to <code>SCROLLBARS_NEVER</code> then this + * will be <code>null</code>. + * + * @return The horizontal scrollbar for this window. + */ +public Adjustable +getHAdjustable() +{ + return(hAdjustable); +} + +/*************************************************************************/ + +/** + * Returns the vertical scrollbar for this object. If the scrollbar + * display policy is set to <code>SCROLLBARS_NEVER</code> then this + * will be <code>null</code>. + * + * @return The horizontal scrollbar for this window. + */ +public Adjustable +getVAdjustable() +{ + return(vAdjustable); +} + +/*************************************************************************/ + +/** + * Returns the current viewport size. The viewport is the region of + * this object's window where the child is actually displayed. + * + * @return The viewport size. + */ +public Dimension getViewportSize () +{ + Dimension viewsize = getSize (); + Insets insets = getInsets (); + + viewsize.width -= (insets.left + insets.right); + viewsize.height -= (insets.top + insets.bottom); + + Component[] list = getComponents(); + if ((list == null) || (list.length <= 0)) + return viewsize; + + Dimension dim = list[0].getPreferredSize(); + + if (dim.width <= 0 && dim.height <= 0) + return viewsize; + + int vScrollbarWidth = getVScrollbarWidth (); + int hScrollbarHeight = getHScrollbarHeight (); + + if (scrollbarDisplayPolicy == SCROLLBARS_ALWAYS) + { + viewsize.width -= vScrollbarWidth; + viewsize.height -= hScrollbarHeight; + return viewsize; + } + + if (scrollbarDisplayPolicy == SCROLLBARS_NEVER) + return viewsize; + + // The scroll policy is SCROLLBARS_AS_NEEDED, so we need to see if + // either scrollbar is needed. + + // Assume we don't need either scrollbar. + boolean mayNeedVertical = false; + boolean mayNeedHorizontal = false; + + boolean needVertical = false; + boolean needHorizontal = false; + + // Check if we need vertical scrollbars. If we do, then we need to + // subtract the width of the vertical scrollbar from the viewport's + // width. + if (dim.height > viewsize.height) + needVertical = true; + else if (dim.height > (viewsize.height - hScrollbarHeight)) + // This is tricky. In this case the child is tall enough that its + // bottom edge would be covered by a horizontal scrollbar, if one + // were present. This means that if there's a horizontal + // scrollbar then we need a vertical scrollbar. + mayNeedVertical = true; + + if (dim.width > viewsize.width) + needHorizontal = true; + else if (dim.width > (viewsize.width - vScrollbarWidth)) + mayNeedHorizontal = true; + + if (needVertical && mayNeedHorizontal) + needHorizontal = true; + + if (needHorizontal && mayNeedVertical) + needVertical = true; + + if (needHorizontal) + viewsize.height -= hScrollbarHeight; + + if (needVertical) + viewsize.width -= vScrollbarWidth; + + return viewsize; +} + +/*************************************************************************/ + +/** + * Returns the height of a horizontal scrollbar. + * + * @return The height of a horizontal scrollbar. + */ +public int +getHScrollbarHeight() +{ + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + return(spp.getHScrollbarHeight()); + else + return(0); // FIXME: What to do here? +} + +/*************************************************************************/ + +/** + * Returns the width of a vertical scrollbar. + * + * @return The width of a vertical scrollbar. + */ +public int +getVScrollbarWidth() +{ + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + return(spp.getVScrollbarWidth()); + else + return(0); // FIXME: What to do here? +} + +/*************************************************************************/ + +/** + * Returns the current scroll position of the viewport. + * + * @return The current scroll position of the viewport. + */ +public Point +getScrollPosition() +{ + int x = 0; + int y = 0; + + Adjustable v = getVAdjustable(); + Adjustable h = getHAdjustable(); + + if (v != null) + y = v.getValue(); + if (h != null) + x = h.getValue(); + + return(new Point(x, y)); +} + +/*************************************************************************/ + +/** + * Sets the scroll position to the specified value. + * + * @param scrollPosition The new scrollPosition. + * + * @exception IllegalArgumentException If the specified value is outside + * the legal scrolling range. + */ +public void +setScrollPosition(Point scrollPosition) throws IllegalArgumentException +{ + setScrollPosition(scrollPosition.x, scrollPosition.y); +} + +/*************************************************************************/ + +/** + * Sets the scroll position to the specified value. + * + * @param x The new X coordinate of the scroll position. + * @param y The new Y coordinate of the scroll position. + * + * @exception IllegalArgumentException If the specified value is outside + * the legal scrolling range. + */ +public void +setScrollPosition(int x, int y) +{ + Adjustable h = getHAdjustable(); + Adjustable v = getVAdjustable(); + + if (h != null) + h.setValue(x); + if (v != null) + v.setValue(y); + + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + spp.setScrollPosition(x, y); +} + +/*************************************************************************/ + +/** + * Notifies this object that it should create its native peer. + */ +public void +addNotify() +{ + if (!isDisplayable ()) + return; + + setPeer((ComponentPeer)getToolkit().createScrollPane(this)); + super.addNotify(); + + Component[] list = getComponents(); + if (list != null && list.length > 0 && ! (list[0] instanceof Panel)) + { + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + panel.add(list[0], BorderLayout.CENTER); + add(panel); + } +} + +/*************************************************************************/ + +/** + * Notifies this object that it should destroy its native peers. + */ +public void +removeNotify() +{ + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Adds the specified child component to this container. A + * <code>ScrollPane</code> can have at most one child, so if a second + * one is added, then first one is removed. + * + * @param component The component to add to this container. + * @param constraints A list of layout constraints for this object. + * @param index The index at which to add the child, which is ignored + * in this implementation. + */ + protected final void addImpl (Component component, Object constraints, + int index) +{ + Component[] list = getComponents(); + if ((list != null) && (list.length > 0)) + remove(list[0]); + + super.addImpl(component, constraints, -1); + + doLayout(); +} + +/*************************************************************************/ + +/** + * Lays out this component. This consists of resizing the sole child + * component to its perferred size. + */ +public void +doLayout() +{ + layout (); +} + +/*************************************************************************/ + +/** + * Lays out this component. This consists of resizing the sole child + * component to its perferred size. + * + * @deprecated This method is deprecated in favor of + * <code>doLayout()</code>. + */ +public void +layout() +{ + Component[] list = getComponents (); + if ((list != null) && (list.length > 0)) + { + Dimension dim = list[0].getPreferredSize (); + Dimension vp = getViewportSize (); + + if (dim.width < vp.width) + dim.width = vp.width; + + if (dim.height < vp.height) + dim.height = vp.height; + + ScrollPanePeer peer = (ScrollPanePeer) getPeer (); + if (peer != null) + peer.childResized (dim.width, dim.height); + + list[0].setSize (dim); + + Point p = getScrollPosition (); + if (p.x > dim.width) + p.x = dim.width; + if (p.y > dim.height) + p.y = dim.height; + + setScrollPosition (p); + } +} + +/*************************************************************************/ + +/** + * This method overrides its superclass method to ensure no layout + * manager is set for this container. <code>ScrollPane</code>'s do + * not have layout managers. + * + * @param layoutManager Ignored + */ +public final void +setLayout(LayoutManager layoutManager) +{ + return; +} + +/*************************************************************************/ + +/** + * Prints all of the components in this container. + * + * @param graphics The desired graphics context for printing. + */ +public void +printComponents(Graphics graphics) +{ + super.printComponents(graphics); +} + +/*************************************************************************/ + +/** + * Returns a debug string for this object. + * + * @return A debug string for this object. + */ +public String +paramString() +{ + Insets insets = getInsets(); + return getName() + "," + + getX() + "," + + getY() + "," + + getWidth() + "x" + getHeight() + "," + + "ScrollPosition=(" + scrollPosition.getX() + "," + + scrollPosition.getY() + ")," + + "Insets=(" + insets.top + "," + + insets.left + "," + + insets.bottom + "," + + insets.right + ")," + + "ScrollbarDisplayPolicy=" + getScrollbarDisplayPolicy() + "," + + "wheelScrollingEnabled=" + isWheelScrollingEnabled(); +} + + /** + * Tells whether or not an event is enabled. + * + * @since 1.4 + */ + protected boolean eventTypeEnabled (int type) + { + if (type == MouseEvent.MOUSE_WHEEL) + return wheelScrollingEnabled; + + return super.eventTypeEnabled (type); + } + + /** + * Tells whether or not wheel scrolling is enabled. + * + * @since 1.4 + */ + public boolean isWheelScrollingEnabled () + { + return wheelScrollingEnabled; + } + + /** + * Enables/disables wheel scrolling. + * + * @since 1.4 + */ + public void setWheelScrollingEnabled (boolean enable) + { + wheelScrollingEnabled = enable; + } + + protected class AccessibleAWTScrollPane extends AccessibleAWTContainer + { + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.SCROLL_PANE; + } + } + + /** + * Gets the AccessibleContext associated with this <code>ScrollPane</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTScrollPane(); + return accessibleContext; + } +} // class ScrollPane + diff --git a/libjava/classpath/java/awt/ScrollPaneAdjustable.java b/libjava/classpath/java/awt/ScrollPaneAdjustable.java new file mode 100644 index 00000000000..cfca19b4423 --- /dev/null +++ b/libjava/classpath/java/awt/ScrollPaneAdjustable.java @@ -0,0 +1,200 @@ +/* ScrollPaneAdjustable.java -- Scrollbars for a ScrollPane + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentListener; +import java.io.Serializable; + +/** + * Need this class since the serialization spec for ScrollPane + * uses it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.4 + */ +public class ScrollPaneAdjustable + implements Adjustable, Serializable +{ + private static final long serialVersionUID = -3359745691033257079L; + + ScrollPane sp; + int orientation; + int value; + int minimum; + int maximum; + int visibleAmount; + int unitIncrement = 1; + int blockIncrement = 1; + AdjustmentListener adjustmentListener; + + private transient boolean valueIsAdjusting = false; + + ScrollPaneAdjustable (ScrollPane sp, int orientation) + { + this.sp = sp; + this.orientation = orientation; + } + + ScrollPaneAdjustable (ScrollPane sp, int orientation, int value, int minimum, + int maximum, int visibleAmount, int unitIncrement, + int blockIncrement) + { + this.sp = sp; + this.orientation = orientation; + this.value = value; + this.minimum = minimum; + this.maximum = maximum; + this.visibleAmount = visibleAmount; + this.unitIncrement = unitIncrement; + this.blockIncrement = blockIncrement; + } + + public void addAdjustmentListener (AdjustmentListener listener) + { + AWTEventMulticaster.add (adjustmentListener, listener); + } + + public void removeAdjustmentListener (AdjustmentListener listener) + { + AWTEventMulticaster.remove (adjustmentListener, listener); + } + + public AdjustmentListener[] getAdjustmentListeners () + { + return (AdjustmentListener[]) AWTEventMulticaster.getListeners + (adjustmentListener, AdjustmentListener.class); + } + + public int getBlockIncrement () + { + return blockIncrement; + } + + public int getMaximum () + { + return maximum; + } + + public int getMinimum () + { + return minimum; + } + + public int getOrientation () + { + return orientation; + } + + public int getUnitIncrement () + { + return unitIncrement; + } + + public int getValue () + { + return value; + } + + public int getVisibleAmount () + { + return visibleAmount; + } + + public void setBlockIncrement (int blockIncrement) + { + this.blockIncrement = blockIncrement; + } + + public void setMaximum (int maximum) + { + this.maximum = maximum; + } + + public void setMinimum (int minimum) + { + this.minimum = minimum; + } + + public void setUnitIncrement (int unitIncrement) + { + this.unitIncrement = unitIncrement; + } + + public void setValue (int value) + { + this.value = value; + + if (value < minimum) + minimum = value; + + if (value > maximum) + maximum = value; + } + + public void setVisibleAmount (int visibleAmount) + { + this.visibleAmount = visibleAmount; + } + + public String paramString () + { + throw new Error ("not implemented"); + } + + /** + * Returns true if the value is in the process of changing. + * + * @since 1.4 + */ + public boolean getValueIsAdjusting () + { + return valueIsAdjusting; + } + + /** + * Sets the value of valueIsAdjusting. + * + * @since 1.4 + */ + public void setValueIsAdjusting (boolean valueIsAdjusting) + { + this.valueIsAdjusting = valueIsAdjusting; + } +} // class ScrollPaneAdjustable + diff --git a/libjava/classpath/java/awt/Scrollbar.java b/libjava/classpath/java/awt/Scrollbar.java new file mode 100644 index 00000000000..6e506c78584 --- /dev/null +++ b/libjava/classpath/java/awt/Scrollbar.java @@ -0,0 +1,825 @@ +/* Scrollbar.java -- AWT Scrollbar widget + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.peer.ScrollbarPeer; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a scrollbar widget. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Scrollbar extends Component implements Accessible, Adjustable +{ + // FIXME: Serialization readObject/writeObject + + /** + * Constant indicating that a scrollbar is horizontal. + */ + public static final int HORIZONTAL = 0; + + /** + * Constant indicating that a scrollbar is vertical. + */ + public static final int VERTICAL = 1; + + /** + * Serialization Constant. + */ + private static final long serialVersionUID = 8451667562882310543L; + + /** + * @serial The amount by which the value of the scrollbar is changed + * when incrementing in line mode. + */ + private int lineIncrement; + + /** + * @serial The amount by which the value of the scrollbar is changed + * when incrementing in page mode. + */ + private int pageIncrement; + + /** + * @serial The maximum value for this scrollbar + */ + private int maximum; + + /** + * @serial The minimum value for this scrollbar + */ + private int minimum; + + /** + * @serial The orientation of this scrollbar, which will be either + * the <code>HORIZONTAL</code> or <code>VERTICAL</code> constant + * from this class. + */ + private int orientation; + + /** + * @serial The current value of this scrollbar. + */ + private int value; + + /** + * @serial The width of the scrollbar's thumb, which is relative + * to the minimum and maximum value of the scrollbar. + */ + private int visibleAmount; + + /** + * List of AdjustmentListener's. + */ + private AdjustmentListener adjustment_listeners; + + /** + * true if the scrollbar is adjusting, false otherwise. + */ + private transient boolean valueIsAdjusting = false; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_scrollbar_number; + + /** + * Initializes a new instance of <code>Scrollbar</code> with a + * vertical orientation and default values for all other parameters. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public Scrollbar() + { + this(VERTICAL); + } + + /** + * Initializes a new instance of <code>Scrollbar</code> with the + * specified orientation and default values for all other parameters. + * The orientation must be either the constant <code>HORIZONTAL</code> or + * <code>VERTICAL</code> from this class. An incorrect value will throw + * an exception. + * + * @param orientation The orientation of this scrollbar. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public Scrollbar(int orientation) throws IllegalArgumentException + { + this(orientation, 0, 10, 0, 100); + } + + /** + * Initializes a new instance of <code>Scrollbar</code> with the + * specified parameters. The orientation must be either the constant + * <code>HORIZONTAL</code> or <code>VERTICAL</code>. An incorrect value + * will throw an exception. Inconsistent values for other parameters + * are silently corrected to valid values. + * + * @param orientation The orientation of this scrollbar. + * @param value The initial value of the scrollbar. + * @param visibleAmount The width of the scrollbar thumb. + * @param minimum The minimum value of the scrollbar. + * @param maximum The maximum value of the scrollbar. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public Scrollbar(int orientation, int value, int visibleAmount, int minimum, + int maximum) throws IllegalArgumentException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + if ((orientation != HORIZONTAL) && (orientation != VERTICAL)) + throw new IllegalArgumentException("Bad orientation value: " + + orientation); + + this.orientation = orientation; + + setValues(value, visibleAmount, minimum, maximum); + + // Default is 1 according to online docs. + lineIncrement = 1; + + // Default is 10 according to javadocs. + pageIncrement = 10; + } + + /** + * Returns the orientation constant for this object. + * + * @return The orientation constant for this object. + */ + public int getOrientation() + { + return orientation; + } + + /** + * Sets the orientation of this scrollbar to the specified value. This + * value must be either the constant <code>HORIZONTAL</code> or + * <code>VERTICAL</code> from this class or an exception will be thrown. + * + * @param orientation The new orientation value. + * + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public void setOrientation(int orientation) + { + if ((orientation != HORIZONTAL) && (orientation != VERTICAL)) + throw new IllegalArgumentException("Bad orientation value: " + + orientation); + + // FIXME: Communicate to peer? Or must this be called before peer creation? + this.orientation = orientation; + } + + /** + * Returns the current value for this scrollbar. + * + * @return The current value for this scrollbar. + */ + public int getValue() + { + return value; + } + + /** + * Sets the current value for this scrollbar to the specified value. + * If this is inconsistent with the minimum and maximum values for this + * scrollbar, the value is silently adjusted. + * + * @param value The new value for this scrollbar. + */ + public void setValue(int value) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the maximum value for this scrollbar. + * + * @return The maximum value for this scrollbar. + */ + public int getMaximum() + { + return maximum; + } + + /** + * Sets the maximum value for this scrollbar to the specified value. + * If the value is less than the current minimum value, it is silent + * set to equal the minimum value. + * + * @param maximum The new maximum value for this scrollbar. + */ + public void setMaximum(int maximum) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the minimum value for this scrollbar. + * + * @return The minimum value for this scrollbar. + */ + public int getMinimum() + { + return minimum; + } + + /** + * Sets the minimum value for this scrollbar to the specified value. If + * this is not consistent with the current value and maximum, it is + * silently adjusted to be consistent. + * + * @param minimum The new minimum value for this scrollbar. + */ + public void setMinimum(int minimum) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @return The width of the scrollbar's thumb. + */ + public int getVisibleAmount() + { + return getVisible(); + } + + /** + * Returns the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @return The width of the scrollbar's thumb. + * + * @deprecated This method is deprecated in favor of + * <code>getVisibleAmount()</code>. + */ + public int getVisible() + { + return visibleAmount; + } + + /** + * Sets the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @param visibleAmount The new visible amount value of the scrollbar. + */ + public void setVisibleAmount(int visibleAmount) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Sets the current value, visible amount, minimum, and maximum for this + * scrollbar. These values are adjusted to be internally consistent + * if necessary. + * + * @param value The new value for this scrollbar. + * @param visibleAmount The new visible amount for this scrollbar. + * @param minimum The new minimum value for this scrollbar. + * @param maximum The new maximum value for this scrollbar. + */ + public synchronized void setValues(int value, int visibleAmount, + int minimum, int maximum) + { + if (maximum < minimum) + maximum = minimum; + + if (value < minimum) + value = minimum; + + if (value > maximum) + value = maximum; + + if (visibleAmount > maximum - minimum) + visibleAmount = maximum - minimum; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null + && (this.value != value || this.visibleAmount != visibleAmount + || this.minimum != minimum || this.maximum != maximum)) + peer.setValues(value, visibleAmount, minimum, maximum); + + this.value = value; + this.visibleAmount = visibleAmount; + this.minimum = minimum; + this.maximum = maximum; + + int range = maximum - minimum; + if (lineIncrement > range) + { + if (range == 0) + lineIncrement = 1; + else + lineIncrement = range; + + if (peer != null) + peer.setLineIncrement(lineIncrement); + } + + if (pageIncrement > range) + { + if (range == 0) + pageIncrement = 1; + else + pageIncrement = range; + + if (peer != null) + peer.setPageIncrement(pageIncrement); + } + } + + /** + * Returns the value added or subtracted when the user activates the scrollbar + * scroll by a "unit" amount. + * + * @return The unit increment value. + */ + public int getUnitIncrement() + { + return getLineIncrement(); + } + + /** + * Returns the value added or subtracted when the user selects the scrollbar + * scroll by a "unit" amount control. + * + * @return The unit increment value. + * + * @deprecated This method is deprecated in favor of + * <code>getUnitIncrement()</code>. + */ + public int getLineIncrement() + { + return lineIncrement; + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "unit" amount control. + * + * @param unitIncrement The new unit increment amount. + */ + public synchronized void setUnitIncrement(int unitIncrement) + { + setLineIncrement(unitIncrement); + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "unit" amount control. + * + * @param lineIncrement The new unit increment amount. + * + * @deprecated This method is deprecated in favor of + * <code>setUnitIncrement()</code>. + */ + public void setLineIncrement(int lineIncrement) + { + if (lineIncrement < 0) + throw new IllegalArgumentException("Unit increment less than zero."); + + int range = maximum - minimum; + if (lineIncrement > range) + { + if (range == 0) + lineIncrement = 1; + else + lineIncrement = range; + } + + if (lineIncrement == this.lineIncrement) + return; + + this.lineIncrement = lineIncrement; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null) + peer.setLineIncrement(this.lineIncrement); + } + + /** + * Returns the value added or subtracted when the user activates the scrollbar + * scroll by a "block" amount. + * + * @return The block increment value. + */ + public int getBlockIncrement() + { + return getPageIncrement(); + } + + /** + * Returns the value added or subtracted when the user selects the scrollbar + * scroll by a "block" amount control. + * + * @return The block increment value. + * + * @deprecated This method is deprecated in favor of + * <code>getBlockIncrement()</code>. + */ + public int getPageIncrement() + { + return pageIncrement; + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "block" amount control. + * + * @param blockIncrement The new block increment amount. + */ + public synchronized void setBlockIncrement(int blockIncrement) + { + setPageIncrement(blockIncrement); + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "block" amount control. + * + * @param pageIncrement The new block increment amount. + * + * @deprecated This method is deprecated in favor of + * <code>setBlockIncrement()</code>. + */ + public void setPageIncrement(int pageIncrement) + { + if (pageIncrement < 0) + throw new IllegalArgumentException("Block increment less than zero."); + + int range = maximum - minimum; + if (pageIncrement > range) + { + if (range == 0) + pageIncrement = 1; + else + pageIncrement = range; + } + + if (pageIncrement == this.pageIncrement) + return; + + this.pageIncrement = pageIncrement; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null) + peer.setPageIncrement(this.pageIncrement); + } + + /** + * Notifies this object to create its native peer. + */ + public synchronized void addNotify() + { + if (peer == null) + peer = getToolkit().createScrollbar(this); + super.addNotify(); + } + + /** + * Adds a new adjustment listener to the list of registered listeners + * for this object. + * + * @param listener The listener to add. + */ + public synchronized void addAdjustmentListener(AdjustmentListener listener) + { + adjustment_listeners = AWTEventMulticaster.add(adjustment_listeners, + listener); + enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK); + } + + /** + * Removes the specified listener from the list of registered listeners + * for this object. + * + * @param listener The listener to remove. + */ + public synchronized void removeAdjustmentListener(AdjustmentListener listener) + { + adjustment_listeners = AWTEventMulticaster.remove(adjustment_listeners, + listener); + } + + /** + * Processes events for this scrollbar. It does this by calling + * <code>processAdjustmentEvent()</code> if the event is an instance of + * <code>AdjustmentEvent</code>, otherwise it calls the superclass to + * process the event. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof AdjustmentEvent) + processAdjustmentEvent((AdjustmentEvent) event); + else + super.processEvent(event); + } + + /** + * Processes adjustment events for this object by dispatching them to + * any registered listeners. Note that this method will only be called + * if adjustment events are enabled. This will happen automatically if + * any listeners are registered. Otherwise, it can be enabled by a + * call to <code>enableEvents()</code>. + * + * @param event The event to process. + */ + protected void processAdjustmentEvent(AdjustmentEvent event) + { + value = event.getValue(); + if (adjustment_listeners != null) + adjustment_listeners.adjustmentValueChanged(event); + } + + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= AdjustmentEvent.ADJUSTMENT_LAST + && e.id >= AdjustmentEvent.ADJUSTMENT_FIRST + && (adjustment_listeners != null + || (eventMask & AWTEvent.ADJUSTMENT_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + /** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ + protected String paramString() + { + return ("value=" + getValue() + ",visibleAmount=" + getVisibleAmount() + + ",minimum=" + getMinimum() + ",maximum=" + getMaximum() + + ",pageIncrement=" + pageIncrement + ",lineIncrement=" + + lineIncrement + ",orientation=" + + (orientation == HORIZONTAL ? "HORIZONTAL" : "VERTICAL") + + super.paramString()); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this <code>Scrollbar</code>. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == AdjustmentListener.class) + return AWTEventMulticaster.getListeners(adjustment_listeners, + listenerType); + + return super.getListeners(listenerType); + } + + /** + * Returns an array of all registered adjustment listeners. + */ + public AdjustmentListener[] getAdjustmentListeners() + { + return (AdjustmentListener[]) getListeners(AdjustmentListener.class); + } + + /** + * Returns true if the value is in the process of changing. + * + * @since 1.4 + */ + public boolean getValueIsAdjusting() + { + return valueIsAdjusting; + } + + /** + * Sets the value of valueIsAdjusting. + * + * @since 1.4 + */ + public void setValueIsAdjusting(boolean valueIsAdjusting) + { + this.valueIsAdjusting = valueIsAdjusting; + } + + /** + * Generate a unique name for this scroll bar. + * + * @return A unique name for this scroll bar. + */ + String generateName() + { + return "scrollbar" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_scrollbar_number++; + } + + /** + * This class provides accessibility support for the + * scrollbar. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected class AccessibleAWTScrollBar extends AccessibleAWTComponent + implements AccessibleValue + { + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = -344337268523697807L; + + /** + * Returns the role of this accessible object. + * + * @return the instance of <code>AccessibleRole</code>, + * which describes this object. + * + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.SCROLL_BAR; + } + + /** + * Returns the state set of this accessible object. + * + * @return a set of <code>AccessibleState</code>s which + * represent the current state of the accessible object. + * + * @see javax.accessibility.AccessibleState + * @see javax.accessibility.AccessibleStateSet + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (getOrientation() == HORIZONTAL) + states.add(AccessibleState.HORIZONTAL); + else + states.add(AccessibleState.VERTICAL); + if (getValueIsAdjusting()) + states.add(AccessibleState.BUSY); + return states; + } + + /** + * Returns an implementation of the <code>AccessibleValue</code> + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible value as well as + * the context. + * + * @return the accessible value associated with this context. + * + * @see javax.accessibility.AccessibleValue + */ + public AccessibleValue getAccessibleValue() + { + return this; + } + + /** + * Returns the current value of this accessible object. + * In this case, this is the same as the value for + * the scrollbar, wrapped in an <code>Integer</code> + * object. + * + * @return the numeric value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + return new Integer(getValue()); + } + + /** + * Sets the current value of this accessible object + * to that supplied. In this case, the value of the + * scrollbar is set, and this method always returns + * true. + * + * @param number the new accessible value. + * + * @return true if the value was set. + * + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + setValue(number.intValue()); + return true; + } + + /** + * Returns the minimum acceptable accessible value used + * by this object. In this case, this is the same as + * the minimum value of the scrollbar, wrapped in an + * object. + * + * @return the minimum value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(getMinimum()); + } + + /** + * Returns the maximum acceptable accessible value used + * by this object. In this case, this is the same as + * the maximum value of the scrollbar, wrapped in an + * object. + * + * @return the maximum value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(getMaximum()); + } + } + + /** + * Gets the AccessibleContext associated with this <code>Scrollbar</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTScrollBar(); + + return accessibleContext; + } +} diff --git a/libjava/classpath/java/awt/Shape.java b/libjava/classpath/java/awt/Shape.java new file mode 100644 index 00000000000..bd8a4343528 --- /dev/null +++ b/libjava/classpath/java/awt/Shape.java @@ -0,0 +1,203 @@ +/* Shape.java -- the classic Object-Oriented shape interface + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * This interface represents an abstract shape. The shape is described by + * a {@link PathIterator}, and has callbacks for determining bounding box, + * where points and rectangles lie in relation to the shape, and tracing + * the trajectory. + * + * <p>A point is inside if it is completely inside, or on the boundary and + * adjacent points in the increasing x or y direction are completely inside. + * Unclosed shapes are considered as implicitly closed when performing + * <code>contains</code> or <code>intersects</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see PathIterator + * @see AffineTransform + * @see java.awt.geom.FlatteningPathIterator + * @see java.awt.geom.GeneralPath + * @since 1.0 + * @status updated to 1.4 + */ +public interface Shape +{ + /** + * Returns a <code>Rectange</code> that bounds the shape. There is no + * guarantee that this is the minimum bounding box, particularly if + * the shape overflows the finite integer range of a bound. Generally, + * <code>getBounds2D</code> returns a tighter bound. + * + * @return the shape's bounding box + * @see #getBounds2D() + */ + Rectangle getBounds(); + + /** + * Returns a high precision bounding box of the shape. There is no guarantee + * that this is the minimum bounding box, but at least it never overflows. + * + * @return the shape's bounding box + * @see #getBounds() + * @since 1.2 + */ + Rectangle2D getBounds2D(); + + /** + * Test if the coordinates lie in the shape. + * + * @param x the x coordinate + * @param y the y coordinate + * @return true if (x,y) lies inside the shape + * @since 1.2 + */ + boolean contains(double x, double y); + + /** + * Test if the point lie in the shape. + * + * @param p the high-precision point + * @return true if p lies inside the shape + * @throws NullPointerException if p is null + * @since 1.2 + */ + boolean contains(Point2D p); + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape, with the caveat that the + * operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, undefined results if negative + * @param h the height of the rectangle, undefined results if negative + * @return true if the rectangle intersects this shape + * @see java.awt.geom.Area + * @since 1.2 + */ + boolean intersects(double x, double y, double w, double h); + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape, with the caveat that the + * operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param r the rectangle + * @return true if the rectangle intersects this shape + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + * @since 1.2 + */ + boolean intersects(Rectangle2D r); + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape, with the caveat + * that the operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, undefined results if negative + * @param h the height of the rectangle, undefined results if negative + * @return true if the rectangle is contained in this shape + * @see java.awt.geom.Area + * @since 1.2 + */ + boolean contains(double x, double y, double w, double h); + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape, with the caveat + * that the operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param r the rectangle + * @return true if the rectangle is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + * @since 1.2 + */ + boolean contains(Rectangle2D r); + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. It is recommended, but + * not required, that the Shape isolate iterations from future changes to + * the boundary, and document this fact. + * + * @param transform an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + PathIterator getPathIterator(AffineTransform transform); + + /** + * Return an iterator along the flattened version of the shape boundary. + * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE points are returned in the + * iterator. The flatness paramter controls how far points are allowed to + * differ from the real curve; although a limit on accuracy may cause this + * parameter to be enlarged if needed. + * + * <p>If the optional transform is provided, the iterator is transformed + * accordingly. Each call returns a new object, independent from others in + * use. It is recommended, but not required, that the Shape isolate + * iterations from future changes to the boundary, and document this fact. + * + * @param transform an optional transform to apply to the iterator + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + PathIterator getPathIterator(AffineTransform transform, double flatness); +} // interface Shape diff --git a/libjava/classpath/java/awt/Stroke.java b/libjava/classpath/java/awt/Stroke.java new file mode 100644 index 00000000000..1a4c61cfb1f --- /dev/null +++ b/libjava/classpath/java/awt/Stroke.java @@ -0,0 +1,65 @@ +/* Stroke.java -- a stroked outline of a shape + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface allows a Graphics2D to grab the outline of a shape, as if + * stroked by a marking pen of appropriate size and shape. The area inked + * by the pen is the area of this stroke. Anything in the graphic which + * traces an outline will use this stroke, such as <code>drawLine</code>. + * Strokes must be immutable, because the graphics object does not clone + * them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see BasicStroke + * @see Graphics2D#setStroke(Stroke) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Stroke +{ + /** + * Returns a shape which outlines the boundary of the given shape, in + * effect converting the infinitely thin line into a new shape. + * + * @param s the shape to stroke + * @return the stroked outline shape + */ + Shape createStrokedShape(Shape s); +} // interface Stroke diff --git a/libjava/classpath/java/awt/SystemColor.java b/libjava/classpath/java/awt/SystemColor.java new file mode 100644 index 00000000000..2ead74a0dbd --- /dev/null +++ b/libjava/classpath/java/awt/SystemColor.java @@ -0,0 +1,462 @@ +/* SystemColor.java -- access dynamic system color values + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.io.Serializable; + +/** + * This class contains the various "system colors" in use by the native + * windowing system. The <code>getRGB()</code> method is dynamic on systems + * which support dynamic system color changes, and most methods in the + * superclass are written to use this dynamic value when reporting colors. + * However, the <code>equals()</code> method is not dynamic, and does not + * track the actual color of instances in this class. This means that equals + * may give surprising results; you are better off relying on getRGB. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public final class SystemColor extends Color implements Serializable +{ + // Implementation note: To be serial compatible with JDK, this class must + // violate the semantic meaning of super.value to be one of the + // NUM_COLORS constants instead of the actual RGB value. Hence there are + // a lot of ugly workarounds in Color and in this class. I would have + // designed it MUCH differently, making a separate id field in this class. + + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4503142729533789064L; + + /** + * Array index of the desktop color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #desktop + */ + public static final int DESKTOP = 0; + + /** + * Array index of the active caption color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaption + */ + public static final int ACTIVE_CAPTION = 1; + + /** + * Array index of the active caption text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaptionText + */ + public static final int ACTIVE_CAPTION_TEXT = 2; + + /** + * Array index of the active caption border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaptionBorder + */ + public static final int ACTIVE_CAPTION_BORDER = 3; + + /** + * Array index of the inactive caption color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaption + */ + public static final int INACTIVE_CAPTION = 4; + + /** + * Array index of the inactive caption text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaptionText + */ + public static final int INACTIVE_CAPTION_TEXT = 5; + + /** + * Array index of the inactive caption border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaptionBorder + */ + public static final int INACTIVE_CAPTION_BORDER = 6; + + /** + * Array index of the window background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #window + */ + public static final int WINDOW = 7; + + /** + * Array index of the window border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #windowBorder + */ + public static final int WINDOW_BORDER = 8; + + /** + * Array index of the window text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #windowText + */ + public static final int WINDOW_TEXT = 9; + + /** + * Array index of the menu background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #menu + */ + public static final int MENU = 10; + + /** + * Array index of the menu text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #menuText + */ + public static final int MENU_TEXT = 11; + + /** + * Array index of the text background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #text + */ + public static final int TEXT = 12; + + /** + * Array index of the text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textText + */ + public static final int TEXT_TEXT = 13; + + /** + * Array index of the highlighted text background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textHighlight + */ + public static final int TEXT_HIGHLIGHT = 14; + + /** + * Array index of the highlighted text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textHighlightText + */ + public static final int TEXT_HIGHLIGHT_TEXT = 15; + + /** + * Array index of the inactive text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textInactiveText + */ + public static final int TEXT_INACTIVE_TEXT = 16; + + /** + * Array index of the control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #control + */ + public static final int CONTROL = 17; + + /** + * Array index of the control text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlText + */ + public static final int CONTROL_TEXT = 18; + + /** + * Array index of the highlighted control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlHighlight + */ + public static final int CONTROL_HIGHLIGHT = 19; + + /** + * Array index of the lightly highlighted control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlLtHighlight + */ + public static final int CONTROL_LT_HIGHLIGHT = 20; + + /** + * Array index of the shadowed control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlShadow + */ + public static final int CONTROL_SHADOW = 21; + + /** + * Array index of the darkly shadowed control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlDkShadow + */ + public static final int CONTROL_DK_SHADOW = 22; + + /** + * Array index of the scrollbar background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #scrollbar + */ + public static final int SCROLLBAR = 23; + + /** + * Array index of the info background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #info + */ + public static final int INFO = 24; + + /** + * Array index of the info text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #infoText + */ + public static final int INFO_TEXT = 25; + + /** + * The number of system colors. Used by + * {@link Toolkit#loadSystemColors(int[])}. + */ + public static final int NUM_COLORS = 26; + + /** + * The internal array used to dynamically update <code>getRGB()</code>. + */ + private static final int[] colors = new int[NUM_COLORS]; + + /** The desktop color. */ + public static final SystemColor desktop + = new SystemColor(DESKTOP); + + /** The active caption background color. */ + public static final SystemColor activeCaption + = new SystemColor(ACTIVE_CAPTION); + + /** The active caption text color. */ + public static final SystemColor activeCaptionText + = new SystemColor(ACTIVE_CAPTION_TEXT); + + /** The active caption border color. */ + public static final SystemColor activeCaptionBorder + = new SystemColor(ACTIVE_CAPTION_BORDER); + + /** The inactive caption background color. */ + public static final SystemColor inactiveCaption + = new SystemColor(INACTIVE_CAPTION); + + /** The inactive caption text color. */ + public static final SystemColor inactiveCaptionText + = new SystemColor(INACTIVE_CAPTION_TEXT); + + /** The inactive caption border color. */ + public static final SystemColor inactiveCaptionBorder + = new SystemColor(INACTIVE_CAPTION_BORDER); + + /** The window background color. */ + public static final SystemColor window + = new SystemColor(WINDOW); + + /** The window border color. */ + public static final SystemColor windowBorder + = new SystemColor(WINDOW_BORDER); + + /** The window text color. */ + public static final SystemColor windowText + = new SystemColor(WINDOW_TEXT); + + /** The menu background color. */ + public static final SystemColor menu + = new SystemColor(MENU); + + /** The menu text color. */ + public static final SystemColor menuText + = new SystemColor(MENU_TEXT); + + /** The text background color. */ + public static final SystemColor text + = new SystemColor(TEXT); + + /** The text foreground color. */ + public static final SystemColor textText + = new SystemColor(TEXT_TEXT); + + /** The highlighted text background color. */ + public static final SystemColor textHighlight + = new SystemColor(TEXT_HIGHLIGHT); + + /** The highlighted text foreground color. */ + public static final SystemColor textHighlightText + = new SystemColor(TEXT_HIGHLIGHT_TEXT); + + /** The inactive text color. */ + public static final SystemColor textInactiveText + = new SystemColor(TEXT_INACTIVE_TEXT); + + /** The control background color. */ + public static final SystemColor control + = new SystemColor(CONTROL); + + /** The control text color. */ + public static final SystemColor controlText + = new SystemColor(CONTROL_TEXT); + + /** The control highlight color. */ + public static final SystemColor controlHighlight + = new SystemColor(CONTROL_HIGHLIGHT); + + /** The control light highlight color. */ + public static final SystemColor controlLtHighlight + = new SystemColor(CONTROL_LT_HIGHLIGHT); + + /** The control shadow color. */ + public static final SystemColor controlShadow + = new SystemColor(CONTROL_SHADOW); + + /** The control dark shadow color. */ + public static final SystemColor controlDkShadow + = new SystemColor(CONTROL_DK_SHADOW); + + /** The scrollbar color. */ + public static final SystemColor scrollbar + = new SystemColor(SCROLLBAR); + + /** The info text background color. */ + public static final SystemColor info + = new SystemColor(INFO); + + /** The info text foreground color. */ + public static final SystemColor infoText + = new SystemColor(INFO_TEXT); + + /** + * Construct a system color which is dynamically updated. + * + * @param id the color id + */ + private SystemColor(int id) + { + // Note: See Color#Color(int, boolean) to explain why we use this + // particular constructor. + super(id, true); + } + + /** + * Returns the RGB value for this color, in the sRGB color space. The blue + * value will be in bits 0-7, green in 8-15, red in 6-23, and the alpha + * value (bits 24-31) is 0xff. This is dynamically updated, so it may not + * match the results of <code>getRed()</code>, <code>getGreen()</code>, or + * <code>getBlue()</code>. + * + * @return the current RGB value + */ + public int getRGB() + { + Toolkit.getDefaultToolkit().loadSystemColors(colors); + return colors[value] | ALPHA_MASK; + } + + /** + * Returns a paint context, used for filling areas of a raster scan with + * the current value of this system color. Since the system colors may be + * dynamically updated, the returned value may not always be the same; but + * as the system color is solid, the context does not need any of the + * passed parameters to do its job. + * + * @param cm the requested color model + * @param deviceBounds the bounding box in device coordinates, ignored + * @param userBounds the bounding box in user coordinates, ignored + * @param xform the bounds transformation, ignored + * @param hints any rendering hints, ignored + * @return a context for painting this solid color + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + Toolkit.getDefaultToolkit().loadSystemColors(colors); + int color = colors[value] | ALPHA_MASK; + if (context == null || color != context.color || !context.getColorModel().equals(cm)) + context = new ColorPaintContext(cm,color); + return context; + } + + /** + * Returns a string describing this color. This is in the format + * "java.awt.SystemColor[i=" + index + ']', where index is one of the + * integer constants of this class. Unfortunately, this description + * does not describe the current value of the color; for that you should + * use <code>new Color(syscolor.getRGB()).toString()</code>. + * + * @return a string describing this color + */ + public String toString() + { + return "java.awt.SystemColor[i=" + value + ']'; + } +} // class SystemColor diff --git a/libjava/classpath/java/awt/TextArea.java b/libjava/classpath/java/awt/TextArea.java new file mode 100644 index 00000000000..d422d3306d2 --- /dev/null +++ b/libjava/classpath/java/awt/TextArea.java @@ -0,0 +1,629 @@ +/* TextArea.java -- A multi-line text entry component + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.TextAreaPeer; +import java.util.HashSet; +import java.util.Set; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleStateSet; + + +/** + * A TextArea is a text component capable of displaying multiple lines + * of user-editable text. A TextArea handles its own scrolling and + * can display vertical and horizontal scrollbars as navigation aids. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextArea extends TextComponent implements java.io.Serializable +{ + /** + * Display both horiztonal and vertical scroll bars. + */ + public static final int SCROLLBARS_BOTH = 0; + + /** + * Display vertical scroll bar only. + */ + public static final int SCROLLBARS_VERTICAL_ONLY = 1; + + /** + * Display horizatonal scroll bar only. + */ + public static final int SCROLLBARS_HORIZONTAL_ONLY = 2; + + /** + * Do not display scrollbars. + */ + public static final int SCROLLBARS_NONE = 3; + + /** + * Serialization constant. + */ + private static final long serialVersionUID = 3692302836626095722L; + + /** + * @serial The number of columns used in this text area's preferred + * and minimum size calculations. + */ + private int columns; + + /** + * @serial The number of rows used in this text area's preferred and + * minimum size calculations. + */ + private int rows; + + /** + * @serial The scrollbar display policy. One of SCROLLBARS_BOTH, + * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY, + * SCROLLBARS_NONE. + */ + private int scrollbarVisibility; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_text_number; + + /** + * Initialize a new instance of <code>TextArea</code> that is empty. + * Conceptually the <code>TextArea</code> has 0 rows and 0 columns + * but its initial bounds are defined by its peer or by the + * container in which it is packed. Both horizontal and vertical + * scrollbars will be displayed. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea () + { + this ("", 0, 0, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of <code>TextArea</code> that contains + * the specified text. Conceptually the <code>TextArea</code> has 0 + * rows and 0 columns but its initial bounds are defined by its peer + * or by the container in which it is packed. Both horizontal and + * veritcal scrollbars will be displayed. + * + * @param text The text to display in this text area. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text) + { + this (text, 0, 0, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of <code>TextArea</code> that is empty + * and can display the specified number of rows and columns of text, + * without the need to scroll. Both horizontal and vertical + * scrollbars will be displayed. + * + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (int rows, int columns) + { + this ("", rows, columns, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of <code>TextArea</code> that can + * display the specified number of rows and columns of text, without + * the need to scroll. The TextArea initially contains the + * specified text. + * + * @param text The text to display in this text area. + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text, int rows, int columns) + { + this (text, rows, columns, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of <code>TextArea</code> that initially + * contains the specified text. The TextArea can display the + * specified number of rows and columns of text, without the need to + * scroll. This constructor allows specification of the scroll bar + * display policy. + * + * @param text The text to display in this text area. + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * @param scrollbarVisibility The scroll bar display policy. One of + * SCROLLBARS_BOTH, SCROLLBARS_VERTICAL_ONLY, + * SCROLLBARS_HORIZONTAL_ONLY, SCROLLBARS_NONE. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text, int rows, int columns, int scrollbarVisibility) + { + super (text); + + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + if (rows < 0 || columns < 0) + throw new IllegalArgumentException ("Bad row or column value"); + + if (scrollbarVisibility != SCROLLBARS_BOTH + && scrollbarVisibility != SCROLLBARS_VERTICAL_ONLY + && scrollbarVisibility != SCROLLBARS_HORIZONTAL_ONLY + && scrollbarVisibility != SCROLLBARS_NONE) + throw new IllegalArgumentException ("Bad scrollbar visibility value"); + + this.rows = rows; + this.columns = columns; + this.scrollbarVisibility = scrollbarVisibility; + + // TextAreas need to receive tab key events so we override the + // default forward and backward traversal key sets. + Set s = new HashSet (); + s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB, + KeyEvent.CTRL_DOWN_MASK)); + setFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, s); + s = new HashSet (); + s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK + | KeyEvent.CTRL_DOWN_MASK)); + setFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, s); + } + + /** + * Retrieve the number of columns that this text area would prefer + * to display. This value may or may not correspond to the number + * of columns that are actually displayed. + * + * @return The preferred number of columns. + */ + public int getColumns () + { + return columns; + } + + /** + * Set the preferred number of columns for this text area. This + * method does not cause the number of columns displayed by the text + * area to be updated, if the text area is currently visible. + * + * @param columns The preferred number of columns. + * + * @exception IllegalArgumentException If columns is less than zero. + */ + public synchronized void setColumns (int columns) + { + if (columns < 0) + throw new IllegalArgumentException ("Value is less than zero: " + + columns); + + this.columns = columns; + } + + /** + * Retrieve the number of rows that this text area would prefer to + * display. This value may or may not correspond to the number of + * rows that are actually displayed. + * + * @return The preferred number of rows. + */ + public int getRows () + { + return rows; + } + + /** + * Set the preferred number of rows for this text area. This method + * does not cause the number of columns displayed by the text area + * to be updated, if the text area is currently visible. + * + * @param rows The preferred number of rows. + * + * @exception IllegalArgumentException If rows is less than zero. + */ + public synchronized void setRows (int rows) + { + if (rows < 1) + throw new IllegalArgumentException ("Value is less than one: " + rows); + + this.rows = rows; + } + + /** + * Retrieve the minimum size for this text area, considering the + * text area's current row and column values. A text area's minimum + * size depends on the number of rows and columns of text it would + * prefer to display, and on the size of the font in which the text + * would be displayed. + * + * @return The minimum size for this text field. + */ + public Dimension getMinimumSize () + { + return getMinimumSize (getRows (), getColumns ()); + } + + /** + * Retrieve the minimum size that this text area would have if its + * row and column values were equal to those specified. A text + * area's minimum size depends on the number of rows and columns of + * text it would prefer to display, and on the size of the font in + * which the text would be displayed. + * + * @param rows The number of rows to use in the minimum size + * calculation. + * @param columns The number of columns to use in the minimum size + * calculation. + * + * @return The minimum size for this text area. + */ + public Dimension getMinimumSize (int rows, int columns) + { + return minimumSize (rows, columns); + } + + /** + * Retrieve the minimum size for this text area, considering the + * text area's current row and column values. A text area's minimum + * size depends on the number of rows and columns of text it would + * prefer to display, and on the size of the font in which the text + * would be displayed. + * + * @return The minimum size for this text area. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize ()</code>. + */ + public Dimension minimumSize () + { + return minimumSize (getRows (), getColumns ()); + } + + /** + * Retrieve the minimum size that this text area would have if its + * row and column values were equal to those specified. A text + * area's minimum size depends on the number of rows and columns of + * text it would prefer to display, and on the size of the font in + * which the text would be displayed. + * + * @param rows The number of rows to use in the minimum size + * calculation. + * @param columns The number of columns to use in the minimum size + * calculation. + * + * @return The minimum size for this text area. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize (int, int)</code>. + */ + public Dimension minimumSize (int rows, int columns) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + // Sun returns Dimension (0,0) in this case. + if (peer == null) + return new Dimension (0, 0); + + return peer.getMinimumSize (rows, columns); + } + + /** + * Retrieve the preferred size for this text area, considering the + * text area's current row and column values. A text area's preferred + * size depends on the number of rows and columns of text it would + * prefer to display, and on the size of the font in which the text + * would be displayed. + * + * @return The preferred size for this text field. + */ + public Dimension getPreferredSize () + { + return getPreferredSize (getRows (), getColumns ()); + } + + /** + * Retrieve the preferred size that this text area would have if its + * row and column values were equal to those specified. A text + * area's preferred size depends on the number of rows and columns + * of text it would prefer to display, and on the size of the font + * in which the text would be displayed. + * + * @param rows The number of rows to use in the preferred size + * calculation. + * @param columns The number of columns to use in the preferred size + * calculation. + * + * @return The preferred size for this text area. + */ + public Dimension getPreferredSize (int rows, int columns) + { + return preferredSize (rows, columns); + } + + /** + * Retrieve the preferred size for this text area, considering the + * text area's current row and column values. A text area's preferred + * size depends on the number of rows and columns of text it would + * prefer to display, and on the size of the font in which the text + * would be displayed. + * + * @return The preferred size for this text field. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize ()</code>. + */ + public Dimension preferredSize () + { + return preferredSize (getRows (), getColumns ()); + } + + /** + * Retrieve the preferred size that this text area would have if its + * row and column values were equal to those specified. A text + * area's preferred size depends on the number of rows and columns + * of text it would prefer to display, and on the size of the font + * in which the text would be displayed. + * + * @param rows The number of rows to use in the preferred size + * calculation. + * @param columns The number of columns to use in the preferred size + * calculation. + * + * @return The preferred size for this text area. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize (int, int)</code>. + */ + public Dimension preferredSize (int rows, int columns) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + // Sun returns Dimension (0,0) in this case. + if (peer == null) + return new Dimension (0, 0); + + return peer.getPreferredSize (rows, columns); + } + + /** + * Retrieve the scroll bar display policy -- one of SCROLLBARS_BOTH, + * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY, + * SCROLLBARS_NONE. + * + * @return The current scroll bar display policy. + */ + public int getScrollbarVisibility () + { + return scrollbarVisibility; + } + + /** + * Notify this object that it should create its native peer. + */ + public void addNotify () + { + if (getPeer () == null) + setPeer ((ComponentPeer) getToolkit().createTextArea (this)); + } + + /** + * Append the specified text to the end of the current text. + * + * @param str The text to append. + */ + public void append (String str) + { + appendText (str); + } + + /** + * Append the specified text to the end of the current text. + * + * @param str The text to append. + * + * @deprecated This method is deprecated in favor of + * <code>append ()</code>. + */ + public void appendText (String str) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + if (peer != null) + peer.insert (str, peer.getText().length ()); + } + + /** + * Insert the specified text at the specified position. The first + * character in the text area is at position zero. + * + * @param str The text to insert. + * @param pos The position at which to insert text. + */ + public void insert (String str, int pos) + { + insertText (str, pos); + } + + /** + * Insert the specified text at the specified position. The first + * character in the text area is at position zero. + * + * @param str The text to insert. + * @param pos The position at which to insert text. + * + * @deprecated This method is deprecated in favor of + * <code>insert ()</code>. + */ + public void insertText (String str, int pos) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + if (peer != null) + peer.insert (str, pos); + } + + /** + * Replace a range of characters with the specified text. The + * character at the start position will be replaced, unless start == + * end. The character at the end posistion will not be replaced. + * The first character in the text area is at position zero. The + * length of the replacement text may differ from the length of the + * text that is replaced. + * + * @param str The new text for the range. + * @param start The start position of the replacement range. + * @param end The end position of the replacement range. + */ + public void replaceRange (String str, int start, int end) + { + replaceText (str, start, end); + } + + /** + * Replace a range of characters with the specified text. The + * character at the start position will be replaced, unless start == + * end. The character at the end posistion will not be replaced. + * The first character in the text area is at position zero. The + * length of the replacement text may differ from the length of the + * text that is replaced. + * + * @param str The new text for the range. + * @param start The start position of the replacement range. + * @param end The end position of the replacement range. + * + * @deprecated This method is deprecated in favor of + * <code>replaceRange ()</code>. + */ + public void replaceText (String str, int start, int end) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + if (peer != null) + peer.replaceRange (str, start, end); + } + + /** + * Retrieve a debugging string for this text area. + * + * @return A debugging string for this text area. + */ + protected String paramString () + { + String sbVisibility = ""; + + switch (scrollbarVisibility) + { + case SCROLLBARS_BOTH: + sbVisibility = "both"; + break; + case SCROLLBARS_VERTICAL_ONLY: + sbVisibility = "vertical-only"; + break; + case SCROLLBARS_HORIZONTAL_ONLY: + sbVisibility = "horizontal-only"; + break; + case SCROLLBARS_NONE: + sbVisibility = "none"; + break; + } + + String editable = ""; + if (isEditable ()) + editable = "editable,"; + + return getName () + "," + getX () + "," + getY () + "," + getWidth () + + "x" + getHeight () + "," + "text=" + getText () + "," + editable + + "selection=" + getSelectionStart () + "-" + getSelectionEnd () + + ",rows=" + rows + ",columns=" + columns + ",scrollbarVisibility=" + + sbVisibility; + } + + /** + * Generate a unique name for this text area. + * + * @return A unique name for this text area. + */ + String generateName () + { + return "text" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_text_number++; + } + + protected class AccessibleAWTTextArea extends AccessibleAWTTextComponent + { + protected AccessibleAWTTextArea() + { + } + + public AccessibleStateSet getAccessibleStateSet() + { + return super.getAccessibleStateSet(); + } + } + + /** + * Gets the AccessibleContext associated with this <code>TextArea</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTTextArea(); + return accessibleContext; + } +} diff --git a/libjava/classpath/java/awt/TextComponent.java b/libjava/classpath/java/awt/TextComponent.java new file mode 100644 index 00000000000..9edbd88fe33 --- /dev/null +++ b/libjava/classpath/java/awt/TextComponent.java @@ -0,0 +1,739 @@ +/* TextComponent.java -- Widgets for entering text + Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.TextEvent; +import java.awt.event.TextListener; +import java.awt.peer.TextComponentPeer; +import java.io.Serializable; +import java.text.BreakIterator; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; +import javax.swing.text.AttributeSet; + +/** + * This class provides common functionality for widgets than + * contain text. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextComponent extends Component + implements Serializable, Accessible +{ + +/* + * Static Variables + */ + +// Constant for serialization +private static final long serialVersionUID = -2214773872412987419L; + +/* + * Instance Variables + */ + +/** + * @serial Indicates whether or not this component is editable. + * This is package-private to avoid an accessor method. + */ +boolean editable; + +/** + * @serial The starting position of the selected text region. + * This is package-private to avoid an accessor method. + */ +int selectionStart; + +/** + * @serial The ending position of the selected text region. + * This is package-private to avoid an accessor method. + */ +int selectionEnd; + +/** + * @serial The text in the component + * This is package-private to avoid an accessor method. + */ +String text; + +/** + * A list of listeners that will receive events from this object. + */ +protected transient TextListener textListener; + + protected class AccessibleAWTTextComponent + extends AccessibleAWTComponent + implements AccessibleText, TextListener + { + // Constructor + // Adds a listener for tracking caret changes + public AccessibleAWTTextComponent() + { + TextComponent.this.addTextListener(this); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TEXT; + } + + public AccessibleStateSet getAccessibleStateSet() + { + // TODO: Docs say PropertyChangeEvent will fire if this state changes. + // That means that the event has to fire when editable changes. + AccessibleStateSet ss = super.getAccessibleStateSet(); + if (editable) + ss.add(AccessibleState.EDITABLE); + return ss; + } + + public AccessibleText getAccessibleText() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point) + */ + public int getIndexAtPoint(Point point) + { + return TextComponent.this.getIndexAtPoint(point); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharacterBounds(int) + */ + public Rectangle getCharacterBounds(int index) + { + return TextComponent.this.getCharacterBounds(index); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharCount() + */ + public int getCharCount() + { + return text.length(); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCaretPosition() + */ + public int getCaretPosition() + { + return TextComponent.this.getCaretPosition(); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getAtIndex(int, int) + */ + public String getAtIndex(int part, int index) + { + if (index < 0 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index, index + 1); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int start = index; + if (!it.isBoundary(index)) + start = it.preceding(index); + int end = it.following(index); + if (end == -1) + return text.substring(index); + else + return text.substring(index, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getAfterIndex(int, int) + */ + public String getAfterIndex(int part, int index) { + if (index < 0 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index, index + 1); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int start = index; + if (!it.isBoundary(index)) + start = it.following(index); + // Make sure there was a complete unit. I.e. if index is in the middle + // of a word, return null if there is no word after the that one. + if (start == -1) + return null; + int end = it.following(start); + if (end == -1) + return text.substring(index); + else + return text.substring(index, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int) + */ + public String getBeforeIndex(int part, int index) + { + if (index < 1 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index - 1, index); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int end = index; + if (!it.isBoundary(index)) + end = it.preceding(index); + // Make sure there was a complete unit. I.e. if index is in the middle + // of a word, return null if there is no word before that one. + if (end == -1) + return null; + int start = it.preceding(end); + if (start == -1) + return text.substring(0, end); + else + return text.substring(start, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharacterAttribute(int) + */ + public AttributeSet getCharacterAttribute(int index) + { + // FIXME: I suspect this really gets filled in by subclasses. + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectionStart() + */ + public int getSelectionStart() { + // TODO Auto-generated method stub + return selectionStart; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectionEnd() + */ + public int getSelectionEnd() + { + return selectionEnd; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectedText() + */ + public String getSelectedText() + { + if (selectionEnd - selectionStart > 0) + return text.substring(selectionStart, selectionEnd); + else + return null; + } + + /* (non-Javadoc) + * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent) + */ + public void textValueChanged(TextEvent event) + { + // TODO Auto-generated method stub + + } + + } + +/*************************************************************************/ + +/* + * Constructors + */ + +TextComponent(String text) +{ + this.text = text; + this.editable = true; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the text in this component + * + * @return The text in this component. + */ +public synchronized String +getText() +{ + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + text = tcp.getText(); + + return(text); +} + +/*************************************************************************/ + +/** + * Sets the text in this component to the specified string. + * + * @param text The new text for this component. + */ +public synchronized void +setText(String text) +{ + if (text == null) + text = ""; + + this.text = text; + + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + tcp.setText(text); + setCaretPosition(0); +} + +/*************************************************************************/ + +/** + * Returns a string that contains the text that is currently selected. + * + * @return The currently selected text region. + */ +public synchronized String +getSelectedText() +{ + String alltext = getText(); + int start = getSelectionStart(); + int end = getSelectionEnd(); + + return(alltext.substring(start, end)); +} + +/*************************************************************************/ + +/** + * Returns the starting position of the selected text region. + * If the text is not selected then caret position is returned. + * + * @return The starting position of the selected text region. + */ +public synchronized int +getSelectionStart() +{ + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + selectionStart = tcp.getSelectionStart(); + + return(selectionStart); +} + +/*************************************************************************/ + +/** + * Sets the starting position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionStart The new start position for selected text. + */ +public synchronized void +setSelectionStart(int selectionStart) +{ + select(selectionStart, getSelectionEnd()); +} + +/*************************************************************************/ + +/** + * Returns the ending position of the selected text region. + * If the text is not selected, then caret position is returned + * + * @return The ending position of the selected text region. + */ +public synchronized int +getSelectionEnd() +{ + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + selectionEnd = tcp.getSelectionEnd(); + + return(selectionEnd); +} + +/*************************************************************************/ + +/** + * Sets the ending position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionEnd The new start position for selected text. + */ +public synchronized void +setSelectionEnd(int selectionEnd) +{ + select(getSelectionStart(), selectionEnd); +} + +/*************************************************************************/ + +/** + * This method sets the selected text range to the text between the + * specified start and end positions. Illegal values for these + * positions are silently fixed. + * + * @param selectionStart The new start position for the selected text. + * @param selectionEnd The new end position for the selected text. + */ +public synchronized void +select(int selectionStart, int selectionEnd) +{ + if (selectionStart < 0) + selectionStart = 0; + + if (selectionStart > getText().length()) + selectionStart = text.length(); + + if (selectionEnd > text.length()) + selectionEnd = text.length(); + + if (selectionStart > getSelectionEnd()) + selectionStart = selectionEnd; + + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + tcp.select(selectionStart, selectionEnd); +} + +/*************************************************************************/ + +/** + * Selects all of the text in the component. + */ +public synchronized void +selectAll() +{ + select(0, getText().length()); +} + +/*************************************************************************/ + +/** + * Returns the current caret position in the text. + * + * @return The caret position in the text. + */ +public synchronized int +getCaretPosition() +{ + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + return(tcp.getCaretPosition()); + else + return(0); +} + +/*************************************************************************/ + +/** + * Sets the caret position to the specified value. + * + * @param caretPosition The new caret position. + * + * @exception IllegalArgumentException If the value supplied for position + * is less than zero. + * + * @since 1.1 + */ +public synchronized void +setCaretPosition(int caretPosition) +{ + if (caretPosition < 0) + throw new IllegalArgumentException (); + + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + tcp.setCaretPosition(caretPosition); +} + +/*************************************************************************/ + +/** + * Tests whether or not this component's text can be edited. + * + * @return <code>true</code> if the text can be edited, <code>false</code> + * otherwise. + */ +public boolean +isEditable() +{ + return(editable); +} + +/*************************************************************************/ + +/** + * Sets whether or not this component's text can be edited. + * + * @param editable <code>true</code> to enable editing of the text, + * <code>false</code> to disable it. + */ +public synchronized void +setEditable(boolean editable) +{ + this.editable = editable; + + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + tcp.setEditable(editable); +} + +/*************************************************************************/ + +/** + * Notifies the component that it should destroy its native peer. + */ +public void +removeNotify() +{ + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Adds a new listener to the list of text listeners for this + * component. + * + * @param listener The listener to be added. + */ +public synchronized void +addTextListener(TextListener listener) +{ + textListener = AWTEventMulticaster.add(textListener, listener); + + enableEvents(AWTEvent.TEXT_EVENT_MASK); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of listeners + * for this component. + * + * @param listener The listener to remove. + */ +public synchronized void +removeTextListener(TextListener listener) +{ + textListener = AWTEventMulticaster.remove(textListener, listener); +} + +/*************************************************************************/ + +/** + * Processes the specified event for this component. Text events are + * processed by calling the <code>processTextEvent()</code> method. + * All other events are passed to the superclass method. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof TextEvent) + processTextEvent((TextEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes the specified text event by dispatching it to any listeners + * that are registered. Note that this method will only be called + * if text event's are enabled. This will be true if there are any + * registered listeners, or if the event has been specifically + * enabled using <code>enableEvents()</code>. + * + * @param event The text event to process. + */ +protected void +processTextEvent(TextEvent event) +{ + if (textListener != null) + textListener.textValueChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= TextEvent.TEXT_LAST + && e.id >= TextEvent.TEXT_FIRST + && (textListener != null + || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string. + * + * @return A debugging string. + */ +protected String +paramString() +{ + return(getClass().getName() + "(text=" + getText() + ")"); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this <code>TextComponent</code>. FooListeners are registered using + * the addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == TextListener.class) + return AWTEventMulticaster.getListeners (textListener, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns all text listeners registered to this object. + */ + public TextListener[] getTextListeners () + { + return (TextListener[]) getListeners (TextListener.class); + } + + /** + * Gets the AccessibleContext associated with this <code>TextComponent</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTTextComponent(); + return accessibleContext; + } + + + /*******************************/ + // Provide AccessibleAWTTextComponent access to several peer functions that + // aren't publicly exposed. This is package-private to avoid an accessor + // method. + synchronized int + getIndexAtPoint(Point p) + { + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + return tcp.getIndexAtPoint(p.x, p.y); + return -1; + } + + synchronized Rectangle + getCharacterBounds(int i) + { + TextComponentPeer tcp = (TextComponentPeer)getPeer(); + if (tcp != null) + return tcp.getCharacterBounds(i); + return null; + } + + + + +} // class TextComponent + diff --git a/libjava/classpath/java/awt/TextField.java b/libjava/classpath/java/awt/TextField.java new file mode 100644 index 00000000000..4d62d024aad --- /dev/null +++ b/libjava/classpath/java/awt/TextField.java @@ -0,0 +1,541 @@ +/* TextField.java -- A one line text entry field + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.ComponentPeer; +import java.awt.peer.TextFieldPeer; +import java.util.EventListener; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleStateSet; + +/** + * This class implements a single line text entry field widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextField extends TextComponent +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = -2966288784432217853L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The number of columns in the text entry field. + */ +private int columns; + +/** + * @serial The character that is echoed when doing protected input + */ +private char echoChar; + +// List of registered ActionListener's for this object. +private ActionListener action_listeners; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of <code>TextField</code> that is empty + * and has one column. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ +public +TextField() +{ + this("", 1); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>TextField</code> containing + * the specified text. The number of columns will be equal to the + * length of the text string. + * + * @param text The text to display in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ +public +TextField(String text) +{ + this(text, text.length()); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>TextField</code> that is empty + * and has the specified number of columns. + * + * @param columns The number of columns in the text field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ +public +TextField(int columns) +{ + this("", columns); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>TextField</code> with the + * specified text and number of columns. + * + * @param text The text to display in the field. + * @param columns The number of columns in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ +public +TextField(String text, int columns) +{ + super(text); + this.columns = columns; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the number of columns in the field. + * + * @return The number of columns in the field. + */ +public int +getColumns() +{ + return(columns); +} + +/*************************************************************************/ + +/** + * Sets the number of columns in this field to the specified value. + * + * @param columns The new number of columns in the field. + * + * @exception IllegalArgumentException If columns is less than zero. + */ +public synchronized void +setColumns(int columns) +{ + if (columns < 0) + throw new IllegalArgumentException("Value is less than zero: " + + columns); + + this.columns = columns; + // FIXME: How to we communicate this to our peer? +} + +/*************************************************************************/ + +/** + * Returns the character that is echoed to the screen when a text + * field is protected (such as when a password is being entered). + * + * @return The echo character for this text field. + */ +public char +getEchoChar() +{ + return(echoChar); +} + +/*************************************************************************/ + +/** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + */ +public void +setEchoChar(char echoChar) +{ + setEchoCharacter (echoChar); +} + +/*************************************************************************/ + +/** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + * + * @deprecated This method is deprecated in favor of + * <code>setEchoChar()</code> + */ +public void +setEchoCharacter(char echoChar) +{ + this.echoChar = echoChar; + + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer != null) + peer.setEchoChar (echoChar); +} + +/*************************************************************************/ + +/** + * Tests whether or not this text field has an echo character set + * so that characters the user type are not echoed to the screen. + * + * @return <code>true</code> if an echo character is set, + * <code>false</code> otherwise. + */ +public boolean +echoCharIsSet() +{ + if (echoChar == '\u0000') + return(false); + else + return(true); +} + +/*************************************************************************/ + +/** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + */ +public Dimension +getMinimumSize() +{ + return getMinimumSize (getColumns ()); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + */ +public Dimension +getMinimumSize(int columns) +{ + return minimumSize (columns); +} + +/*************************************************************************/ + +/** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize()</code>. + */ +public Dimension +minimumSize() +{ + return minimumSize (getColumns ()); +} + +/*************************************************************************/ + +/** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize(int)</code>. + */ +public Dimension +minimumSize(int columns) +{ + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return null; // FIXME: What do we do if there is no peer? + + return peer.getMinimumSize (columns); +} + +/*************************************************************************/ + +/** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + */ +public Dimension +getPreferredSize() +{ + return getPreferredSize (getColumns ()); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + */ +public Dimension +getPreferredSize(int columns) +{ + return preferredSize (columns); +} + +/*************************************************************************/ + +/** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize()</code>. + */ +public Dimension +preferredSize() +{ + return preferredSize (getColumns ()); +} + +/*************************************************************************/ + +/** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize(int)</code>. + */ +public Dimension +preferredSize(int columns) +{ + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return new Dimension (0, 0); + + return peer.getPreferredSize (columns); +} + +/*************************************************************************/ + +/** + * Notifies this object that it should create its native peer. + */ +public void +addNotify() +{ + if (getPeer() != null) + return; + + setPeer((ComponentPeer)getToolkit().createTextField(this)); +} + +/*************************************************************************/ + +/** + * Addes a new listener to the list of action listeners for this + * object. + * + * @param listener The listener to add to the list. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); + + enableEvents(AWTEvent.ACTION_EVENT_MASK); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of action listeners + * for this object. + * + * @param listener The listener to remove from the list. + */ +public synchronized void +removeActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes the specified event. If the event is an instance of + * <code>ActionEvent</code> then <code>processActionEvent()</code> is + * called to process it, otherwise the event is sent to the + * superclass. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes an action event by calling any registered listeners. + * Note to subclasses: This method is not called unless action events + * are enabled on this object. This will be true if any listeners + * are registered, or if action events were specifically enabled + * using <code>enableEvents()</code>. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + action_listeners.actionPerformed(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debug string for this object. + * + * @return A debug string for this object. + */ +protected String +paramString() +{ + return(getClass().getName() + "(columns=" + getColumns() + ",echoChar=" + + getEchoChar()); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this <code>TextField</code>. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == ActionListener.class) + return AWTEventMulticaster.getListeners (action_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Return all ActionListeners register to this <code>TextField</code> object + * as an array. + * + * @since 1.4 + */ + public ActionListener[] getActionListeners () + { + return (ActionListener[]) getListeners (ActionListener.class); + } + + protected class AccessibleAWTTextField extends AccessibleAWTTextComponent + { + protected AccessibleAWTTextField() + { + } + + public AccessibleStateSet getAccessibleStateSet() + { + return super.getAccessibleStateSet(); + } + } + + public AccessibleContext getAccessibleContext() + { + return new AccessibleAWTTextField(); + } + +} // class TextField diff --git a/libjava/classpath/java/awt/TexturePaint.java b/libjava/classpath/java/awt/TexturePaint.java new file mode 100644 index 00000000000..a12e38463f1 --- /dev/null +++ b/libjava/classpath/java/awt/TexturePaint.java @@ -0,0 +1,75 @@ +/* TexturePaint.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; + +/** STUB CLASS ONLY */ +public class TexturePaint implements Paint +{ + private final BufferedImage texture; + private final Rectangle2D anchor; + public TexturePaint(BufferedImage texture, Rectangle2D anchor) + { + this.texture = texture; + this.anchor = anchor; + } + public BufferedImage getImage() + { + return texture; + } + public Rectangle2D getAnchorRect() + { + return anchor; + } + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + throw new Error("not implemented"); + } + public int getTransparency() + { + throw new Error("not implemented"); + } +} // class TexturePaint diff --git a/libjava/classpath/java/awt/Toolkit.java b/libjava/classpath/java/awt/Toolkit.java new file mode 100644 index 00000000000..c7c6f9f0ecb --- /dev/null +++ b/libjava/classpath/java/awt/Toolkit.java @@ -0,0 +1,995 @@ +/* Toolkit.java -- AWT Toolkit superclass + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.event.AWTEventListener; +import java.awt.event.KeyEvent; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.LightweightPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.net.URL; +import java.util.Map; +import java.util.Properties; + +/** + * The AWT system uses a set of native peer objects to implement its + * widgets. These peers are provided by a peer toolkit, that is accessed + * via a subclass of this superclass. The system toolkit is retrieved + * by the static methods <code>getDefaultToolkit</code>. This method + * determines the system toolkit by examining the system property + * <code>awt.toolkit</code>. That property is set to the name of the + * <code>Toolkit</code> subclass for the specified peer set. If the + * <code>awt.toolkit</code> property is not set, then the default + * toolkit <code>gnu.java.awt.peer.gtk.GtkToolkit</code> is used. This + * toolkit creates its peers using the GTK+ toolkit. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Toolkit +{ + /** The default toolkit name. */ + private static String default_toolkit_name + = gnu.classpath.Configuration.default_awt_peer_toolkit; + + /** + * The toolkit in use. Once we load it, we don't ever change it + * if the awt.toolkit property is set. + */ + private static Toolkit toolkit; + + /** The toolkit properties. */ + private static Properties props = new Properties(); + + protected final Map desktopProperties = new Properties(); + + protected final PropertyChangeSupport desktopPropsSupport + = new PropertyChangeSupport(this); + + /** + * Default constructor for subclasses. + */ + public Toolkit() + { + } + + /** + * Creates a peer object for the specified <code>Button</code>. + * + * @param target The <code>Button</code> to create the peer for. + * + * @return The peer for the specified <code>Button</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ButtonPeer createButton(Button target); + + /** + * Creates a peer object for the specified <code>TextField</code>. + * + * @param target The <code>TextField</code> to create the peer for. + * + * @return The peer for the specified <code>TextField</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract TextFieldPeer createTextField(TextField target); + + /** + * Creates a peer object for the specified <code>Label</code>. + * + * @param target The <code>Label</code> to create the peer for. + * + * @return The peer for the specified <code>Label</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract LabelPeer createLabel(Label target); + + /** + * Creates a peer object for the specified <code>List</code>. + * + * @param target The <code>List</code> to create the peer for. + * + * @return The peer for the specified <code>List</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ListPeer createList(List target); + + /** + * Creates a peer object for the specified <code>Checkbox</code>. + * + * @param target The <code>Checkbox</code> to create the peer for. + * + * @return The peer for the specified <code>Checkbox</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract CheckboxPeer createCheckbox(Checkbox target); + + /** + * Creates a peer object for the specified <code>Scrollbar</code>. + * + * @param target The <code>Scrollbar</code> to create the peer for. + * + * @return The peer for the specified <code>Scrollbar</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ScrollbarPeer createScrollbar(Scrollbar target); + + /** + * Creates a peer object for the specified <code>ScrollPane</code>. + * + * @param target The <code>ScrollPane</code> to create the peer for. + * + * @return The peer for the specified <code>ScrollPane</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ScrollPanePeer createScrollPane(ScrollPane target); + + /** + * Creates a peer object for the specified <code>TextArea</code>. + * + * @param target The <code>TextArea</code> to create the peer for. + * + * @return The peer for the specified <code>TextArea</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract TextAreaPeer createTextArea(TextArea target); + + /** + * Creates a peer object for the specified <code>Choice</code>. + * + * @param target The <code>Choice</code> to create the peer for. + * + * @return The peer for the specified <code>Choice</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ChoicePeer createChoice(Choice target); + + /** + * Creates a peer object for the specified <code>Frame</code>. + * + * @param target The <code>Frame</code> to create the peer for. + * + * @return The peer for the specified <code>Frame</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract FramePeer createFrame(Frame target); + + /** + * Creates a peer object for the specified <code>Canvas</code>. + * + * @param target The <code>Canvas</code> to create the peer for. + * + * @return The peer for the specified <code>Canvas</code> object. + */ + protected abstract CanvasPeer createCanvas(Canvas target); + + /** + * Creates a peer object for the specified <code>Panel</code>. + * + * @param target The <code>Panel</code> to create the peer for. + * + * @return The peer for the specified <code>Panel</code> object. + */ + protected abstract PanelPeer createPanel(Panel target); + + /** + * Creates a peer object for the specified <code>Window</code>. + * + * @param target The <code>Window</code> to create the peer for. + * + * @return The peer for the specified <code>Window</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract WindowPeer createWindow(Window target); + + /** + * Creates a peer object for the specified <code>Dialog</code>. + * + * @param target The dialog to create the peer for + * + * @return The peer for the specified font name. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract DialogPeer createDialog(Dialog target); + + /** + * Creates a peer object for the specified <code>MenuBar</code>. + * + * @param target The <code>MenuBar</code> to create the peer for. + * + * @return The peer for the specified <code>MenuBar</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuBarPeer createMenuBar(MenuBar target); + + /** + * Creates a peer object for the specified <code>Menu</code>. + * + * @param target The <code>Menu</code> to create the peer for. + * + * @return The peer for the specified <code>Menu</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuPeer createMenu(Menu target); + + /** + * Creates a peer object for the specified <code>PopupMenu</code>. + * + * @param target The <code>PopupMenu</code> to create the peer for. + * + * @return The peer for the specified <code>PopupMenu</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract PopupMenuPeer createPopupMenu(PopupMenu target); + + /** + * Creates a peer object for the specified <code>MenuItem</code>. + * + * @param target The <code>MenuItem</code> to create the peer for. + * + * @return The peer for the specified <code>MenuItem</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuItemPeer createMenuItem(MenuItem target); + + /** + * Creates a peer object for the specified <code>FileDialog</code>. + * + * @param target The <code>FileDialog</code> to create the peer for. + * + * @return The peer for the specified <code>FileDialog</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract FileDialogPeer createFileDialog(FileDialog target); + + /** + * Creates a peer object for the specified <code>CheckboxMenuItem</code>. + * + * @param target The <code>CheckboxMenuItem</code> to create the peer for. + * + * @return The peer for the specified <code>CheckboxMenuItem</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract CheckboxMenuItemPeer + createCheckboxMenuItem(CheckboxMenuItem target); + + /** + * Creates a peer object for the specified <code>Component</code>. The + * peer returned by this method is not a native windowing system peer + * with its own native window. Instead, this method allows the component + * to draw on its parent window as a "lightweight" widget. + * + * @param target The <code>Component</code> to create the peer for. + * + * @return The peer for the specified <code>Component</code> object. + */ + protected LightweightPeer createComponent(Component target) + { + return new gnu.java.awt.peer.GLightweightPeer (target); + } + + /** + * Creates a peer object for the specified font name. + * + * @param name The font to create the peer for. + * @param style The font style to create the peer for. + * + * @return The peer for the specified font name. + * + * @deprecated + */ + protected abstract FontPeer getFontPeer(String name, int style); + + /** + * Copies the current system colors into the specified array. This is + * the interface used by the <code>SystemColor</code> class. Although + * this method fills in the array with some default colors a real Toolkit + * should override this method and provide real system colors for the + * native GUI platform. + * + * @param systemColors The array to copy the system colors into. + * It must be at least 26 elements. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @see java.awt.SystemColor + */ + protected void loadSystemColors(int systemColors[]) + { + systemColors[SystemColor.DESKTOP] = 0xFF005C5C; + systemColors[SystemColor.ACTIVE_CAPTION] = 0xFF000080; + systemColors[SystemColor.ACTIVE_CAPTION_TEXT] = 0xFFFFFFFF; + systemColors[SystemColor.ACTIVE_CAPTION_BORDER] = 0xFFC0C0C0; + systemColors[SystemColor.INACTIVE_CAPTION] = 0xFF808080; + systemColors[SystemColor.INACTIVE_CAPTION_TEXT] = 0xFFC0C0C0; + systemColors[SystemColor.INACTIVE_CAPTION_BORDER] = 0xFFC0C0C0; + systemColors[SystemColor.WINDOW] = 0xFFFFFFFF; + systemColors[SystemColor.WINDOW_BORDER] = 0xFF000000; + systemColors[SystemColor.WINDOW_TEXT] = 0xFF000000; + systemColors[SystemColor.MENU] = 0xFFC0C0C0; + systemColors[SystemColor.MENU_TEXT] = 0xFF000000; + systemColors[SystemColor.TEXT] = 0xFFC0C0C0; + systemColors[SystemColor.TEXT_TEXT] = 0xFF000000; + systemColors[SystemColor.TEXT_HIGHLIGHT] = 0xFF000090; + systemColors[SystemColor.TEXT_HIGHLIGHT_TEXT] = 0xFFFFFFFF; + systemColors[SystemColor.TEXT_INACTIVE_TEXT] = 0xFF808080; + systemColors[SystemColor.CONTROL] = 0xFFC0C0C0; + systemColors[SystemColor.CONTROL_TEXT] = 0xFF000000; + systemColors[SystemColor.CONTROL_HIGHLIGHT] = 0xFFFFFFFF; + systemColors[SystemColor.CONTROL_LT_HIGHLIGHT] = 0xFFE0E0E0; + systemColors[SystemColor.CONTROL_SHADOW] = 0xFF808080; + systemColors[SystemColor.CONTROL_DK_SHADOW] = 0xFF000000; + systemColors[SystemColor.SCROLLBAR] = 0xFFE0E0E0; + systemColors[SystemColor.INFO] = 0xFFE0E000; + systemColors[SystemColor.INFO_TEXT] = 0xFF000000; + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public void setDynamicLayout(boolean dynamic) + { + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected boolean isDynamicLayoutSet() + { + return false; + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public boolean isDynamicLayoutActive() + { + return false; + } + + /** + * Returns the dimensions of the screen in pixels. + * + * @return The dimensions of the screen in pixels. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract Dimension getScreenSize(); + + /** + * Returns the screen resolution in dots per square inch. + * + * @return The screen resolution in dots per square inch. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract int getScreenResolution(); + + /** + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @since 1.4 + */ + public Insets getScreenInsets(GraphicsConfiguration gc) + { + return null; + } + + /** + * Returns the color model of the screen. + * + * @return The color model of the screen. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract ColorModel getColorModel(); + + /** + * Returns the names of the available fonts. + * + * @return The names of the available fonts. + * + * @deprecated + */ + public abstract String[] getFontList(); + + /** + * Return the font metrics for the specified font + * + * @param name The name of the font to return metrics for. + * + * @return The requested font metrics. + * + * @deprecated + */ + public abstract FontMetrics getFontMetrics(Font name); + + /** + * Flushes any buffered data to the screen so that it is in sync with + * what the AWT system has drawn to it. + */ + public abstract void sync(); + + /** + * Returns an instance of the default toolkit. The default toolkit is + * the subclass of <code>Toolkit</code> specified in the system property + * <code>awt.toolkit</code>, or <code>gnu.java.awt.peer.gtk.GtkToolkit</code> + * if the property is not set. + * + * @return An instance of the system default toolkit. + * + * @throws AWTError If the toolkit cannot be loaded. + */ + public static Toolkit getDefaultToolkit() + { + if (toolkit != null) + return toolkit; + String toolkit_name = System.getProperty("awt.toolkit", + default_toolkit_name); + try + { + Class cls = Class.forName(toolkit_name); + Object obj = cls.newInstance(); + if (!(obj instanceof Toolkit)) + throw new AWTError(toolkit_name + " is not a subclass of " + + "java.awt.Toolkit"); + toolkit = (Toolkit) obj; + return toolkit; + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + AWTError e = new AWTError("Cannot load AWT toolkit: " + toolkit_name); + throw (AWTError) e.initCause(t); + } + } + + /** + * Returns an image from the specified file, which must be in a + * recognized format. Supported formats vary from toolkit to toolkit. + * + * @return name The name of the file to read the image from. + */ + public abstract Image getImage(String name); + + /** + * Returns an image from the specified URL, which must be in a + * recognized format. Supported formats vary from toolkit to toolkit. + * + * @return url The URl to read the image from. + */ + public abstract Image getImage(URL url); + + public abstract Image createImage(String filename); + + public abstract Image createImage(URL url); + + /** + * Readies an image to be rendered on the screen. The width and height + * values can be set to the default sizes for the image by passing -1 + * in those parameters. + * + * @param image The image to prepare for rendering. + * @param width The width of the image. + * @param height The height of the image. + * @param observer The observer to receive events about the preparation + * process. + * + * @return <code>true</code> if the image is already prepared for rendering, + * <code>false</code> otherwise. + */ + public abstract boolean prepareImage(Image image, int width, int height, + ImageObserver observer); + + /** + * Checks the status of specified image as it is being readied for + * rendering. + * + * @param image The image to prepare for rendering. + * @param width The width of the image. + * @param height The height of the image. + * @param observer The observer to receive events about the preparation + * process. + * + * @return A union of the bitmasks from + * <code>java.awt.image.ImageObserver</code> that indicates the current + * state of the imaging readying process. + */ + public abstract int checkImage(Image image, int width, int height, + ImageObserver observer); + + /** + * Creates an image using the specified <code>ImageProducer</code> + * + * @param producer The <code>ImageProducer</code> to create the image from. + * + * @return The created image. + */ + public abstract Image createImage(ImageProducer producer); + + /** + * Creates an image from the specified byte array. The array must be in + * a recognized format. Supported formats vary from toolkit to toolkit. + * + * @param data The raw image data. + * + * @return The created image. + */ + public Image createImage(byte[] data) + { + return createImage(data, 0, data.length); + } + + /** + * Creates an image from the specified portion of the byte array passed. + * The array must be in a recognized format. Supported formats vary from + * toolkit to toolkit. + * + * @param data The raw image data. + * @param offset The offset into the data where the image data starts. + * @param len The length of the image data. + * + * @return The created image. + */ + public abstract Image createImage(byte[] data, int offset, int len); + + /** + * Returns a instance of <code>PrintJob</code> for the specified + * arguments. + * + * @param frame The window initiating the print job. + * @param title The print job title. + * @param props The print job properties. + * + * @return The requested print job, or <code>null</code> if the job + * was cancelled. + * + * @exception NullPointerException If frame is null, + * or GraphicsEnvironment.isHeadless() returns true. + * @exception SecurityException If this thread is not allowed to initiate + * a print job request. + */ + public abstract PrintJob getPrintJob(Frame frame, String title, + Properties props); + + /** + * Returns a instance of <code>PrintJob</code> for the specified + * arguments. + * + * @param frame The window initiating the print job. + * @param title The print job title. + * @param jobAttr A set of job attributes which will control the print job. + * @param pageAttr A set of page attributes which will control the print job. + * + * @exception NullPointerException If frame is null, and either jobAttr is null + * or jobAttr.getDialog() returns JobAttributes.DialogType.NATIVE. + * @exception IllegalArgumentException If pageAttrspecifies differing cross + * feed and feed resolutions, or when GraphicsEnvironment.isHeadless() returns + * true. + * @exception SecurityException If this thread is not allowed to initiate + * a print job request. + * + * @since 1.3 + */ + public PrintJob getPrintJob(Frame frame, String title, + JobAttributes jobAttr, PageAttributes pageAttr) + { + return null; + } + + /** + * Causes a "beep" tone to be generated. + */ + public abstract void beep(); + + /** + * Returns the system clipboard. + * + * @return THe system clipboard. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract Clipboard getSystemClipboard(); + + /** + * Gets the singleton instance of the system selection as a Clipboard object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @since 1.4 + */ + public Clipboard getSystemSelection() + { + return null; + } + + /** + * Returns the accelerator key mask for menu shortcuts. The default is + * <code>Event.CTRL_MASK</code>. A toolkit must override this method + * to change the default. + * + * @return The key mask for the menu accelerator key. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public int getMenuShortcutKeyMask() + { + return Event.CTRL_MASK; + } + + /** + * Returns whether the given locking key on the keyboard is currently in its + * "on" state. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * @exception IllegalArgumentException If keyCode is not one of the valid keys. + * @exception UnsupportedOperationException If the host system doesn't allow + * getting the state of this key programmatically, or if the keyboard doesn't + * have this key. + */ + public boolean getLockingKeyState(int keyCode) + { + if (keyCode != KeyEvent.VK_CAPS_LOCK + && keyCode != KeyEvent.VK_NUM_LOCK + && keyCode != KeyEvent.VK_SCROLL_LOCK) + throw new IllegalArgumentException(); + + throw new UnsupportedOperationException(); + } + + /** + * Sets the state of the given locking key on the keyboard. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * @exception IllegalArgumentException If keyCode is not one of the valid keys. + * @exception UnsupportedOperationException If the host system doesn't allow + * getting the state of this key programmatically, or if the keyboard doesn't + * have this key. + */ + public void setLockingKeyState(int keyCode, boolean on) + { + if (keyCode != KeyEvent.VK_CAPS_LOCK + && keyCode != KeyEvent.VK_NUM_LOCK + && keyCode != KeyEvent.VK_SCROLL_LOCK) + throw new IllegalArgumentException(); + + throw new UnsupportedOperationException(); + } + + /** + * Returns the native container object of the specified component. This + * method is necessary because the parent component might be a lightweight + * component. + * + * @param component The component to fetch the native container for. + * + * @return The native container object for this component. + */ + protected static Container getNativeContainer(Component component) + { + component = component.getParent(); + while (true) + { + if (component == null) + return null; + if (! (component instanceof Container)) + { + component = component.getParent(); + continue; + } + if (component.getPeer() instanceof LightweightPeer) + { + component = component.getParent(); + continue; + } + return (Container) component; + } + } + + /** + * Creates a new custom cursor object. + * + * @exception IndexOutOfBoundsException If the hotSpot values are outside + * the bounds of the cursor. + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Cursor createCustomCursor(Image cursor, Point hotSpot, String name) + { + // Presumably the only reason this isn't abstract is for backwards + // compatibility? FIXME? + return null; + } + + /** + * Returns the supported cursor dimension which is closest to the + * desired sizes. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) + { + return new Dimension (0,0); + } + + /** + * Returns the maximum number of colors the Toolkit supports in a custom + * cursor palette. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public int getMaximumCursorColors() + { + return 0; + } + + /** + * Returns whether Toolkit supports this state for Frames. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @since 1.4 + */ + public boolean isFrameStateSupported(int state) + { + return false; + } + + /** + * Returns the value of the property with the specified name, or the + * default value if the property does not exist. + * + * @param key The name of the property to retrieve. + * @param def The default value of the property. + */ + public static String getProperty(String key, String def) + { + return props.getProperty(key, def); + } + + + /** + * Returns the event queue that is suitable for the calling context. + * + * <p>Despite the word “System” in the name of this + * method, a toolkit may provide different event queues for each + * applet. There is no guarantee that the same queue is shared + * system-wide. + * + * <p>The implementation first checks whether a + * SecurityManager has been installed. If so, its {@link + * java.lang.SecurityManager#checkAwtEventQueueAccess()} method gets + * called. The security manager will throw a SecurityException if it + * does not grant the permission to access the event queue. + * + * <p>Next, the call is delegated to {@link + * #getSystemEventQueueImpl()}. + * + * @return The event queue for this applet (or application). + * + * @throws SecurityException if a security manager has been + * installed, and it does not grant the permission to access the + * event queue. + */ + public final EventQueue getSystemEventQueue() + { + SecurityManager sm; + + sm = System.getSecurityManager(); + if (sm != null) + sm.checkAwtEventQueueAccess(); + + return getSystemEventQueueImpl(); + } + + + /** + * Returns the event queue that is suitable for the calling context. + * + * <p>Despite the word “System” in the name of this + * method, a toolkit may provide different event queues for each + * applet. There is no guarantee that the same queue is shared + * system-wide. + * + * <p>No security checks are performed, which is why this method + * may only be called by Toolkits. + * + * @see #getSystemEventQueue() + */ + protected abstract EventQueue getSystemEventQueueImpl(); + + + /** + * @since 1.3 + */ + public abstract DragSourceContextPeer + createDragSourceContextPeer(DragGestureEvent e); + + /** + * @since 1.3 + */ + public DragGestureRecognizer + createDragGestureRecognizer(Class recognizer, DragSource ds, + Component comp, int actions, + DragGestureListener l) + { + return null; + } + + public final Object getDesktopProperty(String propertyName) + { + return desktopProperties.get(propertyName); + } + + protected final void setDesktopProperty(String name, Object newValue) + { + Object oldValue = getDesktopProperty(name); + desktopProperties.put(name, newValue); + desktopPropsSupport.firePropertyChange(name, oldValue, newValue); + } + + protected Object lazilyLoadDesktopProperty(String name) + { + // FIXME - what is this?? + return null; + } + + protected void initializeDesktopProperties() + { + // Overridden by toolkit implementation? + } + + public void addPropertyChangeListener(String name, + PropertyChangeListener pcl) + { + desktopPropsSupport.addPropertyChangeListener(name, pcl); + } + + public void removePropertyChangeListener(String name, + PropertyChangeListener pcl) + { + desktopPropsSupport.removePropertyChangeListener(name, pcl); + } + + /** + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return desktopPropsSupport.getPropertyChangeListeners(); + } + + /** + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners(String name) + { + return desktopPropsSupport.getPropertyChangeListeners(name); + } + + public void addAWTEventListener(AWTEventListener listener, long eventMask) + { + // SecurityManager s = System.getSecurityManager(); + // if (s != null) + // s.checkPermission(AWTPermission("listenToAllAWTEvents")); + // FIXME + } + + public void removeAWTEventListener(AWTEventListener listener) + { + // FIXME + } + + /** + * @since 1.4 + */ + public AWTEventListener[] getAWTEventListeners() + { + return null; + } + + /** + * @since 1.4 + */ + public AWTEventListener[] getAWTEventListeners(long mask) + { + return null; + } + + /** + * @since 1.3 + */ + public abstract Map mapInputMethodHighlight(InputMethodHighlight highlight); +} // class Toolkit diff --git a/libjava/classpath/java/awt/Transparency.java b/libjava/classpath/java/awt/Transparency.java new file mode 100644 index 00000000000..88858717693 --- /dev/null +++ b/libjava/classpath/java/awt/Transparency.java @@ -0,0 +1,67 @@ +/* Transparency.java -- common transparency modes in graphics + Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * A common transparency mode for layering graphics. + * + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Transparency +{ + /** Image data which is completely opaque, for an alpha value of 1.0. */ + int OPAQUE = 1; + + /** + * Image data which is either completely opaque or transparent, for an + * exact integer alpha value. + */ + int BITMASK = 2; + + /** Image data which is translucent, for a non-integer alpha value. */ + int TRANSLUCENT = 3; + + /** + * Return the transparency type. + * + * @return One of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT}. + */ + int getTransparency(); +} // interface Transparency diff --git a/libjava/classpath/java/awt/Window.java b/libjava/classpath/java/awt/Window.java new file mode 100644 index 00000000000..e26c4e32fe7 --- /dev/null +++ b/libjava/classpath/java/awt/Window.java @@ -0,0 +1,1125 @@ +/* Window.java -- + Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; +import java.awt.event.WindowStateListener; +import java.awt.image.BufferStrategy; +import java.awt.peer.WindowPeer; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.EventListener; +import java.util.Iterator; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * This class represents a top-level window with no decorations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class Window extends Container implements Accessible +{ + private static final long serialVersionUID = 4497834738069338734L; + + // Serialized fields, from Sun's serialization spec. + private String warningString = null; + private int windowSerializedDataVersion = 0; // FIXME + /** @since 1.2 */ + // private FocusManager focusMgr; // FIXME: what is this? + /** @since 1.2 */ + private int state = 0; + /** @since 1.4 */ + private boolean focusableWindowState = true; + + // A list of other top-level windows owned by this window. + private transient Vector ownedWindows = new Vector(); + + private transient WindowListener windowListener; + private transient WindowFocusListener windowFocusListener; + private transient WindowStateListener windowStateListener; + private transient GraphicsConfiguration graphicsConfiguration; + + private transient boolean shown; + + // This is package-private to avoid an accessor method. + transient Component windowFocusOwner; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_window_number; + + protected class AccessibleAWTWindow extends AccessibleAWTContainer + { + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.WINDOW; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isActive()) + states.add(AccessibleState.ACTIVE); + return states; + } + } + + /** + * This (package access) constructor is used by subclasses that want + * to build windows that do not have parents. Eg. toplevel + * application frames. Subclasses cannot call super(null), since + * null is an illegal argument. + */ + Window() + { + visible = false; + // Windows are the only Containers that default to being focus + // cycle roots. + focusCycleRoot = true; + setLayout(new BorderLayout()); + + addWindowFocusListener (new WindowAdapter () + { + public void windowGainedFocus (WindowEvent event) + { + if (windowFocusOwner != null) + { + // FIXME: move this section and the other similar + // sections in Component into a separate method. + EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + synchronized (eq) + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); + if (currentFocusOwner != null) + { + eq.postEvent (new FocusEvent (currentFocusOwner, FocusEvent.FOCUS_LOST, + false, windowFocusOwner)); + eq.postEvent (new FocusEvent (windowFocusOwner, FocusEvent.FOCUS_GAINED, + false, currentFocusOwner)); + } + else + eq.postEvent (new FocusEvent (windowFocusOwner, FocusEvent.FOCUS_GAINED, false)); + } + } + } + }); + } + + Window(GraphicsConfiguration gc) + { + this(); + graphicsConfiguration = gc; + } + + /** + * Initializes a new instance of <code>Window</code> with the specified + * parent. The window will initially be invisible. + * + * @param owner The owning <code>Frame</code> of this window. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null; this exception is always + * thrown when GraphicsEnvironment.isHeadless returns true. + */ + public Window(Frame owner) + { + this (owner, owner.getGraphicsConfiguration ()); + } + + /** + * Initializes a new instance of <code>Window</code> with the specified + * parent. The window will initially be invisible. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null; this exception is always + * thrown when GraphicsEnvironment.isHeadless returns true. + * + * @since 1.2 + */ + public Window(Window owner) + { + this (owner, owner.getGraphicsConfiguration ()); + } + + /** + * Initializes a new instance of <code>Window</code> with the specified + * parent. The window will initially be invisible. + * + * @exception IllegalArgumentException If owner is null or if gc is not from a + * screen device; this exception is always thrown when + * GraphicsEnvironment.isHeadless returns true. + * + * @since 1.3 + */ + public Window(Window owner, GraphicsConfiguration gc) + { + this (); + + synchronized (getTreeLock()) + { + if (owner == null) + throw new IllegalArgumentException ("owner must not be null"); + + parent = owner; + owner.ownedWindows.add(new WeakReference(this)); + } + + // FIXME: make this text visible in the window. + SecurityManager s = System.getSecurityManager(); + if (s != null && ! s.checkTopLevelWindow(this)) + warningString = System.getProperty("awt.appletWarning"); + + if (gc != null + && gc.getDevice().getType() != GraphicsDevice.TYPE_RASTER_SCREEN) + throw new IllegalArgumentException ("gc must be from a screen device"); + + if (gc == null) + graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration(); + else + graphicsConfiguration = gc; + } + + GraphicsConfiguration getGraphicsConfigurationImpl() + { + if (graphicsConfiguration != null) + return graphicsConfiguration; + + return super.getGraphicsConfigurationImpl(); + } + + /** + * Creates the native peer for this window. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createWindow(this); + super.addNotify(); + } + + /** + * Relays out this window's child components at their preferred size. + * + * @specnote pack() doesn't appear to be called internally by show(), so + * we duplicate some of the functionality. + */ + public void pack() + { + if (parent != null && !parent.isDisplayable()) + parent.addNotify(); + if (peer == null) + addNotify(); + + setSize(getPreferredSize()); + + validate(); + } + + /** + * Shows on-screen this window and any of its owned windows for whom + * isVisible returns true. + */ + public void show() + { + if (parent != null && !parent.isDisplayable()) + parent.addNotify(); + if (peer == null) + addNotify(); + + // Show visible owned windows. + synchronized (getTreeLock()) + { + Iterator e = ownedWindows.iterator(); + while(e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + { + if (w.isVisible()) + w.getPeer().setVisible(true); + } + else + // Remove null weak reference from ownedWindows. + // Unfortunately this can't be done in the Window's + // finalize method because there is no way to guarantee + // synchronous access to ownedWindows there. + e.remove(); + } + } + validate(); + super.show(); + toFront(); + + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + manager.setGlobalFocusedWindow (this); + + if (!shown) + { + FocusTraversalPolicy policy = getFocusTraversalPolicy (); + Component initialFocusOwner = null; + + if (policy != null) + initialFocusOwner = policy.getInitialComponent (this); + + if (initialFocusOwner != null) + initialFocusOwner.requestFocusInWindow (); + + shown = true; + } + } + + public void hide() + { + // Hide visible owned windows. + synchronized (getTreeLock ()) + { + Iterator e = ownedWindows.iterator(); + while(e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + { + if (w.isVisible() && w.getPeer() != null) + w.getPeer().setVisible(false); + } + else + e.remove(); + } + } + super.hide(); + } + + public boolean isDisplayable() + { + if (super.isDisplayable()) + return true; + return peer != null; + } + + /** + * Destroys any resources associated with this window. This includes + * all components in the window and all owned top-level windows. + */ + public void dispose() + { + hide(); + + synchronized (getTreeLock ()) + { + Iterator e = ownedWindows.iterator(); + while(e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + w.dispose(); + else + // Remove null weak reference from ownedWindows. + e.remove(); + } + + for (int i = 0; i < ncomponents; ++i) + component[i].removeNotify(); + this.removeNotify(); + + // Post a WINDOW_CLOSED event. + WindowEvent we = new WindowEvent(this, WindowEvent.WINDOW_CLOSED); + getToolkit().getSystemEventQueue().postEvent(we); + } + } + + /** + * Sends this window to the back so that all other windows display in + * front of it. + */ + public void toBack() + { + if (peer != null) + { + WindowPeer wp = (WindowPeer) peer; + wp.toBack(); + } + } + + /** + * Brings this window to the front so that it displays in front of + * any other windows. + */ + public void toFront() + { + if (peer != null) + { + WindowPeer wp = (WindowPeer) peer; + wp.toFront(); + } + } + + /** + * Returns the toolkit used to create this window. + * + * @return The toolkit used to create this window. + * + * @specnote Unlike Component.getToolkit, this implementation always + * returns the value of Toolkit.getDefaultToolkit(). + */ + public Toolkit getToolkit() + { + return Toolkit.getDefaultToolkit(); + } + + /** + * Returns the warning string that will be displayed if this window is + * popped up by an unsecure applet or application. + * + * @return The unsecure window warning message. + */ + public final String getWarningString() + { + return warningString; + } + + /** + * Returns the locale that this window is configured for. + * + * @return The locale this window is configured for. + */ + public Locale getLocale() + { + return locale == null ? Locale.getDefault() : locale; + } + + /* + /** @since 1.2 + public InputContext getInputContext() + { + // FIXME + } + */ + + /** + * Sets the cursor for this window to the specifiec cursor. + * + * @param cursor The new cursor for this window. + */ + public void setCursor(Cursor cursor) + { + super.setCursor(cursor); + } + + public Window getOwner() + { + return (Window) parent; + } + + /** @since 1.2 */ + public Window[] getOwnedWindows() + { + Window [] trimmedList; + synchronized (getTreeLock ()) + { + // Windows with non-null weak references in ownedWindows. + Window [] validList = new Window [ownedWindows.size()]; + + Iterator e = ownedWindows.iterator(); + int numValid = 0; + while (e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + validList[numValid++] = w; + else + // Remove null weak reference from ownedWindows. + e.remove(); + } + + if (numValid != validList.length) + { + trimmedList = new Window [numValid]; + System.arraycopy (validList, 0, trimmedList, 0, numValid); + } + else + trimmedList = validList; + } + return trimmedList; + } + + /** + * Adds the specified listener to the list of <code>WindowListeners</code> + * that will receive events for this window. + * + * @param listener The <code>WindowListener</code> to add. + */ + public synchronized void addWindowListener(WindowListener listener) + { + windowListener = AWTEventMulticaster.add(windowListener, listener); + } + + /** + * Removes the specified listener from the list of + * <code>WindowListeners</code> that will receive events for this window. + * + * @param listener The <code>WindowListener</code> to remove. + */ + public synchronized void removeWindowListener(WindowListener listener) + { + windowListener = AWTEventMulticaster.remove(windowListener, listener); + } + + /** + * Returns an array of all the window listeners registered on this window. + * + * @since 1.4 + */ + public synchronized WindowListener[] getWindowListeners() + { + return (WindowListener[]) + AWTEventMulticaster.getListeners(windowListener, + WindowListener.class); + } + + /** + * Returns an array of all the window focus listeners registered on this + * window. + * + * @since 1.4 + */ + public synchronized WindowFocusListener[] getWindowFocusListeners() + { + return (WindowFocusListener[]) + AWTEventMulticaster.getListeners(windowFocusListener, + WindowFocusListener.class); + } + + /** + * Returns an array of all the window state listeners registered on this + * window. + * + * @since 1.4 + */ + public synchronized WindowStateListener[] getWindowStateListeners() + { + return (WindowStateListener[]) + AWTEventMulticaster.getListeners(windowStateListener, + WindowStateListener.class); + } + + /** + * Adds the specified listener to this window. + */ + public void addWindowFocusListener (WindowFocusListener wfl) + { + windowFocusListener = AWTEventMulticaster.add (windowFocusListener, wfl); + } + + /** + * Adds the specified listener to this window. + * + * @since 1.4 + */ + public void addWindowStateListener (WindowStateListener wsl) + { + windowStateListener = AWTEventMulticaster.add (windowStateListener, wsl); + } + + /** + * Removes the specified listener from this window. + */ + public void removeWindowFocusListener (WindowFocusListener wfl) + { + windowFocusListener = AWTEventMulticaster.remove (windowFocusListener, wfl); + } + + /** + * Removes the specified listener from this window. + * + * @since 1.4 + */ + public void removeWindowStateListener (WindowStateListener wsl) + { + windowStateListener = AWTEventMulticaster.remove (windowStateListener, wsl); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Window. FooListeners are registered using the addFooListener + * method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public EventListener[] getListeners(Class listenerType) + { + if (listenerType == WindowListener.class) + return getWindowListeners(); + return super.getListeners(listenerType); + } + + void dispatchEventImpl(AWTEvent e) + { + // Make use of event id's in order to avoid multiple instanceof tests. + if (e.id <= WindowEvent.WINDOW_LAST + && e.id >= WindowEvent.WINDOW_FIRST + && (windowListener != null + || windowFocusListener != null + || windowStateListener != null + || (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + /** + * Processes the specified event for this window. If the event is an + * instance of <code>WindowEvent</code>, then + * <code>processWindowEvent()</code> is called to process the event, + * otherwise the superclass version of this method is invoked. + * + * @param evt The event to process. + */ + protected void processEvent(AWTEvent evt) + { + if (evt instanceof WindowEvent) + processWindowEvent((WindowEvent) evt); + else + super.processEvent(evt); + } + + /** + * Dispatches this event to any listeners that are listening for + * <code>WindowEvents</code> on this window. This method only gets + * invoked if it is enabled via <code>enableEvents()</code> or if + * a listener has been added. + * + * @param evt The event to process. + */ + protected void processWindowEvent(WindowEvent evt) + { + int id = evt.getID(); + + if (id == WindowEvent.WINDOW_GAINED_FOCUS + || id == WindowEvent.WINDOW_LOST_FOCUS) + processWindowFocusEvent (evt); + else if (id == WindowEvent.WINDOW_STATE_CHANGED) + processWindowStateEvent (evt); + else + { + if (windowListener != null) + { + switch (evt.getID()) + { + case WindowEvent.WINDOW_ACTIVATED: + windowListener.windowActivated(evt); + break; + + case WindowEvent.WINDOW_CLOSED: + windowListener.windowClosed(evt); + break; + + case WindowEvent.WINDOW_CLOSING: + windowListener.windowClosing(evt); + break; + + case WindowEvent.WINDOW_DEACTIVATED: + windowListener.windowDeactivated(evt); + break; + + case WindowEvent.WINDOW_DEICONIFIED: + windowListener.windowDeiconified(evt); + break; + + case WindowEvent.WINDOW_ICONIFIED: + windowListener.windowIconified(evt); + break; + + case WindowEvent.WINDOW_OPENED: + windowListener.windowOpened(evt); + break; + + default: + break; + } + } + } + } + + /** + * Identifies if this window is active. The active window is a Frame or + * Dialog that has focus or owns the active window. + * + * @return true if active, else false. + * @since 1.4 + */ + public boolean isActive() + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + return manager.getActiveWindow() == this; + } + + /** + * Identifies if this window is focused. A window is focused if it is the + * focus owner or it contains the focus owner. + * + * @return true if focused, else false. + * @since 1.4 + */ + public boolean isFocused() + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + return manager.getFocusedWindow() == this; + } + + /** + * Returns the child window that has focus if this window is active. + * This method returns <code>null</code> if this window is not active + * or no children have focus. + * + * @return The component that has focus, or <code>null</code> if no + * component has focus. + */ + public Component getFocusOwner () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Window activeWindow = manager.getActiveWindow (); + + // The currently-focused Component belongs to the active Window. + if (activeWindow == this) + return manager.getFocusOwner (); + else + return windowFocusOwner; + } + + /** + * Set the focus owner for this window. This method is used to + * remember which component was focused when this window lost + * top-level focus, so that when it regains top-level focus the same + * child component can be refocused. + * + * @param windowFocusOwner the component in this window that owns + * the focus. + */ + void setFocusOwner (Component windowFocusOwner) + { + this.windowFocusOwner = windowFocusOwner; + } + + /** + * Post a Java 1.0 event to the event queue. + * + * @param e The event to post. + * + * @deprecated + */ + public boolean postEvent(Event e) + { + return handleEvent (e); + } + + /** + * Tests whether or not this window is visible on the screen. + * + * In contrast to the normal behaviour of Container, which is that + * a container is showing if its parent is visible and showing, a Window + * is even showing, if its parent (i.e. an invisible Frame) is not showing. + * + * @return <code>true</code> if this window is visible, <code>false</code> + * otherwise. + */ + public boolean isShowing() + { + return isVisible(); + } + + public void setLocationRelativeTo (Component c) + { + if (c == null || !c.isShowing ()) + { + int x = 0; + int y = 0; + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment (); + Point center = ge.getCenterPoint (); + x = center.x - (width / 2); + y = center.y - (height / 2); + setLocation (x, y); + } + // FIXME: handle case where component is non-null. + } + + /** + * A BltBufferStrategy for windows. + */ + private class WindowBltBufferStrategy extends BltBufferStrategy + { + /** + * Creates a block transfer strategy for this window. + * + * @param numBuffers the number of buffers in this strategy + * @param accelerated true if the buffer should be accelerated, + * false otherwise + */ + WindowBltBufferStrategy(int numBuffers, boolean accelerated) + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(accelerated), + new ImageCapabilities(accelerated), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * A FlipBufferStrategy for windows. + */ + private class WindowFlipBufferStrategy extends FlipBufferStrategy + { + /** + * Creates a flip buffer strategy for this window. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws AWTException if the requested number of buffers is not + * supported + */ + WindowFlipBufferStrategy(int numBuffers) + throws AWTException + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * Creates a buffering strategy that manages how this window is + * repainted. This method attempts to create the optimum strategy + * based on the desired number of buffers. Hardware or software + * acceleration may be used. + * + * createBufferStrategy attempts different levels of optimization, + * but guarantees that some strategy with the requested number of + * buffers will be created even if it is not optimal. First it + * attempts to create a page flipping strategy, then an accelerated + * blitting strategy, then an unaccelerated blitting strategy. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws IllegalArgumentException if requested number of buffers + * is less than one + * @throws IllegalStateException if this window is not displayable + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Window.createBufferStrategy: number" + + " of buffers is less than one"); + + if (!isDisplayable()) + throw new IllegalStateException("Window.createBufferStrategy: window is" + + " not displayable"); + + BufferStrategy newStrategy = null; + + // try a flipping strategy + try + { + newStrategy = new WindowFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + + // fall back to an accelerated blitting strategy + if (newStrategy == null) + newStrategy = new WindowBltBufferStrategy(numBuffers, true); + + bufferStrategy = newStrategy; + } + + /** + * Creates a buffering strategy that manages how this window is + * repainted. This method attempts to create a strategy based on + * the specified capabilities and throws an exception if the + * requested strategy is not supported. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * @param caps the requested buffering capabilities + * + * @throws AWTException if the requested capabilities are not + * supported + * @throws IllegalArgumentException if requested number of buffers + * is less than one or if caps is null + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers, + BufferCapabilities caps) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Window.createBufferStrategy: number" + + " of buffers is less than one"); + + if (caps == null) + throw new IllegalArgumentException("Window.createBufferStrategy:" + + " capabilities object is null"); + + // a flipping strategy was requested + if (caps.isPageFlipping()) + { + try + { + bufferStrategy = new WindowFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + } + else + bufferStrategy = new WindowBltBufferStrategy(numBuffers, true); + } + + /** + * Returns the buffer strategy used by the window. + * + * @return the buffer strategy. + * @since 1.4 + */ + public BufferStrategy getBufferStrategy() + { + return bufferStrategy; + } + + /** + * @since 1.2 + * + * @deprecated + */ + public void applyResourceBundle(ResourceBundle rb) + { + throw new Error ("Not implemented"); + } + + /** + * @since 1.2 + * + * @deprecated + */ + public void applyResourceBundle(String rbName) + { + ResourceBundle rb = ResourceBundle.getBundle(rbName, Locale.getDefault(), + ClassLoader.getSystemClassLoader()); + if (rb != null) + applyResourceBundle(rb); + } + + /** + * Gets the AccessibleContext associated with this <code>Window</code>. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTWindow(); + return accessibleContext; + } + + /** + * Get graphics configuration. The implementation for Window will + * not ask any parent containers, since Window is a toplevel + * window and not actually embedded in the parent component. + */ + public GraphicsConfiguration getGraphicsConfiguration() + { + if (graphicsConfiguration != null) return graphicsConfiguration; + if (peer != null) return peer.getGraphicsConfiguration(); + return null; + } + + protected void processWindowFocusEvent(WindowEvent event) + { + if (windowFocusListener != null) + { + switch (event.getID ()) + { + case WindowEvent.WINDOW_GAINED_FOCUS: + windowFocusListener.windowGainedFocus (event); + break; + + case WindowEvent.WINDOW_LOST_FOCUS: + windowFocusListener.windowLostFocus (event); + break; + + default: + break; + } + } + } + + /** + * @since 1.4 + */ + protected void processWindowStateEvent(WindowEvent event) + { + if (windowStateListener != null + && event.getID () == WindowEvent.WINDOW_STATE_CHANGED) + windowStateListener.windowStateChanged (event); + } + + /** + * Returns whether this <code>Window</code> can get the focus or not. + * + * @since 1.4 + */ + public final boolean isFocusableWindow () + { + if (getFocusableWindowState () == false) + return false; + + if (this instanceof Dialog + || this instanceof Frame) + return true; + + // FIXME: Implement more possible cases for returning true. + + return false; + } + + /** + * Returns the value of the focusableWindowState property. + * + * @since 1.4 + */ + public boolean getFocusableWindowState () + { + return focusableWindowState; + } + + /** + * Sets the value of the focusableWindowState property. + * + * @since 1.4 + */ + public void setFocusableWindowState (boolean focusableWindowState) + { + this.focusableWindowState = focusableWindowState; + } + + // setBoundsCallback is needed so that when a user moves a window, + // the Window's location can be updated without calling the peer's + // setBounds method. When a user moves a window the peer window's + // location is updated automatically and the windowing system sends + // a message back to the application informing it of its updated + // dimensions. We must update the AWT Window class with these new + // dimensions. But we don't want to call the peer's setBounds + // method, because the peer's dimensions have already been updated. + // (Under X, having this method prevents Configure event loops when + // moving windows: Component.setBounds -> peer.setBounds -> + // postConfigureEvent -> Component.setBounds -> ... In some cases + // Configure event loops cause windows to jitter back and forth + // continuously). + void setBoundsCallback (int x, int y, int w, int h) + { + if (this.x == x && this.y == y && width == w && height == h) + return; + invalidate(); + boolean resized = width != w || height != h; + boolean moved = this.x != x || this.y != y; + this.x = x; + this.y = y; + width = w; + height = h; + if (resized && isShowing ()) + { + ComponentEvent ce = + new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + if (moved && isShowing ()) + { + ComponentEvent ce = + new ComponentEvent(this, ComponentEvent.COMPONENT_MOVED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + /** + * Generate a unique name for this window. + * + * @return A unique name for this window. + */ + String generateName() + { + return "win" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_window_number++; + } +} diff --git a/libjava/classpath/java/awt/color/CMMException.java b/libjava/classpath/java/awt/color/CMMException.java new file mode 100644 index 00000000000..ab328ec8492 --- /dev/null +++ b/libjava/classpath/java/awt/color/CMMException.java @@ -0,0 +1,63 @@ +/* CMMException.java -- error in the native CMM + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * Thrown when there is an error in the native CMM. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class CMMException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 5775558044142994965L; + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public CMMException(String message) + { + super(message); + } +} // class CMMException diff --git a/libjava/classpath/java/awt/color/ColorSpace.java b/libjava/classpath/java/awt/color/ColorSpace.java new file mode 100644 index 00000000000..79369da710f --- /dev/null +++ b/libjava/classpath/java/awt/color/ColorSpace.java @@ -0,0 +1,183 @@ +/* ColorSpace.java -- transforms between color spaces + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import java.io.Serializable; + +/** + * NEEDS DOCUMENTATION + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public abstract class ColorSpace implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -409452704308689724L; + + public static final int TYPE_XYZ = 0; + public static final int TYPE_Lab = 1; + public static final int TYPE_Luv = 2; + public static final int TYPE_YCbCr = 3; + public static final int TYPE_Yxy = 4; + public static final int TYPE_RGB = 5; + public static final int TYPE_GRAY = 6; + public static final int TYPE_HSV = 7; + public static final int TYPE_HLS = 8; + public static final int TYPE_CMYK = 9; + // mysterious gap in the enumeration sequenece + public static final int TYPE_CMY = 11; + public static final int TYPE_2CLR = 12; + public static final int TYPE_3CLR = 13; + public static final int TYPE_4CLR = 14; + public static final int TYPE_5CLR = 15; + public static final int TYPE_6CLR = 16; + public static final int TYPE_7CLR = 17; + public static final int TYPE_8CLR = 18; + public static final int TYPE_9CLR = 19; + public static final int TYPE_ACLR = 20; + public static final int TYPE_BCLR = 21; + public static final int TYPE_CCLR = 22; + public static final int TYPE_DCLR = 23; + public static final int TYPE_ECLR = 24; + public static final int TYPE_FCLR = 25; + + public static final int CS_sRGB = 1000; + public static final int CS_LINEAR_RGB = 1004; + public static final int CS_CIEXYZ = 1001; + public static final int CS_PYCC = 1002; + public static final int CS_GRAY = 1003; + + private static final int CS_BASE = CS_sRGB; + private static final int CS_END = CS_LINEAR_RGB + 1; + private static final int CS_COUNT = CS_END - CS_BASE; + + // Instances are lazily instantiated + private static final ColorSpace[] INSTANCES = new ColorSpace[CS_COUNT]; + + /** + * @serial + */ + // Visible in subclass. + final int type; + + /** + * @serial + */ + // Visible in subclass. + final int numComponents; + + protected ColorSpace(int type, int numcomponents) + { + this.type = type; + numComponents = numcomponents; + } + + public static ColorSpace getInstance(int colorspace) + { + if ((colorspace >= CS_BASE) && (colorspace < CS_END)) + { + int instanceIndex = colorspace - CS_BASE; + if (INSTANCES[instanceIndex] == null) + { + ICC_Profile profile = new ICC_Profile(colorspace); + INSTANCES[instanceIndex] = new ICC_ColorSpace(profile); + } + return INSTANCES[instanceIndex]; + } + throw new IllegalArgumentException("unknown/unsupported colorspace"); + } + + public boolean isCS_sRGB() + { + return type == CS_sRGB; + } + + /** + * Transforms a color value assumed to be in this ColorSpace into a value in + * the default CS_sRGB color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at least + * the number of components in this ColorSpace. + */ + public abstract float[] toRGB(float[] colorvalue); + + public abstract float[] fromRGB(float[] rgbvalue); + + public abstract float[] toCIEXYZ(float[] colorvalue); + + public abstract float[] fromCIEXYZ(float[] colorvalue); + + public int getType() + { + return type; + } + + public int getNumComponents() + { + return numComponents; + } + + public String getName(int idx) + { + return "type " + type; + } + + /** + * @since 1.4 + */ + public float getMinValue(int idx) + { + if (idx < 0 || idx >= numComponents) + throw new IllegalArgumentException(); + return 0; + } + + /** + * @since 1.4 + */ + public float getMaxValue(int idx) + { + if (idx < 0 || idx >= numComponents) + throw new IllegalArgumentException(); + return 1; + } +} // class ColorSpace diff --git a/libjava/classpath/java/awt/color/ICC_ColorSpace.java b/libjava/classpath/java/awt/color/ICC_ColorSpace.java new file mode 100644 index 00000000000..b50048cf94b --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ColorSpace.java @@ -0,0 +1,314 @@ +/* ICC_ColorSpace.java -- the canonical color space implementation + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import gnu.java.awt.color.CieXyzConverter; +import gnu.java.awt.color.ClutProfileConverter; +import gnu.java.awt.color.ColorSpaceConverter; +import gnu.java.awt.color.GrayProfileConverter; +import gnu.java.awt.color.GrayScaleConverter; +import gnu.java.awt.color.LinearRGBConverter; +import gnu.java.awt.color.PyccConverter; +import gnu.java.awt.color.RgbProfileConverter; +import gnu.java.awt.color.SrgbConverter; + +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * ICC_ColorSpace - an implementation of ColorSpace + * + * While an ICC_Profile class abstracts the data in an ICC profile file + * an ICC_ColorSpace performs the color space conversions defined by + * an ICC_Profile instance. + * + * Typically, an ICC_Profile will either be created using getInstance, + * either from the built-in colorspaces, or from an ICC profile file. + * Then a ICC_Colorspace will be used to perform transforms from the + * device colorspace to and from the profile color space. + * + * The PCS used by ColorSpace is CIE XYZ relative a D50 white point. + * (Profiles using a CIE Lab PCS will have their input and output converted + * to D50 CIE XYZ accordingly. + * + * Note that a valid profile may not contain transforms in both directions, + * in which case the output may be undefined. + * All built-in colorspaces have bidirectional transforms, but developers + * using an ICC profile file may want to check the profile class using + * the ICC_Profile.getProfileClass() method. Input class profiles are + * guaranteed to have transforms to the PCS, output class profiles are + * guaranteed to have transforms from the PCS to device space. + * + * @author Sven de Marothy + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public class ICC_ColorSpace extends ColorSpace +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3455889114070431483L; + + /** + * @serial + */ + private ICC_Profile thisProfile; + + /** + * @serial + */ + private float[] minVal; + + /** + * @serial + */ + private float[] maxVal; + + /** + * @serial + */ + private float[] diffMinMax; + + /** + * @serial + */ + private float[] invDiffMinMax; + + /** + * @serial + */ + private boolean needScaleInit; + + /** + * Tells us if the PCS is CIE LAB (must be CIEXYZ otherwise) + */ + private transient int type; + private transient int nComponents; + private transient ColorSpaceConverter converter; + + /** + * Constructs a new ICC_ColorSpace from an ICC_Profile object. + * + * @exception IllegalArgumentException If profile is inappropriate for + * representing a ColorSpace. + */ + public ICC_ColorSpace(ICC_Profile profile) + { + super(profile.getColorSpaceType(), profile.getNumComponents()); + + converter = getConverter(profile); + thisProfile = profile; + nComponents = profile.getNumComponents(); + type = profile.getColorSpaceType(); + makeArrays(); + } + + /** + * Return the profile + */ + public ICC_Profile getProfile() + { + return thisProfile; + } + + /** + * Transforms a color value assumed to be in this ColorSpace into a value in + * the default CS_sRGB color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at least + * the number of components in this ColorSpace. + */ + public float[] toRGB(float[] colorvalue) + { + return converter.toRGB(colorvalue); + } + + /** + * Transforms a color value assumed to be in the default CS_sRGB color space + * into this ColorSpace. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least 3. + */ + public float[] fromRGB(float[] rgbvalue) + { + return converter.fromRGB(rgbvalue); + } + + /** + * Transforms a color value assumed to be in this ColorSpace into the + * CS_CIEXYZ conversion color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least the number of components in this ColorSpace. + */ + public float[] toCIEXYZ(float[] colorvalue) + { + return converter.toCIEXYZ(colorvalue); + } + + /** + * Transforms a color value assumed to be in the CS_CIEXYZ conversion color + * space into this ColorSpace. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least 3. + */ + public float[] fromCIEXYZ(float[] colorvalue) + { + return converter.fromCIEXYZ(colorvalue); + } + + public boolean isCS_sRGB() + { + return converter instanceof SrgbConverter; + } + + /** + * Returns the minimum normalized color component value for the specified + * component. + * + * @exception IllegalArgumentException If component is less than 0 or greater + * than numComponents - 1. + * + * @since 1.4 + */ + public float getMinValue(int idx) + { + // FIXME: Not 100% certain of this. + if (type == ColorSpace.TYPE_Lab && (idx == 1 || idx == 2)) + return -128f; + + if (idx < 0 || idx >= nComponents) + throw new IllegalArgumentException(); + return 0; + } + + /** + * Returns the maximum normalized color component value for the specified + * component. + * + * @exception IllegalArgumentException If component is less than 0 or greater + * than numComponents - 1. + * + * @since 1.4 + */ + public float getMaxValue(int idx) + { + if (type == ColorSpace.TYPE_XYZ && idx >= 0 && idx <= 2) + return 1 + 32767 / 32768f; + else if (type == ColorSpace.TYPE_Lab) + { + if (idx == 0) + return 100; + if (idx == 1 || idx == 2) + return 127; + } + if (idx < 0 || idx >= nComponents) + throw new IllegalArgumentException(); + return 1; + } + + /** + * Returns a colorspace converter suitable for a given profile + */ + private ColorSpaceConverter getConverter(ICC_Profile profile) + { + ColorSpaceConverter converter; + switch (profile.isPredefined()) + { + case CS_sRGB: + converter = new SrgbConverter(); + break; + case CS_CIEXYZ: + converter = new CieXyzConverter(); + break; + case CS_GRAY: + converter = new GrayScaleConverter(); + break; + case CS_LINEAR_RGB: + converter = new LinearRGBConverter(); + break; + case CS_PYCC: + converter = new PyccConverter(); + break; + default: + if (profile instanceof ICC_ProfileRGB) + converter = new RgbProfileConverter((ICC_ProfileRGB) profile); + else if (profile instanceof ICC_ProfileGray) + converter = new GrayProfileConverter((ICC_ProfileGray) profile); + else + converter = new ClutProfileConverter(profile); + break; + } + return converter; + } + + /** + * Serialization compatibility requires these variable to be set, + * although we don't use them. Perhaps we should? + */ + private void makeArrays() + { + minVal = new float[nComponents]; + maxVal = new float[nComponents]; + + invDiffMinMax = diffMinMax = null; + for (int i = 0; i < nComponents; i++) + { + minVal[i] = getMinValue(i); + maxVal[i] = getMaxValue(i); + } + needScaleInit = true; + } + + /** + * Deserializes the object + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // set up objects + converter = getConverter(thisProfile); + nComponents = thisProfile.getNumComponents(); + type = thisProfile.getColorSpaceType(); + } +} // class ICC_ColorSpace diff --git a/libjava/classpath/java/awt/color/ICC_Profile.java b/libjava/classpath/java/awt/color/ICC_Profile.java new file mode 100644 index 00000000000..75f55a1dacb --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_Profile.java @@ -0,0 +1,1244 @@ +/* ICC_Profile.java -- color space profiling + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import gnu.java.awt.color.ProfileHeader; +import gnu.java.awt.color.TagEntry; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * ICC Profile - represents an ICC Color profile. + * The ICC profile format is a standard file format which maps the transform + * from a device color space to a standard Profile Color Space (PCS), which + * can either be CIE L*a*b or CIE XYZ. + * (With the exception of device link profiles which map from one device space + * to another) + * + * ICC profiles calibrated to specific input/output devices are used when color + * fidelity is of importance. + * + * An instance of ICC_Profile can be created using the getInstance() methods, + * either using one of the predefined color spaces enumerated in ColorSpace, + * or from an ICC profile file, or from an input stream. + * + * An ICC_ColorSpace object can then be created to transform color values + * through the profile. + * + * The ICC_Profile class implements the version 2 format specified by + * International Color Consortium Specification ICC.1:1998-09, + * and its addendum ICC.1A:1999-04, April 1999 + * (available at www.color.org) + * + * @author Sven de Marothy + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public class ICC_Profile implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -3938515861990936766L; + + /** + * ICC Profile classes + */ + public static final int CLASS_INPUT = 0; + public static final int CLASS_DISPLAY = 1; + public static final int CLASS_OUTPUT = 2; + public static final int CLASS_DEVICELINK = 3; + public static final int CLASS_COLORSPACECONVERSION = 4; + public static final int CLASS_ABSTRACT = 5; + public static final int CLASS_NAMEDCOLOR = 6; + + /** + * ICC Profile class signatures + */ + public static final int icSigInputClass = 0x73636e72; // 'scnr' + public static final int icSigDisplayClass = 0x6d6e7472; // 'mntr' + public static final int icSigOutputClass = 0x70727472; // 'prtr' + public static final int icSigLinkClass = 0x6c696e6b; // 'link' + public static final int icSigColorSpaceClass = 0x73706163; // 'spac' + public static final int icSigAbstractClass = 0x61627374; // 'abst' + public static final int icSigNamedColorClass = 0x6e6d636c; // 'nmcl' + + /** + * Color space signatures + */ + public static final int icSigXYZData = 0x58595A20; // 'XYZ ' + public static final int icSigLabData = 0x4C616220; // 'Lab ' + public static final int icSigLuvData = 0x4C757620; // 'Luv ' + public static final int icSigYCbCrData = 0x59436272; // 'YCbr' + public static final int icSigYxyData = 0x59787920; // 'Yxy ' + public static final int icSigRgbData = 0x52474220; // 'RGB ' + public static final int icSigGrayData = 0x47524159; // 'GRAY' + public static final int icSigHsvData = 0x48535620; // 'HSV ' + public static final int icSigHlsData = 0x484C5320; // 'HLS ' + public static final int icSigCmykData = 0x434D594B; // 'CMYK' + public static final int icSigCmyData = 0x434D5920; // 'CMY ' + public static final int icSigSpace2CLR = 0x32434C52; // '2CLR' + public static final int icSigSpace3CLR = 0x33434C52; // '3CLR' + public static final int icSigSpace4CLR = 0x34434C52; // '4CLR' + public static final int icSigSpace5CLR = 0x35434C52; // '5CLR' + public static final int icSigSpace6CLR = 0x36434C52; // '6CLR' + public static final int icSigSpace7CLR = 0x37434C52; // '7CLR' + public static final int icSigSpace8CLR = 0x38434C52; // '8CLR' + public static final int icSigSpace9CLR = 0x39434C52; // '9CLR' + public static final int icSigSpaceACLR = 0x41434C52; // 'ACLR' + public static final int icSigSpaceBCLR = 0x42434C52; // 'BCLR' + public static final int icSigSpaceCCLR = 0x43434C52; // 'CCLR' + public static final int icSigSpaceDCLR = 0x44434C52; // 'DCLR' + public static final int icSigSpaceECLR = 0x45434C52; // 'ECLR' + public static final int icSigSpaceFCLR = 0x46434C52; // 'FCLR' + + /** + * Rendering intents + */ + public static final int icPerceptual = 0; + public static final int icRelativeColorimetric = 1; + public static final int icSaturation = 2; + public static final int icAbsoluteColorimetric = 3; + + /** + * Tag signatures + */ + public static final int icSigAToB0Tag = 0x41324230; // 'A2B0' + public static final int icSigAToB1Tag = 0x41324231; // 'A2B1' + public static final int icSigAToB2Tag = 0x41324232; // 'A2B2' + public static final int icSigBlueColorantTag = 0x6258595A; // 'bXYZ' + public static final int icSigBlueTRCTag = 0x62545243; // 'bTRC' + public static final int icSigBToA0Tag = 0x42324130; // 'B2A0' + public static final int icSigBToA1Tag = 0x42324131; // 'B2A1' + public static final int icSigBToA2Tag = 0x42324132; // 'B2A2' + public static final int icSigCalibrationDateTimeTag = 0x63616C74; // 'calt' + public static final int icSigCharTargetTag = 0x74617267; // 'targ' + public static final int icSigCopyrightTag = 0x63707274; // 'cprt' + public static final int icSigCrdInfoTag = 0x63726469; // 'crdi' + public static final int icSigDeviceMfgDescTag = 0x646D6E64; // 'dmnd' + public static final int icSigDeviceModelDescTag = 0x646D6464; // 'dmdd' + public static final int icSigDeviceSettingsTag = 0x64657673; // 'devs' + public static final int icSigGamutTag = 0x67616D74; // 'gamt' + public static final int icSigGrayTRCTag = 0x6b545243; // 'kTRC' + public static final int icSigGreenColorantTag = 0x6758595A; // 'gXYZ' + public static final int icSigGreenTRCTag = 0x67545243; // 'gTRC' + public static final int icSigLuminanceTag = 0x6C756d69; // 'lumi' + public static final int icSigMeasurementTag = 0x6D656173; // 'meas' + public static final int icSigMediaBlackPointTag = 0x626B7074; // 'bkpt' + public static final int icSigMediaWhitePointTag = 0x77747074; // 'wtpt' + public static final int icSigNamedColor2Tag = 0x6E636C32; // 'ncl2' + public static final int icSigOutputResponseTag = 0x72657370; // 'resp' + public static final int icSigPreview0Tag = 0x70726530; // 'pre0' + public static final int icSigPreview1Tag = 0x70726531; // 'pre1' + public static final int icSigPreview2Tag = 0x70726532; // 'pre2' + public static final int icSigProfileDescriptionTag = 0x64657363; // 'desc' + public static final int icSigProfileSequenceDescTag = 0x70736571; // 'pseq' + public static final int icSigPs2CRD0Tag = 0x70736430; // 'psd0' + public static final int icSigPs2CRD1Tag = 0x70736431; // 'psd1' + public static final int icSigPs2CRD2Tag = 0x70736432; // 'psd2' + public static final int icSigPs2CRD3Tag = 0x70736433; // 'psd3' + public static final int icSigPs2CSATag = 0x70733273; // 'ps2s' + public static final int icSigPs2RenderingIntentTag = 0x70733269; // 'ps2i' + public static final int icSigRedColorantTag = 0x7258595A; // 'rXYZ' + public static final int icSigRedTRCTag = 0x72545243; // 'rTRC' + public static final int icSigScreeningDescTag = 0x73637264; // 'scrd' + public static final int icSigScreeningTag = 0x7363726E; // 'scrn' + public static final int icSigTechnologyTag = 0x74656368; // 'tech' + public static final int icSigUcrBgTag = 0x62666420; // 'bfd ' + public static final int icSigViewingCondDescTag = 0x76756564; // 'vued' + public static final int icSigViewingConditionsTag = 0x76696577; // 'view' + public static final int icSigChromaticityTag = 0x6368726D; // 'chrm' + + /** + * Non-ICC tag 'head' for use in retrieving the header with getData() + */ + public static final int icSigHead = 0x68656164; + + /** + * Header offsets + */ + public static final int icHdrSize = 0; + public static final int icHdrCmmId = 4; + public static final int icHdrVersion = 8; + public static final int icHdrDeviceClass = 12; + public static final int icHdrColorSpace = 16; + public static final int icHdrPcs = 20; + public static final int icHdrDate = 24; + public static final int icHdrMagic = 36; + public static final int icHdrPlatform = 40; + public static final int icHdrFlags = 44; + public static final int icHdrManufacturer = 48; + public static final int icHdrModel = 52; + public static final int icHdrAttributes = 56; + public static final int icHdrRenderingIntent = 64; + public static final int icHdrIlluminant = 68; + public static final int icHdrCreator = 80; + + /** + * + */ + public static final int icTagType = 0; + public static final int icTagReserved = 4; + public static final int icCurveCount = 8; + public static final int icCurveData = 12; + public static final int icXYZNumberX = 8; + + /** + * offset of the Tag table + */ + private static final int tagTableOffset = 128; + + /** + * @serial + */ + private static final int iccProfileSerializedDataVersion = 1; + + /** + * Constants related to generating profiles for + * built-in colorspace profiles + */ + /** + * Copyright notice to stick into built-in-profile files. + */ + private static final String copyrightNotice = "Generated by GNU Classpath."; + + /** + * Resolution of the TRC to use for predefined profiles. + * 1024 should suffice. + */ + private static final int TRC_POINTS = 1024; + + /** + * CIE 1931 D50 white point (in Lab coordinates) + */ + private static final float[] D50 = { 0.96422f, 1.00f, 0.82521f }; + + /** + * Color space profile ID + * Set to the predefined profile class (e.g. CS_sRGB) if a predefined + * color space is used, set to -1 otherwise. + * (or if the profile has been modified) + */ + private transient int profileID; + + /** + * The profile header data + */ + private transient ProfileHeader header; + + /** + * A hashtable containing the profile tags as TagEntry objects + */ + private transient Hashtable tagTable; + + /** + * Contructor for predefined colorspaces + */ + ICC_Profile(int profileID) + { + header = null; + tagTable = null; + createProfile(profileID); + } + + /** + * Constructs an ICC_Profile from a header and a table of loaded tags. + */ + ICC_Profile(ProfileHeader h, Hashtable tags) throws IllegalArgumentException + { + header = h; + tagTable = tags; + profileID = -1; // Not a predefined color space + } + + /** + * Constructs an ICC_Profile from a byte array of data. + */ + ICC_Profile(byte[] data) throws IllegalArgumentException + { + // get header and verify it + header = new ProfileHeader(data); + header.verifyHeader(data.length); + tagTable = createTagTable(data); + profileID = -1; // Not a predefined color space + } + + /** + * Free up the used memory. + */ + protected void finalize() + { + } + + /** + * Returns an ICC_Profile instance from a byte array of profile data. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + * + * @param data - the profile data + * @return An ICC_Profile object + */ + public static ICC_Profile getInstance(byte[] data) + { + ProfileHeader header = new ProfileHeader(data); + + // verify it as a correct ICC header, including size + header.verifyHeader(data.length); + + Hashtable tags = createTagTable(data); + + if (isRGBProfile(header, tags)) + return new ICC_ProfileRGB(data); + if (isGrayProfile(header, tags)) + return new ICC_ProfileGray(data); + + return new ICC_Profile(header, tags); + } + + /** + * Returns an predefined ICC_Profile instance. + * + * This will construct an ICC_Profile instance from one of the predefined + * color spaces in the ColorSpace class. (e.g. CS_sRGB, CS_GRAY, etc) + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @return An ICC_Profile object + */ + public static ICC_Profile getInstance(int cspace) + { + if (cspace == ColorSpace.CS_sRGB || cspace == ColorSpace.CS_LINEAR_RGB) + return new ICC_ProfileRGB(cspace); + if (cspace == ColorSpace.CS_GRAY) + return new ICC_ProfileGray(cspace); + return new ICC_Profile(cspace); + } + + /** + * Returns an ICC_Profile instance from an ICC Profile file. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + * @throws IOException if the file could not be read. + * + * @param filename - the file name of the profile file. + * @return An ICC_Profile object + */ + public static ICC_Profile getInstance(String filename) + throws IOException + { + return getInstance(new FileInputStream(filename)); + } + + /** + * Returns an ICC_Profile instance from an InputStream. + * + * This method can be used for reading ICC profiles embedded in files + * which support this. (JPEG and SVG for instance). + * + * The stream is treated in the following way: The profile header + * (128 bytes) is read first, and the header is validated. If the profile + * header is valid, it will then attempt to read the rest of the profile + * from the stream. The stream is not closed after reading. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + * @throws IOException if the stream could not be read. + * + * @param in - the input stream to read the profile from. + * @return An ICC_Profile object + */ + public static ICC_Profile getInstance(InputStream in) + throws IOException + { + // read the header + byte[] headerData = new byte[ProfileHeader.HEADERSIZE]; + if (in.read(headerData) != ProfileHeader.HEADERSIZE) + throw new IllegalArgumentException("Invalid profile header"); + + ProfileHeader header = new ProfileHeader(headerData); + + // verify it as a correct ICC header, but do not verify the + // size as we are reading from a stream. + header.verifyHeader(-1); + + // get the size + byte[] data = new byte[header.getSize()]; + System.arraycopy(headerData, 0, data, 0, ProfileHeader.HEADERSIZE); + + // read the rest + if (in.read(data, ProfileHeader.HEADERSIZE, + header.getSize() - ProfileHeader.HEADERSIZE) != header.getSize() + - ProfileHeader.HEADERSIZE) + throw new IOException("Incorrect profile size"); + + return getInstance(data); + } + + /** + * Returns the major version number + */ + public int getMajorVersion() + { + return header.getMajorVersion(); + } + + /** + * Returns the minor version number. + * + * Only the least-significant byte contains data, in BCD form: + * the least-significant nibble is the BCD bug fix revision, + * the most-significant nibble is the BCD minor revision number. + * + * (E.g. For a v2.1.0 profile this will return <code>0x10</code>) + */ + public int getMinorVersion() + { + return header.getMinorVersion(); + } + + /** + * Returns the device class of this profile, + * + * (E.g. CLASS_INPUT for a scanner profile, + * CLASS_OUTPUT for a printer) + */ + public int getProfileClass() + { + return header.getProfileClass(); + } + + /** + * Returns the color space of this profile, in terms + * of the color space constants defined in ColorSpace. + * (For example, it may be a ColorSpace.TYPE_RGB) + */ + public int getColorSpaceType() + { + return header.getColorSpace(); + } + + /** + * Returns the color space of this profile's Profile Connection Space (OCS) + * + * In terms of the color space constants defined in ColorSpace. + * This may be TYPE_XYZ or TYPE_Lab + */ + public int getPCSType() + { + return header.getProfileColorSpace(); + } + + /** + * Writes the profile data to an ICC profile file. + * @param filename - The name of the file to write + * @throws IOException if the write failed. + */ + public void write(String filename) throws IOException + { + FileOutputStream out = new FileOutputStream(filename); + write(out); + out.flush(); + out.close(); + } + + /** + * Writes the profile data in ICC profile file-format to a stream. + * This is useful for embedding ICC profiles in file formats which + * support this (such as JPEG and SVG). + * + * The stream is not closed after writing. + * @param out - The outputstream to which the profile data should be written + * @throws IOException if the write failed. + */ + public void write(OutputStream out) throws IOException + { + out.write(getData()); + } + + /** + * Returns the data corresponding to this ICC_Profile as a byte array. + * + * @return The data in a byte array, + * where the first element corresponds to first byte of the profile file. + */ + public byte[] getData() + { + int size = getSize(); + byte[] data = new byte[size]; + + // Header + System.arraycopy(header.getData(size), 0, data, 0, ProfileHeader.HEADERSIZE); + // # of tags + byte[] tt = getTagTable(); + System.arraycopy(tt, 0, data, ProfileHeader.HEADERSIZE, tt.length); + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { + TagEntry tag = (TagEntry) e.nextElement(); + System.arraycopy(tag.getData(), 0, + data, tag.getOffset(), tag.getSize()); + } + return data; + } + + /** + * Returns the ICC profile tag data + * The non ICC-tag icSigHead is also permitted to request the header data. + * + * @param tagSignature The ICC signature of the requested tag + * @return A byte array containing the tag data + */ + public byte[] getData(int tagSignature) + { + if (tagSignature == icSigHead) + return header.getData(getSize()); + + TagEntry t = (TagEntry) tagTable.get(TagEntry.tagHashKey(tagSignature)); + if (t == null) + return null; + return t.getData(); + } + + /** + * Sets the ICC profile tag data. + * + * Note that an ICC profile can only contain one tag of each type, if + * a tag already exists with the given signature, it is replaced. + * + * @param tagSignature - The signature of the tag to set + * @param data - A byte array containing the tag data + */ + public void setData(int tagSignature, byte[] data) + { + profileID = -1; // Not a predefined color space if modified. + + if (tagSignature == icSigHead) + header = new ProfileHeader(data); + else + { + TagEntry t = new TagEntry(tagSignature, data); + tagTable.put(t.hashKey(), t); + } + } + + /** + * Get the number of components in the profile's device color space. + */ + public int getNumComponents() + { + int[] lookup = + { + ColorSpace.TYPE_RGB, 3, ColorSpace.TYPE_CMY, 3, + ColorSpace.TYPE_CMYK, 4, ColorSpace.TYPE_GRAY, 1, + ColorSpace.TYPE_YCbCr, 3, ColorSpace.TYPE_XYZ, 3, + ColorSpace.TYPE_Lab, 3, ColorSpace.TYPE_HSV, 3, + ColorSpace.TYPE_2CLR, 2, ColorSpace.TYPE_Luv, 3, + ColorSpace.TYPE_Yxy, 3, ColorSpace.TYPE_HLS, 3, + ColorSpace.TYPE_3CLR, 3, ColorSpace.TYPE_4CLR, 4, + ColorSpace.TYPE_5CLR, 5, ColorSpace.TYPE_6CLR, 6, + ColorSpace.TYPE_7CLR, 7, ColorSpace.TYPE_8CLR, 8, + ColorSpace.TYPE_9CLR, 9, ColorSpace.TYPE_ACLR, 10, + ColorSpace.TYPE_BCLR, 11, ColorSpace.TYPE_CCLR, 12, + ColorSpace.TYPE_DCLR, 13, ColorSpace.TYPE_ECLR, 14, + ColorSpace.TYPE_FCLR, 15 + }; + for (int i = 0; i < lookup.length; i += 2) + if (header.getColorSpace() == lookup[i]) + return lookup[i + 1]; + return 3; // should never happen. + } + + /** + * After deserializing we must determine if the class we want + * is really one of the more specialized ICC_ProfileRGB or + * ICC_ProfileGray classes. + */ + protected Object readResolve() throws ObjectStreamException + { + if (isRGBProfile(header, tagTable)) + return new ICC_ProfileRGB(getData()); + if (isGrayProfile(header, tagTable)) + return new ICC_ProfileGray(getData()); + return this; + } + + /** + * Deserializes an instance + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + String predef = (String) s.readObject(); + byte[] data = (byte[]) s.readObject(); + + if (data != null) + { + header = new ProfileHeader(data); + tagTable = createTagTable(data); + profileID = -1; // Not a predefined color space + } + + if (predef != null) + { + predef = predef.intern(); + if (predef.equals("CS_sRGB")) + createProfile(ColorSpace.CS_sRGB); + if (predef.equals("CS_LINEAR_RGB")) + createProfile(ColorSpace.CS_LINEAR_RGB); + if (predef.equals("CS_CIEXYZ")) + createProfile(ColorSpace.CS_CIEXYZ); + if (predef.equals("CS_GRAY")) + createProfile(ColorSpace.CS_GRAY); + if (predef.equals("CS_PYCC")) + createProfile(ColorSpace.CS_PYCC); + } + } + + /** + * Serializes an instance + * The format is a String and a byte array, + * The string is non-null if the instance is one of the built-in profiles. + * Otherwise the byte array is non-null and represents the profile data. + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (profileID == ColorSpace.CS_sRGB) + s.writeObject("CS_sRGB"); + else if (profileID == ColorSpace.CS_LINEAR_RGB) + s.writeObject("CS_LINEAR_RGB"); + else if (profileID == ColorSpace.CS_CIEXYZ) + s.writeObject("CS_CIEXYZ"); + else if (profileID == ColorSpace.CS_GRAY) + s.writeObject("CS_GRAY"); + else if (profileID == ColorSpace.CS_PYCC) + s.writeObject("CS_PYCC"); + else + { + s.writeObject(null); // null string + s.writeObject(getData()); // data + return; + } + s.writeObject(null); // null data + } + + /** + * Sorts a ICC profile byte array into TagEntry objects stored in + * a hash table. + */ + private static Hashtable createTagTable(byte[] data) + throws IllegalArgumentException + { + ByteBuffer buf = ByteBuffer.wrap(data); + int nTags = buf.getInt(tagTableOffset); + + Hashtable tagTable = new Hashtable(); + for (int i = 0; i < nTags; i++) + { + TagEntry te = new TagEntry(buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 4), + buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 8), + buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 12), + data); + + if (tagTable.put(te.hashKey(), te) != null) + throw new IllegalArgumentException("Duplicate tag in profile:" + te); + } + return tagTable; + } + + /** + * Returns the total size of the padded, stored data + * Note: Tags must be stored on 4-byte aligned offsets. + */ + private int getSize() + { + int totalSize = ProfileHeader.HEADERSIZE; // size of header + + int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; // size of tag table + if ((tagTableSize & 0x0003) != 0) + tagTableSize += 4 - (tagTableSize & 0x0003); // pad + totalSize += tagTableSize; + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { // tag data + int tagSize = ((TagEntry) e.nextElement()).getSize(); + if ((tagSize & 0x0003) != 0) + tagSize += 4 - (tagSize & 0x0003); // pad + totalSize += tagSize; + } + return totalSize; + } + + /** + * Generates the tag index table + */ + private byte[] getTagTable() + { + int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; + if ((tagTableSize & 0x0003) != 0) + tagTableSize += 4 - (tagTableSize & 0x0003); // pad + + int offset = 4; + int tagOffset = ProfileHeader.HEADERSIZE + tagTableSize; + ByteBuffer buf = ByteBuffer.allocate(tagTableSize); + buf.putInt(tagTable.size()); // number of tags + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { + TagEntry tag = (TagEntry) e.nextElement(); + buf.putInt(offset, tag.getSignature()); + buf.putInt(offset + 4, tagOffset); + buf.putInt(offset + 8, tag.getSize()); + tag.setOffset(tagOffset); + int tagSize = tag.getSize(); + if ((tagSize & 0x0003) != 0) + tagSize += 4 - (tagSize & 0x0003); // pad + tagOffset += tagSize; + offset += 12; + } + return buf.array(); + } + + /** + * Returns if the criteria for an ICC_ProfileRGB are met. + * This means: + * Color space is TYPE_RGB + * (r,g,b)ColorantTags included + * (r,g,b)TRCTags included + * mediaWhitePointTag included + */ + private static boolean isRGBProfile(ProfileHeader header, Hashtable tags) + { + if (header.getColorSpace() != ColorSpace.TYPE_RGB) + return false; + if (tags.get(TagEntry.tagHashKey(icSigRedColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGreenColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigBlueColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigRedTRCTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGreenTRCTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigBlueTRCTag)) == null) + return false; + return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null); + } + + /** + * Returns if the criteria for an ICC_ProfileGray are met. + * This means: + * Colorspace is TYPE_GRAY + * grayTRCTag included + * mediaWhitePointTag included + */ + private static boolean isGrayProfile(ProfileHeader header, Hashtable tags) + { + if (header.getColorSpace() != ColorSpace.TYPE_GRAY) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGrayTRCTag)) == null) + return false; + return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null); + } + + /** + * Returns curve data for a 'curv'-type tag + * If it's a gamma curve, a single entry will be returned with the + * gamma value (including 1.0 for linear response) + * Otherwise the TRC table is returned. + * + * (Package private - used by ICC_ProfileRGB and ICC_ProfileGray) + */ + short[] getCurve(int signature) + { + byte[] data = getData(signature); + short[] curve; + + // can't find tag? + if (data == null) + return null; + + // not an curve type tag? + ByteBuffer buf = ByteBuffer.wrap(data); + if (buf.getInt(0) != 0x63757276) // 'curv' type + return null; + int count = buf.getInt(8); + if (count == 0) + { + curve = new short[1]; + curve[0] = 0x0100; // 1.00 in u8fixed8 + return curve; + } + if (count == 1) + { + curve = new short[1]; + curve[0] = buf.getShort(12); // other u8fixed8 gamma + return curve; + } + curve = new short[count]; + for (int i = 0; i < count; i++) + curve[i] = buf.getShort(12 + i * 2); + return curve; + } + + /** + * Returns XYZ tristimulus values for an 'XYZ ' type tag + * @return the XYZ values, or null if the tag was not an 'XYZ ' type tag. + * + * (Package private - used by ICC_ProfileXYZ and ICC_ProfileGray) + */ + float[] getXYZData(int signature) + { + byte[] data = getData(signature); + + // can't find tag? + if (data == null) + return null; + + // not an XYZData type tag? + ByteBuffer buf = ByteBuffer.wrap(data); + if (buf.getInt(0) != icSigXYZData) // 'XYZ ' type + return null; + + float[] point = new float[3]; + + // get the X,Y,Z tristimulus values + point[0] = ((float) buf.getInt(8)) / 65536f; + point[1] = ((float) buf.getInt(12)) / 65536f; + point[2] = ((float) buf.getInt(16)) / 65536f; + return point; + } + + /** + * Returns the profile ID if it's a predefined profile + * Or -1 for a profile loaded from an ICC profile + * + * (Package private - used by ICC_ColorSpace) + */ + int isPredefined() + { + return profileID; + } + + /** + * Creates a tag of XYZ-value type. + */ + private byte[] makeXYZData(float[] values) + { + ByteBuffer buf = ByteBuffer.allocate(20); + buf.putInt(0, icSigXYZData); // 'XYZ ' + buf.putInt(4, 0); + buf.putInt(8, (int) (values[0] * 65536.0)); + buf.putInt(12, (int) (values[1] * 65536.0)); + buf.putInt(16, (int) (values[2] * 65536.0)); + return buf.array(); + } + + /** + * Creates a tag of text type + */ + private byte[] makeTextTag(String text) + { + int length = text.length(); + ByteBuffer buf = ByteBuffer.allocate(8 + length + 1); + byte[] data; + try + { + data = text.getBytes("US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + data = new byte[length]; // shouldn't happen + } + + buf.putInt(0, (int) 0x74657874); // 'text' + buf.putInt(4, 0); + for (int i = 0; i < length; i++) + buf.put(8 + i, data[i]); + buf.put(8 + length, (byte) 0); // null-terminate + return buf.array(); + } + + /** + * Creates a tag of textDescriptionType + */ + private byte[] makeDescTag(String text) + { + int length = text.length(); + ByteBuffer buf = ByteBuffer.allocate(90 + length + 1); + buf.putInt(0, (int) 0x64657363); // 'desc' + buf.putInt(4, 0); // reserved + buf.putInt(8, length + 1); // ASCII length, including null termination + byte[] data; + + try + { + data = text.getBytes("US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + data = new byte[length]; // shouldn't happen + } + + for (int i = 0; i < length; i++) + buf.put(12 + i, data[i]); + buf.put(12 + length, (byte) 0); // null-terminate + + for (int i = 0; i < 39; i++) + buf.putShort(13 + length + (i * 2), (short) 0); // 78 bytes we can ignore + + return buf.array(); + } + + /** + * Creates a tag of TRC type (linear curve) + */ + private byte[] makeTRC() + { + ByteBuffer buf = ByteBuffer.allocate(12); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, 0); + return buf.array(); + } + + /** + * Creates a tag of TRC type (single gamma value) + */ + private byte[] makeTRC(float gamma) + { + short gammaValue = (short) (gamma * 256f); + ByteBuffer buf = ByteBuffer.allocate(14); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, 1); + buf.putShort(12, gammaValue); // 1.00 in u8fixed8 + return buf.array(); + } + + /** + * Creates a tag of TRC type (TRC curve points) + */ + private byte[] makeTRC(float[] trc) + { + ByteBuffer buf = ByteBuffer.allocate(12 + 2 * trc.length); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, trc.length); // number of points + + // put the curve values + for (int i = 0; i < trc.length; i++) + buf.putShort(12 + i * 2, (short) (trc[i] * 65535f)); + + return buf.array(); + } + + /** + * Creates an identity color lookup table. + */ + private byte[] makeIdentityClut() + { + final int nIn = 3; + final int nOut = 3; + final int nInEntries = 256; + final int nOutEntries = 256; + final int gridpoints = 16; + + // gridpoints**nIn + final int clutSize = 2 * nOut * gridpoints * gridpoints * gridpoints; + final int totalSize = clutSize + 2 * nInEntries * nIn + + 2 * nOutEntries * nOut + 52; + + ByteBuffer buf = ByteBuffer.allocate(totalSize); + buf.putInt(0, 0x6D667432); // 'mft2' + buf.putInt(4, 0); // reserved + buf.put(8, (byte) nIn); // number input channels + buf.put(9, (byte) nOut); // number output channels + buf.put(10, (byte) gridpoints); // number gridpoints + buf.put(11, (byte) 0); // padding + + // identity matrix + buf.putInt(12, 65536); // = 1 in s15.16 fixed point + buf.putInt(16, 0); + buf.putInt(20, 0); + buf.putInt(24, 0); + buf.putInt(28, 65536); + buf.putInt(32, 0); + buf.putInt(36, 0); + buf.putInt(40, 0); + buf.putInt(44, 65536); + + buf.putShort(48, (short) nInEntries); // input table entries + buf.putShort(50, (short) nOutEntries); // output table entries + + // write the linear input channels, unsigned 16.16 fixed point, + // from 0.0 to FF.FF + for (int channel = 0; channel < 3; channel++) + for (int i = 0; i < nInEntries; i++) + { + short n = (short) ((i << 8) | i); // assumes 256 entries + buf.putShort(52 + (channel * nInEntries + i) * 2, n); + } + int clutOffset = 52 + nInEntries * nIn * 2; + + for (int x = 0; x < gridpoints; x++) + for (int y = 0; y < gridpoints; y++) + for (int z = 0; z < gridpoints; z++) + { + int offset = clutOffset + z * 2 * nOut + y * gridpoints * 2 * nOut + + x * gridpoints * gridpoints * 2 * nOut; + double xf = ((double) x) / ((double) gridpoints - 1.0); + double yf = ((double) y) / ((double) gridpoints - 1.0); + double zf = ((double) z) / ((double) gridpoints - 1.0); + buf.putShort(offset, (short) (xf * 65535.0)); + buf.putShort(offset + 2, (short) (yf * 65535.0)); + buf.putShort(offset + 4, (short) (zf * 65535.0)); + } + + for (int channel = 0; channel < 3; channel++) + for (int i = 0; i < nOutEntries; i++) + { + short n = (short) ((i << 8) | i); // assumes 256 entries + buf.putShort(clutOffset + clutSize + (channel * nOutEntries + i) * 2, + n); + } + + return buf.array(); + } + + /** + * Creates profile data corresponding to the built-in colorspaces. + */ + private void createProfile(int colorSpace) throws IllegalArgumentException + { + this.profileID = colorSpace; + header = new ProfileHeader(); + tagTable = new Hashtable(); + + switch (colorSpace) + { + case ColorSpace.CS_sRGB: + createRGBProfile(); + return; + case ColorSpace.CS_LINEAR_RGB: + createLinearRGBProfile(); + return; + case ColorSpace.CS_CIEXYZ: + createCIEProfile(); + return; + case ColorSpace.CS_GRAY: + createGrayProfile(); + return; + case ColorSpace.CS_PYCC: + createPyccProfile(); + return; + default: + throw new IllegalArgumentException("Not a predefined color space!"); + } + } + + /** + * Creates an ICC_Profile representing the sRGB color space + */ + private void createRGBProfile() + { + header.setColorSpace( ColorSpace.TYPE_RGB ); + header.setProfileColorSpace( ColorSpace.TYPE_XYZ ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] r = { 1f, 0f, 0f }; + float[] g = { 0f, 1f, 0f }; + float[] b = { 0f, 0f, 1f }; + float[] black = { 0f, 0f, 0f }; + + // CIE 1931 D50 white point (in Lab coordinates) + float[] white = D50; + + // Get tristimulus values (matrix elements) + r = cs.toCIEXYZ(r); + g = cs.toCIEXYZ(g); + b = cs.toCIEXYZ(b); + + // Generate the sRGB TRC curve, this is the linear->nonlinear + // RGB transform. + cs = new ICC_ColorSpace(getInstance(ICC_ColorSpace.CS_LINEAR_RGB)); + float[] points = new float[TRC_POINTS]; + float[] in = new float[3]; + for (int i = 0; i < TRC_POINTS; i++) + { + in[0] = in[1] = in[2] = ((float) i) / ((float) TRC_POINTS - 1); + in = cs.fromRGB(in); + // Note this value is the same for all components. + points[i] = in[0]; + } + + setData(icSigRedColorantTag, makeXYZData(r)); + setData(icSigGreenColorantTag, makeXYZData(g)); + setData(icSigBlueColorantTag, makeXYZData(b)); + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigMediaBlackPointTag, makeXYZData(black)); + setData(icSigRedTRCTag, makeTRC(points)); + setData(icSigGreenTRCTag, makeTRC(points)); + setData(icSigBlueTRCTag, makeTRC(points)); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Generic sRGB")); + this.profileID = ColorSpace.CS_sRGB; + } + + /** + * Creates an linear sRGB profile + */ + private void createLinearRGBProfile() + { + header.setColorSpace(ColorSpace.TYPE_RGB); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] r = { 1f, 0f, 0f }; + float[] g = { 0f, 1f, 0f }; + float[] b = { 0f, 0f, 1f }; + float[] black = { 0f, 0f, 0f }; + + float[] white = D50; + + // Get tristimulus values (matrix elements) + r = cs.toCIEXYZ(r); + g = cs.toCIEXYZ(g); + b = cs.toCIEXYZ(b); + + setData(icSigRedColorantTag, makeXYZData(r)); + setData(icSigGreenColorantTag, makeXYZData(g)); + setData(icSigBlueColorantTag, makeXYZData(b)); + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigMediaBlackPointTag, makeXYZData(black)); + + setData(icSigRedTRCTag, makeTRC()); + setData(icSigGreenTRCTag, makeTRC()); + setData(icSigBlueTRCTag, makeTRC()); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Linear RGB")); + this.profileID = ColorSpace.CS_LINEAR_RGB; + } + + /** + * Creates an CIE XYZ identity profile + */ + private void createCIEProfile() + { + header.setColorSpace( ColorSpace.TYPE_XYZ ); + header.setProfileColorSpace( ColorSpace.TYPE_XYZ ); + header.setProfileClass( CLASS_COLORSPACECONVERSION ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] white = D50; + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigAToB0Tag, makeIdentityClut()); + setData(icSigBToA0Tag, makeIdentityClut()); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("CIE XYZ identity profile")); + this.profileID = ColorSpace.CS_CIEXYZ; + } + + /** + * Creates a linear gray ICC_Profile + */ + private void createGrayProfile() + { + header.setColorSpace(ColorSpace.TYPE_GRAY); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + + // CIE 1931 D50 white point (in Lab coordinates) + float[] white = D50; + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigGrayTRCTag, makeTRC(1.0f)); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Linear grayscale")); + this.profileID = ColorSpace.CS_GRAY; + } + + /** + * XXX Implement me + */ + private void createPyccProfile() + { + header.setColorSpace(ColorSpace.TYPE_3CLR); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + + // Create CLUTs here. :-) + + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Photo YCC")); + this.profileID = ColorSpace.CS_PYCC; + } +} // class ICC_Profile diff --git a/libjava/classpath/java/awt/color/ICC_ProfileGray.java b/libjava/classpath/java/awt/color/ICC_ProfileGray.java new file mode 100644 index 00000000000..3b5948dc8fc --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ProfileGray.java @@ -0,0 +1,133 @@ +/* ICC_ProfileGray.java -- the ICC profile for a Gray colorspace + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * ICC_ProfileGray - a special case of ICC_Profiles. + * + * The ICC_Profile.getInstance() method will return an instance of the + * ICC_ProfileGray subclass when all the following conditions are met: + * The device color space of the profile is TYPE_GRAY. + * The profile contains a gray TRCTag. + * The profile contains a mediaWhitePointTag. + * + * As per the ICC specification, the color space conversion can then + * be done through the following method: + * linearGray = grayTRC[deviceGray] + * + * Note that if the profile contains a CLUT for the color space conversion, + * it should be used instead, and the TRC information ignored. + * + * @author Sven de Marothy + * @since 1.2 + */ +public class ICC_ProfileGray extends ICC_Profile +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1124721290732002649L; + private transient float[] whitePoint; + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileGray from a predefined ColorSpace (CS_GRAY) + */ + ICC_ProfileGray(int cspace) + { + super(cspace); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileGray from profile data. + */ + ICC_ProfileGray(byte[] data) + { + super(data); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + + /** + * Returns the media white point of the profile. + */ + public float[] getMediaWhitePoint() + { + float[] wp = new float[3]; + wp[0] = whitePoint[0]; + wp[1] = whitePoint[1]; + wp[2] = whitePoint[2]; + return wp; + } + + /** + * Returns the TRC gamma value. + * @throws ProfileDataException if the TRC is described by a lookup + * table and not a gamma value. + */ + public float getGamma() + { + short[] data = getCurve(icSigGrayTRCTag); + if (data == null) + throw new IllegalArgumentException("Couldn't read Gray TRC data."); + if (data.length != 1) + throw new ProfileDataException("TRC is a table, not a gamma value."); + + // convert the unsigned 7.8 fixed-point gamma to a float. + double gamma = (double) (data[0] & (0xFFFF)) / 256.0; + return (float) gamma; + } + + /** + * Returns the TRC lookup table. + * @throws ProfileDataException if the TRC is described by a gamma value + * and not a lookup table. + */ + public short[] getTRC() + { + short[] data = getCurve(icSigGrayTRCTag); + if (data == null) + throw new IllegalArgumentException("Couldn't read Gray TRC data."); + if (data.length <= 1) + throw new ProfileDataException("Gamma value, not a TRC table."); + return data; + } +} // class ICC_ProfileGray diff --git a/libjava/classpath/java/awt/color/ICC_ProfileRGB.java b/libjava/classpath/java/awt/color/ICC_ProfileRGB.java new file mode 100644 index 00000000000..00393328bbd --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ProfileRGB.java @@ -0,0 +1,227 @@ +/* ICC_ProfileRGB.java -- the ICC profile for a RGB colorspace + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * ICC_ProfileRGB - a special case of ICC_Profiles. + * + * The ICC_Profile.getInstance() method will return an instance of the + * ICC_ProfileRGB subclass when all the following conditions are met: + * The device color space of the profile is TYPE_RGB. + * The profile contains red, green and blue ColorantTags. + * The profile contains red, green and blue TRCTags. + * The profile contains a mediaWhitePointTag included. + * + * As per the ICC specification, the color space conversion can then + * be done through the following method: + * linearR = redTRC[deviceR] + * linearG = greenTRC[deviceG] + * linearB = blueTRC[deviceB] + * TRC curves are either a single gamma value, or a 1-dimensional lookup table. + * + * Followed by the matrix transform: + * PCS = M*linear + * + * Where PCS is the vector of profile color space (must be XYZ) coordinates, + * linear is the vector of linear RGB coordinates, and the matrix M is + * constructed from the ColorantTags, where the columns are red, green and + * blue respectively, and the rows are X, Y and Z. + * + * Note that if the profile contains a CLUT for the color space conversion, + * it should be used instead, and the TRC information ignored. + * + * @author Sven de Marothy + * @since 1.2 + */ +public class ICC_ProfileRGB extends ICC_Profile +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8505067385152579334L; + + public static final int REDCOMPONENT = 0; + + public static final int GREENCOMPONENT = 1; + + public static final int BLUECOMPONENT = 2; + + private transient float[][] matrix; + + private transient float[] gamma; + + private transient float[] whitePoint; + + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileRGB from a predefined ColorSpace (CS_LINEAR_RGB and CS_sRGB) + */ + ICC_ProfileRGB(int cspace) + { + super(cspace); + matrix = createMatrix(); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileRGB from profile data. + */ + ICC_ProfileRGB(byte[] data) + { + super(data); + matrix = createMatrix(); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Returns the media white point of the profile. + */ + public float[] getMediaWhitePoint() + { + float[] wp = new float[3]; + wp[0] = whitePoint[0]; + wp[1] = whitePoint[1]; + wp[2] = whitePoint[2]; + return wp; + } + + /** + * Returns the colorant matrix of the conversion. + */ + public float[][] getMatrix() + { + float[][] mat = new float[3][3]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + mat[i][j] = matrix[i][j]; + return mat; + } + + /** + * Returns the gamma value of a component + * @throws ProfileDataException if the TRC is described by a lookup + * table and not a gamma value. + */ + public float getGamma(int component) + { + short[] data; + switch (component) + { + case REDCOMPONENT: + data = getCurve(icSigRedTRCTag); + break; + case GREENCOMPONENT: + data = getCurve(icSigGreenTRCTag); + break; + case BLUECOMPONENT: + data = getCurve(icSigBlueTRCTag); + break; + default: + throw new IllegalArgumentException("Not a valid component"); + } + if (data == null) + throw new IllegalArgumentException("Error reading TRC"); + + if (data.length != 1) + throw new ProfileDataException("Not a single-gamma TRC"); + + // convert the unsigned 7.8 fixed-point gamma to a float. + float gamma = (float) (((int) data[0] & 0xFF00) >> 8); + double fraction = ((int) data[0] & 0x00FF) / 256.0; + gamma += (float) fraction; + return gamma; + } + + /** + * Returns the TRC lookup table for a component + * @throws ProfileDataException if the TRC is described by a gamma + * value and not a lookup table. + */ + public short[] getTRC(int component) + { + short[] data; + switch (component) + { + case REDCOMPONENT: + data = getCurve(icSigRedTRCTag); + break; + case GREENCOMPONENT: + data = getCurve(icSigGreenTRCTag); + break; + case BLUECOMPONENT: + data = getCurve(icSigBlueTRCTag); + break; + default: + throw new IllegalArgumentException("Not a valid component"); + } + if (data == null) + throw new IllegalArgumentException("Error reading TRC"); + + if (data.length <= 1) + throw new ProfileDataException("Gamma value, not a TRC table."); + + return data; + } + + /** + * Creates the colorspace conversion matrix from the RGB tristimulus + * values. + */ + private float[][] createMatrix() throws IllegalArgumentException + { + float[][] mat = new float[3][3]; + float[] r; + float[] g; + float[] b; + r = getXYZData(icSigRedColorantTag); + g = getXYZData(icSigGreenColorantTag); + b = getXYZData(icSigBlueColorantTag); + if (r == null || g == null || b == null) + throw new IllegalArgumentException("Error reading colorant tags!"); + for (int i = 0; i < 3; i++) + { + mat[i][0] = r[i]; + mat[i][1] = g[i]; + mat[i][2] = b[i]; + } + return mat; + } +} // class ICC_ProfileRGB diff --git a/libjava/classpath/java/awt/color/ProfileDataException.java b/libjava/classpath/java/awt/color/ProfileDataException.java new file mode 100644 index 00000000000..1af23b1835c --- /dev/null +++ b/libjava/classpath/java/awt/color/ProfileDataException.java @@ -0,0 +1,64 @@ +/* ProfileDataException.java -- error in processing an ICC_Profile + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * Thrown when there is an error accessing or processing an + * <code>ICC_Profile</code>. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class ProfileDataException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7286140888240322498L; + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public ProfileDataException(String message) + { + super(message); + } +} // class ProfileDataException diff --git a/libjava/classpath/java/awt/color/package.html b/libjava/classpath/java/awt/color/package.html new file mode 100644 index 00000000000..9a08577c199 --- /dev/null +++ b/libjava/classpath/java/awt/color/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.color package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.color</title></head> + +<body> +<p>Classes to represent color spaces and profiles.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/datatransfer/Clipboard.java b/libjava/classpath/java/awt/datatransfer/Clipboard.java new file mode 100644 index 00000000000..9953a724dba --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/Clipboard.java @@ -0,0 +1,114 @@ +/* Clipboard.java -- Class for transferring data via cut and paste. + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * This class allows data to be transferred using a cut and paste type + * mechanism. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Clipboard +{ + /** + * The data being transferred. + */ + protected Transferable contents; + + /** + * The owner of this clipboard. + */ + protected ClipboardOwner owner; + + // The clipboard name + private String name; + + /** + * Initializes a new instance of <code>Clipboard</code> with the + * specified name. + * + * @param name The clipboard name. + */ + public Clipboard(String name) + { + this.name = name; + } + + /** + * Returns the name of the clipboard. + */ + public String getName() + { + return name; + } + + /** + * Returns the contents of the clipboard. + * + * @param requestor The object requesting the contents. + * + * @exception IllegalStateException If the clipboard is currently unavailable + */ + public synchronized Transferable getContents(Object requestor) + { + return contents; + } + + /** + * Sets the content and owner of this clipboard. + * If the given owner is different from the current owner + * then lostOwnership is called on the current owner. + * XXX - is this called with the old or new contents. + * + * @param contents The new clipboard contents. + * @param owner The new clipboard owner + * + * @exception IllegalStateException If the clipboard is currently unavailable + */ + public synchronized void setContents(Transferable contents, ClipboardOwner owner) + { + if (this.owner != owner) + if (this.owner != null) + this.owner.lostOwnership(this, contents); + + this.owner = owner; + this.contents = contents; + } +} + diff --git a/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java b/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java new file mode 100644 index 00000000000..df758254a55 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java @@ -0,0 +1,57 @@ +/* ClipboardOwner.java -- Interface for clipboard providers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * This interface is for classes that will own a clipboard object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ClipboardOwner +{ + /** + * This method is called to notify this object that it no longer + * has ownership of the specified <code>Clipboard</code>. + * + * @param clipboard The clipboard for which ownership was lost. + * @param contents The contents of the clipboard which are no longer owned. + */ + void lostOwnership (Clipboard clipboard, Transferable contents); +} + diff --git a/libjava/classpath/java/awt/datatransfer/DataFlavor.java b/libjava/classpath/java/awt/datatransfer/DataFlavor.java new file mode 100644 index 00000000000..e5fbd240293 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/DataFlavor.java @@ -0,0 +1,1034 @@ +/* DataFlavor.java -- A type of data to transfer via the clipboard. + Copyright (C) 1999, 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * This class represents a particular data format used for transferring + * data via the clipboard. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DataFlavor implements java.io.Externalizable, Cloneable +{ + static final long serialVersionUID = 8367026044764648243L; + + // FIXME: Serialization: Need to write methods for. + +/** + * This is the data flavor used for tranferring plain text. The MIME + * type is "text/plain; charset=unicode". The representation class + * is <code>java.io.InputStream</code>. + * + * @deprecated The charset unicode is platform specific and InputStream + * deals with bytes not chars. Use <code>getRederForText()</code>. + */ +public static final DataFlavor plainTextFlavor; + +/** + * This is the data flavor used for transferring Java strings. The + * MIME type is "application/x-java-serialized-object" and the + * representation class is <code>java.lang.String</code>. + */ +public static final DataFlavor stringFlavor; + +/** + * This is a data flavor used for transferring lists of files. The + * representation type is a <code>java.util.List</code>, with each element of + * the list being a <code>java.io.File</code>. + */ +public static final DataFlavor javaFileListFlavor; + +/** + * This is an image flavor used for transferring images. The + * representation type is a <code>java.awt.Image</code>. + */ +public static final DataFlavor imageFlavor; + +/** + * This is the MIME type used for transferring a serialized object. + * The representation class is the type of object be deserialized. + */ +public static final String javaSerializedObjectMimeType = + "application/x-java-serialized-object"; + +/** + * This is the MIME type used to transfer a Java object reference within + * the same JVM. The representation class is the class of the object + * being transferred. + */ +public static final String javaJVMLocalObjectMimeType = + "application/x-java-jvm-local-objectref"; + +/** + * This is the MIME type used to transfer a link to a remote object. + * The representation class is the type of object being linked to. + */ +public static final String javaRemoteObjectMimeType = + "application/x-java-remote-object"; + +static +{ + plainTextFlavor + = new DataFlavor(java.io.InputStream.class, + "text/plain; charset=unicode", + "plain unicode text"); + + stringFlavor + = new DataFlavor(java.lang.String.class, + "Java Unicode String"); + + javaFileListFlavor + = new DataFlavor(java.util.List.class, + "Java File List"); + + // javaFileListFlavor.mimeType = "application/x-java-file-list"; + + imageFlavor + = new DataFlavor(java.awt.Image.class, + "Java Image"); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +// The MIME type for this flavor +private final String mimeType; + +// The representation class for this flavor +private final Class representationClass; + +// The human readable name of this flavor +private String humanPresentableName; + +/*************************************************************************/ + +/* + * Static Methods + */ + +/** + * This method attempts to load the named class. The following class + * loaders are searched in order: the bootstrap class loader, the + * system class loader, the context class loader (if it exists), and + * the specified fallback class loader. + * + * @param className The name of the class to load. + * @param classLoader The class loader to use if all others fail, which + * may be <code>null</code>. + * + * @exception ClassNotFoundException If the class cannot be loaded. + */ +protected static final Class +tryToLoadClass(String className, ClassLoader classLoader) + throws ClassNotFoundException +{ + try + { + return(Class.forName(className)); + } + catch(Exception e) { ; } + // Commented out for Java 1.1 + /* + try + { + return(className.getClass().getClassLoader().findClass(className)); + } + catch(Exception e) { ; } + + try + { + return(ClassLoader.getSystemClassLoader().findClass(className)); + } + catch(Exception e) { ; } + */ + + // FIXME: What is the context class loader? + /* + try + { + } + catch(Exception e) { ; } + */ + + if (classLoader != null) + return(classLoader.loadClass(className)); + else + throw new ClassNotFoundException(className); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Empty public constructor needed for externalization. + * Should not be used for normal instantiation. + */ +public +DataFlavor() +{ + mimeType = null; + representationClass = null; + humanPresentableName = null; +} + +/*************************************************************************/ + +/** + * Private constructor. + */ +private +DataFlavor(Class representationClass, + String mimeType, + String humanPresentableName) +{ + this.representationClass = representationClass; + this.mimeType = mimeType; + if (humanPresentableName != null) + this.humanPresentableName = humanPresentableName; + else + this.humanPresentableName = mimeType; +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>DataFlavor</code>. The class + * and human readable name are specified, the MIME type will be + * "application/x-java-serialized-object". If the human readable name + * is not specified (<code>null</code>) then the human readable name + * will be the same as the MIME type. + * + * @param representationClass The representation class for this object. + * @param humanPresentableName The display name of the object. + */ +public +DataFlavor(Class representationClass, String humanPresentableName) +{ + this(representationClass, + "application/x-java-serialized-object" + + "; class=" + + representationClass.getName(), + humanPresentableName); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>DataFlavor</code> with the + * specified MIME type and description. If the MIME type has a + * "class=<rep class>" parameter then the representation class will + * be the class name specified. Otherwise the class defaults to + * <code>java.io.InputStream</code>. If the human readable name + * is not specified (<code>null</code>) then the human readable name + * will be the same as the MIME type. + * + * @param mimeType The MIME type for this flavor. + * @param humanPresentableName The display name of this flavor. + * @param classLoader The class loader for finding classes if the default + * class loaders do not work. + * + * @exception IllegalArgumentException If the representation class + * specified cannot be loaded. + * @exception ClassNotFoundException If the class is not loaded. + */ +public +DataFlavor(String mimeType, String humanPresentableName, + ClassLoader classLoader) throws ClassNotFoundException +{ + this(getRepresentationClassFromMime(mimeType, classLoader), + mimeType, humanPresentableName); +} + +private static Class +getRepresentationClassFromMime(String mimeString, ClassLoader classLoader) +{ + String classname = getParameter("class", mimeString); + if (classname != null) + { + try + { + return tryToLoadClass(classname, classLoader); + } + catch(Exception e) + { + throw new IllegalArgumentException("classname: " + e.getMessage()); + } + } + else + { + return java.io.InputStream.class; + } +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>DataFlavor</code> with the + * specified MIME type and description. If the MIME type has a + * "class=<rep class>" parameter then the representation class will + * be the class name specified. Otherwise the class defaults to + * <code>java.io.InputStream</code>. If the human readable name + * is not specified (<code>null</code>) then the human readable name + * will be the same as the MIME type. This is the same as calling + * <code>new DataFlavor(mimeType, humanPresentableName, null)</code>. + * + * @param mimeType The MIME type for this flavor. + * @param humanPresentableName The display name of this flavor. + * + * @exception IllegalArgumentException If the representation class + * specified cannot be loaded. + */ +public +DataFlavor(String mimeType, String humanPresentableName) +{ + this (getRepresentationClassFromMime (mimeType, null), humanPresentableName); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of <code>DataFlavor</code> with the specified + * MIME type. This type can have a "class=" parameter to specify the + * representation class, and then the class must exist or an exception will + * be thrown. If there is no "class=" parameter then the representation class + * will be <code>java.io.InputStream</code>. This is the same as calling + * <code>new DataFlavor(mimeType, null)</code>. + * + * @param mimeType The MIME type for this flavor. + * + * @exception IllegalArgumentException If a class is not specified in + * the MIME type. + * @exception ClassNotFoundException If the class cannot be loaded. + */ +public +DataFlavor(String mimeType) throws ClassNotFoundException +{ + this(mimeType, null); +} + +/*************************************************************************/ + +/** + * Returns the MIME type of this flavor. + * + * @return The MIME type for this flavor. + */ +public String +getMimeType() +{ + return(mimeType); +} + +/*************************************************************************/ + +/** + * Returns the representation class for this flavor. + * + * @return The representation class for this flavor. + */ +public Class +getRepresentationClass() +{ + return(representationClass); +} + +/*************************************************************************/ + +/** + * Returns the human presentable name for this flavor. + * + * @return The human presentable name for this flavor. + */ +public String +getHumanPresentableName() +{ + return(humanPresentableName); +} + +/*************************************************************************/ + +/** + * Returns the primary MIME type for this flavor. + * + * @return The primary MIME type for this flavor. + */ +public String +getPrimaryType() +{ + int idx = mimeType.indexOf("/"); + if (idx == -1) + return(mimeType); + + return(mimeType.substring(0, idx)); +} + +/*************************************************************************/ + +/** + * Returns the MIME subtype for this flavor. + * + * @return The MIME subtype for this flavor. + */ +public String +getSubType() +{ + int idx = mimeType.indexOf("/"); + if (idx == -1) + return(""); + + String subtype = mimeType.substring(idx + 1); + + idx = subtype.indexOf(" "); + if (idx == -1) + return(subtype); + else + return(subtype.substring(0, idx)); +} + +/*************************************************************************/ + +/** + * Returns the value of the named MIME type parameter, or <code>null</code> + * if the parameter does not exist. Given the parameter name and the mime + * string. + * + * @param paramName The name of the parameter. + * @param mimeString The mime string from where the name should be found. + * + * @return The value of the parameter or null. + */ +private static String +getParameter(String paramName, String mimeString) +{ + int idx = mimeString.indexOf(paramName + "="); + if (idx == -1) + return(null); + + String value = mimeString.substring(idx + paramName.length() + 1); + + idx = value.indexOf(" "); + if (idx == -1) + return(value); + else + return(value.substring(0, idx)); +} + +/*************************************************************************/ + +/** + * Returns the value of the named MIME type parameter, or <code>null</code> + * if the parameter does not exist. + * + * @param paramName The name of the paramter. + * + * @return The value of the parameter. + */ +public String +getParameter(String paramName) +{ + return getParameter(paramName, mimeType); +} + +/*************************************************************************/ + +/** + * Sets the human presentable name to the specified value. + * + * @param humanPresentableName The new display name. + */ +public void +setHumanPresentableName(String humanPresentableName) +{ + this.humanPresentableName = humanPresentableName; +} + +/*************************************************************************/ + +/** + * Tests the MIME type of this object for equality against the specified + * MIME type. + * + * @param mimeType The MIME type to test against. + * + * @return <code>true</code> if the MIME type is equal to this object's + * MIME type, <code>false</code> otherwise. + * + * @exception NullPointerException If mimeType is null. + */ +public boolean +isMimeTypeEqual(String mimeType) +{ + // FIXME: Need to handle default attributes and parameters + + return(this.mimeType.equals(mimeType)); +} + +/*************************************************************************/ + +/** + * Tests the MIME type of this object for equality against the specified + * data flavor's MIME type + * + * @param flavor The flavor to test against. + * + * @return <code>true</code> if the flavor's MIME type is equal to this + * object's MIME type, <code>false</code> otherwise. + */ +public final boolean +isMimeTypeEqual(DataFlavor flavor) +{ + return(isMimeTypeEqual(flavor.getMimeType())); +} + +/*************************************************************************/ + +/** + * Tests whether or not this flavor represents a serialized object. + * + * @return <code>true</code> if this flavor represents a serialized + * object, <code>false</code> otherwise. + */ +public boolean +isMimeTypeSerializedObject() +{ + return(mimeType.startsWith(javaSerializedObjectMimeType)); +} + +/*************************************************************************/ + +/** + * Tests whether or not this flavor has a representation class of + * <code>java.io.InputStream</code>. + * + * @return <code>true</code> if the representation class of this flavor + * is <code>java.io.InputStream</code>, <code>false</code> otherwise. + */ +public boolean +isRepresentationClassInputStream() +{ + return(representationClass.getName().equals("java.io.InputStream")); +} + +/*************************************************************************/ + +/** + * Tests whether the representation class for this flavor is + * serializable. + * + * @return <code>true</code> if the representation class is serializable, + * <code>false</code> otherwise. + */ +public boolean +isRepresentationClassSerializable() +{ + Class[] interfaces = representationClass.getInterfaces(); + + int i = 0; + while (i < interfaces.length) + { + if (interfaces[i].getName().equals("java.io.Serializable")) + return(true); + ++i; + } + + return(false); +} + +/*************************************************************************/ + +/** + * Tests whether the representation class for his flavor is remote. + * + * @return <code>true</code> if the representation class is remote, + * <code>false</code> otherwise. + */ +public boolean +isRepresentationClassRemote() +{ + // FIXME: Implement + throw new RuntimeException("Not implemented"); +} + +/*************************************************************************/ + +/** + * Tests whether or not this flavor represents a serialized object. + * + * @return <code>true</code> if this flavor represents a serialized + * object, <code>false</code> otherwise. + */ +public boolean +isFlavorSerializedObjectType() +{ + // FIXME: What is the diff between this and isMimeTypeSerializedObject? + return(mimeType.startsWith(javaSerializedObjectMimeType)); +} + +/*************************************************************************/ + +/** + * Tests whether or not this flavor represents a remote object. + * + * @return <code>true</code> if this flavor represents a remote object, + * <code>false</code> otherwise. + */ +public boolean +isFlavorRemoteObjectType() +{ + return(mimeType.startsWith(javaRemoteObjectMimeType)); +} + +/*************************************************************************/ + +/** + * Tests whether or not this flavor represents a list of files. + * + * @return <code>true</code> if this flavor represents a list of files, + * <code>false</code> otherwise. + */ +public boolean +isFlavorJavaFileListType() +{ + if (this.mimeType.equals(javaFileListFlavor.mimeType) && + this.representationClass.equals(javaFileListFlavor.representationClass)) + return(true); + + return(false); +} + +/*************************************************************************/ + +/** + * Returns a copy of this object. + * + * @return A copy of this object. + * + * @exception CloneNotSupportedException If the object's class does not support + * the Cloneable interface. Subclasses that override the clone method can also + * throw this exception to indicate that an instance cannot be cloned. + */ +public Object clone () throws CloneNotSupportedException +{ + try + { + return(super.clone()); + } + catch(Exception e) + { + return(null); + } +} + +/*************************************************************************/ + +/** + * This method test the specified <code>DataFlavor</code> for equality + * against this object. This will be true if the MIME type and + * representation type are the equal. + * + * @param flavor The <code>DataFlavor</code> to test against. + * + * @return <code>true</code> if the flavor is equal to this object, + * <code>false</code> otherwise. + */ +public boolean +equals(DataFlavor flavor) +{ + if (flavor == null) + return(false); + + if (!this.mimeType.toLowerCase().equals(flavor.mimeType.toLowerCase())) + return(false); + + if (!this.representationClass.equals(flavor.representationClass)) + return(false); + + return(true); +} + +/*************************************************************************/ + +/** + * This method test the specified <code>Object</code> for equality + * against this object. This will be true if the following conditions + * are met: + * <p> + * <ul> + * <li>The object is not <code>null</code>.</li> + * <li>The object is an instance of <code>DataFlavor</code>.</li> + * <li>The object's MIME type and representation class are equal to + * this object's.</li> + * </ul> + * + * @param obj The <code>Object</code> to test against. + * + * @return <code>true</code> if the flavor is equal to this object, + * <code>false</code> otherwise. + */ +public boolean +equals(Object obj) +{ + if (!(obj instanceof DataFlavor)) + return(false); + + return(equals((DataFlavor)obj)); +} + +/*************************************************************************/ + +/** + * Tests whether or not the specified string is equal to the MIME type + * of this object. + * + * @param str The string to test against. + * + * @return <code>true</code> if the string is equal to this object's MIME + * type, <code>false</code> otherwise. + * + * @deprecated Not compatible with <code>hashCode()</code>. + * Use <code>isMimeTypeEqual()</code> + */ +public boolean +equals(String str) +{ + return(isMimeTypeEqual(str)); +} + +/*************************************************************************/ + +/** + * Returns the hash code for this data flavor. + * The hash code is based on the (lower case) mime type and the + * representation class. + */ +public int +hashCode() +{ + return(mimeType.toLowerCase().hashCode()^representationClass.hashCode()); +} + +/*************************************************************************/ + +/** + * Returns <code>true</code> when the given <code>DataFlavor</code> + * matches this one. + */ +public boolean +match(DataFlavor dataFlavor) +{ + // XXX - How is this different from equals? + return(equals(dataFlavor)); +} + +/*************************************************************************/ + +/** + * This method exists for backward compatibility. It simply returns + * the same name/value pair passed in. + * + * @param name The parameter name. + * @param value The parameter value. + * + * @return The name/value pair. + * + * @deprecated + */ +protected String +normalizeMimeTypeParameter(String name, String value) +{ + return(name + "=" + value); +} + +/*************************************************************************/ + +/** + * This method exists for backward compatibility. It simply returns + * the MIME type string unchanged. + * + * @param type The MIME type. + * + * @return The MIME type. + * + * @deprecated + */ +protected String +normalizeMimeType(String type) +{ + return(type); +} + +/*************************************************************************/ + +/** + * Serialize this class. + * + * @param stream The <code>ObjectOutput</code> stream to serialize to. + * + * @exception IOException If an error occurs. + */ +public void +writeExternal(ObjectOutput stream) throws IOException +{ + // FIXME: Implement me +} + +/*************************************************************************/ + +/** + * De-serialize this class. + * + * @param stream The <code>ObjectInput</code> stream to deserialize from. + * + * @exception IOException If an error ocurs. + * @exception ClassNotFoundException If the class for an object being restored + * cannot be found. + */ +public void +readExternal(ObjectInput stream) throws IOException, ClassNotFoundException +{ + // FIXME: Implement me +} + +/*************************************************************************/ + +/** + * Returns a string representation of this DataFlavor. Including the + * representation class name, MIME type and human presentable name. + */ +public String +toString() +{ + return("DataFlavor[representationClass=" + + representationClass.getName() + + ",mimeType=" + + mimeType + + "humanPresentableName=" + + humanPresentableName); +} + +/*************************************************************************/ + +/** + * XXX - Currently returns <code>plainTextFlavor</code>. + */ +public static final DataFlavor +getTextPlainUnicodeFlavor() +{ + return(plainTextFlavor); +} + +/*************************************************************************/ + +/** + * XXX - Currently returns <code>java.io.InputStream</code>. + * + * @since 1.3 + */ +public final Class +getDefaultRepresentationClass() +{ + return(java.io.InputStream.class); +} +/*************************************************************************/ + +/** + * XXX - Currently returns <code>java.io.InputStream</code>. + */ +public final String +getDefaultRepresentationClassAsString() +{ + return(getDefaultRepresentationClass().getName()); +} + +/*************************************************************************/ + +/** + * Selects the best supported text flavor on this implementation. + * Returns <code>null</code> when none of the given flavors is liked. + * + * The <code>DataFlavor</code> returned the first data flavor in the + * array that has either a representation class which is (a subclass of) + * <code>Reader</code> or <code>String</code>, or has a representation + * class which is (a subclass of) <code>InputStream</code> and has a + * primary MIME type of "text" and has an supported encoding. + */ +public static final DataFlavor +selectBestTextFlavor(DataFlavor[] availableFlavors) +{ + for(int i=0; i<availableFlavors.length; i++) + { + DataFlavor df = availableFlavors[i]; + Class c = df.representationClass; + + // A Reader or String is good. + if ((Reader.class.isAssignableFrom(c)) + || (String.class.isAssignableFrom(c))) + { + return df; + } + + // A InputStream is good if the mime primary type is "text" + if ((InputStream.class.isAssignableFrom(c)) + && ("text".equals(df.getPrimaryType()))) + { + String encoding = availableFlavors[i].getParameter("charset"); + if (encoding == null) + encoding = "us-ascii"; + Reader r = null; + try + { + // Try to construct a dummy reader with the found encoding + r = new InputStreamReader + (new ByteArrayInputStream(new byte[0]), encoding); + } + catch(UnsupportedEncodingException uee) { /* ignore */ } + if (r != null) + return df; + } + } + + // Nothing found + return(null); +} + +/*************************************************************************/ + +/** + * Creates a <code>Reader</code> for a given <code>Transferable</code>. + * + * If the representation class is a (subclass of) <code>Reader</code> + * then an instance of the representation class is returned. If the + * representatation class is a <code>String</code> then a + * <code>StringReader</code> is returned. And if the representation class + * is a (subclass of) <code>InputStream</code> and the primary MIME type + * is "text" then a <code>InputStreamReader</code> for the correct charset + * encoding is returned. + * + * @param transferable The <code>Transferable</code> for which a text + * <code>Reader</code> is requested. + * + * @exception IllegalArgumentException If the representation class is not one + * of the seven listed above or the Transferable has null data. + * @exception NullPointerException If the Transferable is null. + * @exception UnsupportedFlavorException when the transferable doesn't + * support this <code>DataFlavor</code>. Or if the representable class + * isn't a (subclass of) <code>Reader</code>, <code>String</code>, + * <code>InputStream</code> and/or the primary MIME type isn't "text". + * @exception IOException when any IOException occurs. + * @exception UnsupportedEncodingException if the "charset" isn't supported + * on this platform. + */ +public Reader getReaderForText(Transferable transferable) + throws UnsupportedFlavorException, IOException +{ + if (!transferable.isDataFlavorSupported(this)) + throw new UnsupportedFlavorException(this); + + if (Reader.class.isAssignableFrom(representationClass)) + return((Reader)transferable.getTransferData(this)); + + if (String.class.isAssignableFrom(representationClass)) + return(new StringReader((String)transferable.getTransferData(this))); + + if (InputStream.class.isAssignableFrom(representationClass) + && "text".equals(getPrimaryType())) + { + InputStream in = (InputStream)transferable.getTransferData(this); + String encoding = getParameter("charset"); + if (encoding == null) + encoding = "us-ascii"; + return(new InputStreamReader(in, encoding)); + } + + throw new UnsupportedFlavorException(this); +} + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.nio.ByteBuffer or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassByteBuffer () + { + return ByteBuffer.class.isAssignableFrom (representationClass); + } + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.nio.CharBuffer or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassCharBuffer () + { + return CharBuffer.class.isAssignableFrom (representationClass); + } + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.io.Reader or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassReader () + { + return Reader.class.isAssignableFrom (representationClass); + } + +} // class DataFlavor + diff --git a/libjava/classpath/java/awt/datatransfer/FlavorMap.java b/libjava/classpath/java/awt/datatransfer/FlavorMap.java new file mode 100644 index 00000000000..59718c4513c --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorMap.java @@ -0,0 +1,75 @@ +/* FlavorMap.java -- Maps between flavor names and MIME types. + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.Map; + +/** + * This interface maps between native platform type names and DataFlavors. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface FlavorMap +{ + /** + * Maps the specified <code>DataFlavor</code> objects to the native + * data type name. The returned <code>Map</code> has keys that are + * the data flavors and values that are strings. The returned map + * may be modified. This can be useful for implementing nested mappings. + * + * @param flavors An array of data flavors to map + * or null for all data flavors. + * + * @return A <code>Map</code> of native data types. + */ + Map getNativesForFlavors (DataFlavor[] flavors); + + /** + * Maps the specified native type names to <code>DataFlavor</code>'s. + * The returned <code>Map</code> has keys that are strings and values + * that are <code>DataFlavor</code>'s. The returned map may be + * modified. This can be useful for implementing nested mappings. + * + * @param natives An array of native types to map + * or null for all native types. + * + * @return A <code>Map</code> of data flavors. + */ + Map getFlavorsForNatives (String[] natives); +} diff --git a/libjava/classpath/java/awt/datatransfer/FlavorTable.java b/libjava/classpath/java/awt/datatransfer/FlavorTable.java new file mode 100644 index 00000000000..11cdda06ca7 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorTable.java @@ -0,0 +1,73 @@ +/* FlavorTable.java -- A relaxed mapping between flavors + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.List; + +/** + * A FlavorMap which no longer requires a 1-to-1 mapping between flavors. Any + * native can map to multiple flavors, and any flavor can map to multiple + * natives; although the mappings are usually symmetric. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public interface FlavorTable extends FlavorMap +{ + /** + * Returns a list of String natives corresponding to the given flavor. The + * list should be sorted from best to worst. The list must be modifiable + * without affecting this table. + * + * @param flavor the flavor to look up, or null to return all natives + * @return the sorted list of natives + */ + List getNativesForFlavor(DataFlavor flavor); + + /** + * Returns a list of flavors corresponding to the given String native. The + * list should be sorted from best to worst. The list must be modifiable + * without affecting this table. + * + * @param name the native name to look up, or null to return all flavors + * @return the sorted list of flavors + */ + List getFlavorsForNative(String name); +} diff --git a/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java b/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java new file mode 100644 index 00000000000..6113ab760b8 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java @@ -0,0 +1,70 @@ +/* MimeTypeParseException.java -- thrown when MIME string couldn't be parsed + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * MIME string couldn't be parsed correctly. + * + * @author Mark Wielaard (mark@klomp.org) + * @status updated to 1.4 + */ +public class MimeTypeParseException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5604407764691570741L; + + /** + * Create a new instance without any message. + */ + public MimeTypeParseException() + { + } + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public MimeTypeParseException(String message) + { + super(message); + } +} // class MimeTypeParseException diff --git a/libjava/classpath/java/awt/datatransfer/StringSelection.java b/libjava/classpath/java/awt/datatransfer/StringSelection.java new file mode 100644 index 00000000000..b74f2fa6418 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/StringSelection.java @@ -0,0 +1,158 @@ +/* StringSelection.java -- Clipboard handler for text. + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.IOException; +import java.io.StringReader; + +/** + * This class transfers a string as plain text using the clipboard. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class StringSelection implements Transferable, ClipboardOwner +{ + +/* + * Class Variables + */ + +// List of flavors we support +// XXX: DataFlavor.plainTextFlavor is deprecated. +static final DataFlavor[] supported_flavors + = { DataFlavor.stringFlavor, + DataFlavor.plainTextFlavor }; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +// This is the data to transfer +private String data; + + /** + * Transfer the specfied string as text. + * + * @param data the data for the string selection + */ + public StringSelection(String data) + { + this.data = data; + } + +/** + * Returns a list of supported data flavors. + * + * @return A list of supported data flavors. + */ +public DataFlavor[] +getTransferDataFlavors() +{ + return(supported_flavors); +} + +/*************************************************************************/ + +/** + * Tests whether or not the specified data flavor is supported. + * + * @param flavor The data flavor to test. + * + * @return <code>true</code> if the data flavor is supported, + * <code>false</code> otherwise. + */ +public boolean +isDataFlavorSupported(DataFlavor flavor) +{ + for (int i = 0; i < supported_flavors.length; i++) + if (supported_flavors[i].equals(flavor)) + return(true); + + return(false); +} + +/*************************************************************************/ + +/** + * This method returns the data in the requested format. + * + * @param flavor The desired data flavor. + * + * @return The transferred data. + * + * @exception UnsupportedFlavorException If the specified flavor is not + * supported. + * @exception IOException If any other error occurs. + */ +public Object +getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, + IOException +{ + if (!isDataFlavorSupported(flavor)) + throw new UnsupportedFlavorException(flavor); + + if (DataFlavor.plainTextFlavor == flavor) + /* The behavior of this method for DataFlavor.plainTextFlavor and + equivalent DataFlavors is inconsistent with the definition of + DataFlavor.plainTextFlavor. We choose to do like Sun's implementation + and return a Reader instead of an InputString. */ + /* return(new StringBufferInputStream(data)); */ + return(new StringReader(data)); + else // DataFlavor.stringFlavor + return data; +} + +/*************************************************************************/ + +/** + * Called when ownership of the clipboard object is lost. + * + * @param clipboard The affected clipboard. + * @param contents The clipboard contents. + */ +public void +lostOwnership(Clipboard clipboard, Transferable contents) +{ + // FIXME: What does this do? +} + +} // class StringSelection + diff --git a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java new file mode 100644 index 00000000000..f6530f5117c --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java @@ -0,0 +1,169 @@ +/* SystemFlavorMap.java -- Maps between native flavor names and MIME types. + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class maps between native platform type names and DataFlavors. + * + * XXX - The current implementation does no mapping at all. + * + * @author Mark Wielaard (mark@klomp.org) + * + * @since 1.2 + */ +public final class SystemFlavorMap implements FlavorMap, FlavorTable +{ + /** + * The default (instance) flavor map. + */ + private static FlavorMap defaultFlavorMap; + + /** + * Private constructor. + */ + private SystemFlavorMap () + { + } + + /** + * Maps the specified <code>DataFlavor</code> objects to the native + * data type name. The returned <code>Map</code> has keys that are + * the data flavors and values that are strings. The returned map + * may be modified. This can be useful for implementing nested mappings. + * + * @param flavors An array of data flavors to map + * or null for all data flavors. + * + * @return A <code>Map</code> of native data types to data flavors. + */ + public Map getNativesForFlavors (DataFlavor[] flavors) + { + return new HashMap(); + } + + /** + * Maps the specified native type names to <code>DataFlavor</code>'s. + * The returned <code>Map</code> has keys that are strings and values + * that are <code>DataFlavor</code>'s. The returned map may be + * modified. This can be useful for implementing nested mappings. + * + * @param natives An array of native types to map + * or null for all native types. + * + * @return A <code>Map</code> of data flavors to native type names. + */ + public Map getFlavorsForNatives (String[] natives) + { + return new HashMap(); + } + + /** + * Returns the default (instance) (System)FlavorMap. + */ + public static FlavorMap getDefaultFlavorMap () + { + if (defaultFlavorMap == null) + defaultFlavorMap = new SystemFlavorMap (); + + return defaultFlavorMap; + } + + /** + * Returns the native type name for the given java mime type. + */ + public static String encodeJavaMIMEType (String mime) + { + return null; + } + + /** + * Returns the native type name for the given data flavor. + */ + public static String encodeDataFlavor (DataFlavor df) + { + return null; + } + + /** + * Returns true if the native type name can be represented as + * a java mime type. + */ + public static boolean isJavaMIMEType (String name) + { + return false; + } + + /** + * Returns the java mime type for the given the native type name. + */ + public static String decodeJavaMIMEType (String name) + { + return null; + } + + /** + * Returns the data flavor given the native type name + * or null when no such data flavor exists. + */ + public static DataFlavor decodeDataFlavor (String name) + throws ClassNotFoundException + { + String javaMIMEType = decodeJavaMIMEType (name); + + if (javaMIMEType != null) + return new DataFlavor (javaMIMEType); + else + return null; + } + + public List getFlavorsForNative (String nat) + { + throw new Error ("Not implemented"); + } + + public List getNativesForFlavor (DataFlavor flav) + { + throw new Error ("Not implemented"); + } + +} // class SystemFlavorMap diff --git a/libjava/classpath/java/awt/datatransfer/Transferable.java b/libjava/classpath/java/awt/datatransfer/Transferable.java new file mode 100644 index 00000000000..80753923618 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/Transferable.java @@ -0,0 +1,83 @@ +/* Transferable.java -- Data transfer source + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.IOException; + +/** + * This interface is implemented by classes that can transfer data. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Transferable +{ + /** + * This method returns a list of available data flavors for the data being + * transferred. The array returned will be sorted from most preferred + * flavor at the beginning to least preferred at the end. + * + * @return adA list of data flavors for this data + */ + DataFlavor[] getTransferDataFlavors(); + + /** + * Tests whether or not this data can be delivered in the specified data + * flavor. + * + * @param flavor the data flavor to test + * @return true if the data flavor is supported + */ + boolean isDataFlavorSupported(DataFlavor flavor); + + /** + * Returns the data in the specified <code>DataFlavor</code>. + * + * @param flavor the data flavor to return + * @return the data in the appropriate flavor + * @throws UnsupportedFlavorException if the flavor is not supported + * @throws IOException if the data is not available + * @see DataFlavor#getRepresentationClass + */ + Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException; + +} // interface Transferable + diff --git a/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java b/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java new file mode 100644 index 00000000000..1c1da0348c3 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java @@ -0,0 +1,65 @@ +/* UnsupportedFlavorException.java -- ata flavor is not valid + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * The data flavor requested is not supported for the transfer data. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Transferable#getTransferData(DataFlavor) + * @status updated to 1.4 + */ +public class UnsupportedFlavorException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5383814944251665601L; + + /** + * Initializes a new instance of <code>UnsupportedDataFlavor</code> + * for the specified data flavor. + * + * @param flavor the data flavor that is not supported + */ + public UnsupportedFlavorException(DataFlavor flavor) + { + super(flavor == null ? null : flavor.getHumanPresentableName()); + } +} // class UnsupportedFlavorException diff --git a/libjava/classpath/java/awt/datatransfer/package.html b/libjava/classpath/java/awt/datatransfer/package.html new file mode 100644 index 00000000000..5ab860cdc19 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.datatransfer package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.datatransfer</title></head> + +<body> +<p>Classes to represent different flavors of data for transferring native +and system types through for example a clipboard.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/dnd/Autoscroll.java b/libjava/classpath/java/awt/dnd/Autoscroll.java new file mode 100644 index 00000000000..ba4d4476cfc --- /dev/null +++ b/libjava/classpath/java/awt/dnd/Autoscroll.java @@ -0,0 +1,70 @@ +/* Autoscroll.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Insets; +import java.awt.Point; + +/** + * During DnD operations it is possible that a user may wish to drop the + * subject of the operation on a region of a scrollable GUI control that + * is not currently visible to the user. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * @status updated to 1.4 + */ +public interface Autoscroll +{ + /** + * This method returns the Insets describing the autoscrolling region or + * border relative to the geometry of the implementing Component + */ + Insets getAutoscrollInsets (); + + /** + * Notify the Component to autoscroll + * + * @param location A Point indicating the location of the cursor that + * triggered this operation + */ + void autoscroll (Point location); + +} // interface Autoscroll + diff --git a/libjava/classpath/java/awt/dnd/DnDConstants.java b/libjava/classpath/java/awt/dnd/DnDConstants.java new file mode 100644 index 00000000000..85c9c0528e6 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DnDConstants.java @@ -0,0 +1,77 @@ +/* DnDConstants.java -- constants for drag-and-drop operations + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * This class contains various constants used in drag-and-drop operations. + * Why it is not an interface is beyond me. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class DnDConstants +{ + /** No action takes place. */ + public static final int ACTION_NONE = 0; + + /** The copy action. */ + public static final int ACTION_COPY = 1; + + /** The move action. */ + public static final int ACTION_MOVE = 2; + + /** Either a copy or a move. */ + public static final int ACTION_COPY_OR_MOVE = 3; + + /** + * A link action. This does not copy or move, but creates a reference back + * to the original. However, since platforms differ on how a reference should + * behave, this action is not recommended for common use. + */ + public static final int ACTION_LINK = 1073741824; + + /** A synonym for {@link #ACTION_LINK}. */ + public static final int ACTION_REFERENCE = ACTION_LINK; + + private DnDConstants() + { + // Do nothing here. + } +} diff --git a/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java b/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java new file mode 100644 index 00000000000..d9f5ec00f1d --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java @@ -0,0 +1,74 @@ +/* DnDEventMulticaster.java -- helper class for listener chains in java.awt.dnd + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.AWTEventMulticaster; +import java.util.EventListener; + +class DnDEventMulticaster extends AWTEventMulticaster +{ + protected DnDEventMulticaster (EventListener a, EventListener b) + { + super (a, b); + } + + public static DragSourceListener add (DragSourceListener a, + DragSourceListener b) + { + return (DragSourceListener) addInternal (a, b); + } + + public static DragSourceMotionListener add (DragSourceMotionListener a, + DragSourceMotionListener b) + { + return (DragSourceMotionListener) addInternal (a, b); + } + + public static DragSourceListener remove (DragSourceListener a, + DragSourceListener b) + { + return (DragSourceListener) removeInternal (a, b); + } + + public static DragSourceMotionListener remove (DragSourceMotionListener a, + DragSourceMotionListener b) + { + return (DragSourceMotionListener) removeInternal (a, b); + } +} diff --git a/libjava/classpath/java/awt/dnd/DragGestureEvent.java b/libjava/classpath/java/awt/dnd/DragGestureEvent.java new file mode 100644 index 00000000000..9f2bc7c98b8 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureEvent.java @@ -0,0 +1,156 @@ +/* DragGestureEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.datatransfer.Transferable; +import java.awt.event.InputEvent; +import java.util.EventObject; +import java.util.Iterator; +import java.util.List; + +/** + * STUBBED + * @see DragGestureRecognizer + * @see DragGestureListener + * @see DragSource + * @since 1.2 + */ +public class DragGestureEvent extends EventObject +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 9080172649166731306L; + + private DragSource dragSource; + private Component component; + private final Point origin; + private final int action; + + public DragGestureEvent(DragGestureRecognizer dgr, int action, Point origin, + List events) + { + super(dgr); + if (origin == null || events == null) + throw new IllegalArgumentException(); + this.origin = origin; + this.action = action; + } + + public DragGestureRecognizer getSourceAsDragGestureRecognizer() + { + return (DragGestureRecognizer) source; + } + public Component getComponent() + { + return null; + } + public DragSource getDragSource() + { + return null; + } + public Point getDragOrigin() + { + return origin; + } + public Iterator iterator() + { + return null; + } + public Object[] toArray() + { + return null; + } + public Object[] toArray(Object[] array) + { + return array; + } + public int getDragAction() + { + return 0; + } + public InputEvent getTriggerEvent() + { + return null; + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Transferable trans) + { + startDrag(dragCursor, null, null, trans, null); + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Transferable trans, + DragSourceListener l) + { + startDrag(dragCursor, null, null, trans, l); + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener l) + { + } +} // class DragGestureEvent diff --git a/libjava/classpath/java/awt/dnd/DragGestureListener.java b/libjava/classpath/java/awt/dnd/DragGestureListener.java new file mode 100644 index 00000000000..e8befe80f19 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureListener.java @@ -0,0 +1,63 @@ +/* DragGestureListener.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This is a listener for starting a drag-and-drop gesture. Upon receiving + * notification, the implementor then starts the drag operation. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragGestureRecognizer + * @see DragGestureEvent + * @see DragSource + * @since 1.2 + * @status updated to 1.4 + */ +public interface DragGestureListener extends EventListener +{ + /** + * Called when the native platform notifies the virtual machine that a + * drag-and-drop has been initiated. + * + * @param e the event + */ + void dragGestureRecognized(DragGestureEvent e); +} // interface DragGestureListener diff --git a/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java new file mode 100644 index 00000000000..07b822e7a68 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java @@ -0,0 +1,179 @@ +/* DragGestureRecognizer.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Point; +import java.awt.event.InputEvent; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.TooManyListenersException; + +/** + * STUBBED + * @since 1.2 + */ +public abstract class DragGestureRecognizer implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8996673345831063337L; + + protected DragSource dragSource; + protected Component component; + protected transient DragGestureListener dragGestureListener; + protected int sourceActions; + protected ArrayList events = new ArrayList(); + + protected DragGestureRecognizer(DragSource ds, Component c, int sa, + DragGestureListener dgl) + { + if (ds == null) + throw new IllegalArgumentException(); + dragSource = ds; + component = c; + sourceActions = sa; + dragGestureListener = dgl; + } + + protected DragGestureRecognizer(DragSource ds, Component c, int sa) + { + this(ds, c, sa, null); + } + + protected DragGestureRecognizer(DragSource ds, Component c) + { + this(ds, c, 0, null); + } + + protected DragGestureRecognizer(DragSource ds) + { + this(ds, null, 0, null); + } + + protected abstract void registerListeners(); + + protected abstract void unregisterListeners(); + + public DragSource getDragSource() + { + return dragSource; + } + + public Component getComponent() + { + return component; + } + + public void setComponent(Component c) + { + component = c; + } + + public int getSourceActions() + { + return sourceActions; + } + + public void setSourceActions(int sa) + { + sourceActions = sa; + } + + public InputEvent getTriggerEvent() + { + return events.size() > 0 ? (InputEvent) events.get(0) : null; + } + + public void resetRecognizer() + { + throw new Error("not implemented"); + } + + /** + * Register a new DragGestureListener. + * + * @exception TooManyListenersException If a DragGestureListener has already + * been added. + */ + public void addDragGestureListener(DragGestureListener dgl) + throws TooManyListenersException + { + if (dragGestureListener != null) + throw new TooManyListenersException(); + dragGestureListener = dgl; + } + + public void removeDragGestureListener(DragGestureListener dgl) + { + if (dragGestureListener != dgl) + throw new IllegalArgumentException(); + dragGestureListener = null; + } + + protected void fireDragGestureRecognized(int dragAction, Point p) + { + throw new Error("not implemented"); + } + + protected void appendEvent(InputEvent e) + { + if (e == null) + return; + events.add(e); + } + + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + dragGestureListener = (DragGestureListener) s.readObject(); + } + + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(dragGestureListener instanceof Serializable + ? dragGestureListener : null); + } +} // class DragGestureRecognizer diff --git a/libjava/classpath/java/awt/dnd/DragSource.java b/libjava/classpath/java/awt/dnd/DragSource.java new file mode 100644 index 00000000000..13ffc961510 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSource.java @@ -0,0 +1,257 @@ +/* DragSource.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.datatransfer.FlavorMap; +import java.awt.datatransfer.SystemFlavorMap; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.io.Serializable; +import java.util.EventListener; + +/** + * @since 1.2 + */ +public class DragSource implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6236096958971414066L; + + public static final Cursor DefaultCopyDrop = null; + public static final Cursor DefaultMoveDrop = null; + public static final Cursor DefaultLinkDrop = null; + public static final Cursor DefaultCopyNoDrop = null; + public static final Cursor DefaultMoveNoDrop = null; + public static final Cursor DefaultLinkNoDrop = null; + + private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap (); + + private transient DragSourceListener dragSourceListener; + private transient DragSourceMotionListener dragSourceMotionListener; + + /** + * Initializes the drag source. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public DragSource() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + } + + /** + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public static DragSource getDefaultDragSource() + { + return null; + } + + public static boolean isDragImageSupported() + { + return false; + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener dsl, + FlavorMap map) + { + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Transferable trans, DragSourceListener dsl, + FlavorMap map) + { + startDrag(trigger, dragCursor, null, null, trans, dsl, map); + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener dsl) + { + startDrag(trigger, dragCursor, dragImage, imageOffset, trans, dsl, null); + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Transferable trans, DragSourceListener dsl) + { + startDrag(trigger, dragCursor, null, null, trans, dsl, null); + } + + /** + * Creates the DragSourceContext to handle this drag. + * + * @exception IllegalArgumentException FIXME + * @exception NullPointerException If dscp, dgl, dragImage or t is null. + */ + protected DragSourceContext + createDragSourceContext(DragSourceContextPeer peer, DragGestureEvent dge, + Cursor cursor, Image image, Point offset, + Transferable t, DragSourceListener dsl) + { + return null; + } + + public FlavorMap getFlavorMap() + { + return flavorMap; + } + + public DragGestureRecognizer + createDragGestureRecognizer(Class recognizer, Component c, int actions, + DragGestureListener dgl) + { + return Toolkit.getDefaultToolkit () + .createDragGestureRecognizer (recognizer, this, c, actions, + dgl); + } + + public DragGestureRecognizer + createDefaultDragGestureRecognizer(Component c, int actions, + DragGestureListener dgl) + { + return createDragGestureRecognizer (MouseDragGestureRecognizer.class, c, + actions, dgl); + } + + /** + * @since 1.4 + */ + public void addDragSourceListener(DragSourceListener l) + { + DnDEventMulticaster.add (dragSourceListener, l); + } + + /** + * @since 1.4 + */ + public void removeDragSourceListener(DragSourceListener l) + { + DnDEventMulticaster.remove (dragSourceListener, l); + } + + /** + * @since 1.4 + */ + public DragSourceListener[] getDragSourceListeners() + { + return (DragSourceListener[]) getListeners (DragSourceListener.class); + } + + /** + * @since 1.4 + */ + public void addDragSourceMotionListener(DragSourceMotionListener l) + { + DnDEventMulticaster.add (dragSourceMotionListener, l); + } + + /** + * @since 1.4 + */ + public void removeDragSourceMotionListener(DragSourceMotionListener l) + { + DnDEventMulticaster.remove (dragSourceMotionListener, l); + } + + /** + * @since 1.4 + */ + public DragSourceMotionListener[] getDragSourceMotionListeners () + { + return (DragSourceMotionListener[]) getListeners + (DragSourceMotionListener.class); + } + + /** + * @since 1.4 + */ + public EventListener[] getListeners (Class listenerType) + { + if (listenerType == DragSourceListener.class) + return DnDEventMulticaster.getListeners (dragSourceListener, + listenerType); + + if (listenerType == DragSourceMotionListener.class) + return DnDEventMulticaster.getListeners (dragSourceMotionListener, + listenerType); + + // Return an empty EventListener array. + return new EventListener [0]; + } +} // class DragSource diff --git a/libjava/classpath/java/awt/dnd/DragSourceAdapter.java b/libjava/classpath/java/awt/dnd/DragSourceAdapter.java new file mode 100644 index 00000000000..90d9a698347 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceAdapter.java @@ -0,0 +1,126 @@ +/* DragSourceAdapter.java -- drag-and-drop listener adapter + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * This class implements <code>DragSourceListener</code> and + * <code>DragSourceMotionListener</code>, and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of these interfaces to extend this class and override only the + * desired methods. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragSourceEvent + * @see DragSourceListener + * @see DragSourceMotionListener + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class DragSourceAdapter + implements DragSourceListener, DragSourceMotionListener +{ + /** + * Default constructor. + */ + public DragSourceAdapter() + { + } + + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the event + */ + public void dragEnter(DragSourceDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the event + */ + public void dragOver(DragSourceDragEvent e) + { + } + + /** + * Called whenever the mouse is moved during a drag-and-drop operation. + * + * @param e the event + */ + public void dragMouseMoved(DragSourceDragEvent e) + { + } + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the event + */ + public void dropActionChanged(DragSourceDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the event + */ + public void dragExit(DragSourceEvent e) + { + } + + /** + * Called when the drag and drop operation is complete. After this event, + * <code>getDropSuccess</code> of the event is valid, and + * <code>getDropAction</code> holds the action requested by the drop site. + * Furthermore, the <code>DragSourceContext</code> is invalidated. + * + * @param e the event + */ + public void dragDropEnd(DragSourceDropEvent e) + { + } +} // class DragSourceAdapter diff --git a/libjava/classpath/java/awt/dnd/DragSourceContext.java b/libjava/classpath/java/awt/dnd/DragSourceContext.java new file mode 100644 index 00000000000..2cf0d6d0bc9 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceContext.java @@ -0,0 +1,200 @@ +/* DragSourceContext.java -- + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.io.Serializable; +import java.util.TooManyListenersException; + +/** + * @since 1.2 + */ +public class DragSourceContext + implements DragSourceListener, DragSourceMotionListener, Serializable +{ + /** + * Compatible with JDK 1.2+ + */ + static final long serialVersionUID = -115407898692194719L; + + protected static final int DEFAULT = 0; + protected static final int ENTER = 1; + protected static final int OVER = 2; + protected static final int CHANGED = 3; + + private DragSourceContextPeer peer; + private Cursor cursor; + private Transferable transferable; + private DragGestureEvent trigger; + private DragSourceListener dragSourceListener; + private boolean useCustomCursor; // FIXME: currently unused but needed for serialization. + private int sourceActions; // FIXME: currently unused but needed for serialization. + private Image image; + private Point offset; + + /** + * Initializes a drag source context. + * + * @exception IllegalArgumentException If Component or DragSource of trigger + * are null, the drag action for the trigger event is DnDConstants.ACTION_NONE + * or if the source actions for the DragGestureRecognizer associated with the + * trigger event are equal to DnDConstants.ACTION_NONE. + * @exception NullPointerException If peer or trigger is null. + */ + public DragSourceContext (DragSourceContextPeer peer, + DragGestureEvent trigger, Cursor cursor, + Image image, Point offset, Transferable trans, + DragSourceListener dsl) + { + if (peer == null + || trigger == null) + throw new NullPointerException (); + + if (trigger.getComponent () == null + || trigger.getDragSource () == null + || trigger.getDragAction () == DnDConstants.ACTION_NONE + || trigger.getSourceAsDragGestureRecognizer () + .getSourceActions () == DnDConstants.ACTION_NONE) + throw new IllegalArgumentException (); + + this.peer = peer; + this.trigger = trigger; + this.cursor = cursor; + this.image = image; + this.offset = offset; + this.transferable = trans; + this.dragSourceListener = dsl; + + throw new Error ("not implemented"); + } + + public DragSource getDragSource() + { + return trigger.getDragSource (); + } + + public Component getComponent() + { + return trigger.getComponent (); + } + + public DragGestureEvent getTrigger() + { + return trigger; + } + + public int getSourceActions() + { + return trigger.getSourceAsDragGestureRecognizer ().getSourceActions (); + } + + public void setCursor (Cursor cursor) + { + this.cursor = cursor; + // FIXME: Check if we need to do more here + } + + public Cursor getCursor() + { + return cursor; + } + + /** + * Adds a <code>DragSourceListener</code>. + * + * @exception TooManyListenersException If a <code>DragSourceListener</code> + * has already been added. + */ + public void addDragSourceListener (DragSourceListener dsl) + throws TooManyListenersException + { + if (dragSourceListener != null) + throw new TooManyListenersException (); + + dragSourceListener = dsl; + } + + public void removeDragSourceListener (DragSourceListener dsl) + { + if (dragSourceListener == dsl) + dragSourceListener = null; + } + + public void transferablesFlavorsChanged() + { + } + + public void dragEnter(DragSourceDragEvent e) + { + } + + public void dragOver(DragSourceDragEvent e) + { + } + + public void dragExit(DragSourceEvent e) + { + } + + public void dropActionChanged(DragSourceDragEvent e) + { + } + + public void dragDropEnd(DragSourceDropEvent e) + { + } + + public void dragMouseMoved(DragSourceDragEvent e) + { + } + + public Transferable getTransferable() + { + return transferable; + } + + protected void updateCurrentCursor(int dropOp, int targetAct, int status) + { + } +} // class DragSourceContext diff --git a/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java b/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java new file mode 100644 index 00000000000..511700b616d --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java @@ -0,0 +1,102 @@ +/* DragSourceDragEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import gnu.java.awt.EventModifier; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class DragSourceDragEvent extends DragSourceEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = 481346297933902471L; + + private final int dropAction; + private final int targetActions; + private final int gestureModifiers; + + public DragSourceDragEvent(DragSourceContext context, int dropAction, + int actions, int modifiers) + { + super(context); + this.dropAction = dropAction; + targetActions = actions; + gestureModifiers = EventModifier.extend(modifiers); + } + + public DragSourceDragEvent(DragSourceContext context, int dropAction, + int actions, int modifiers, int x, int y) + { + super(context, x, y); + this.dropAction = dropAction; + targetActions = actions; + gestureModifiers = EventModifier.extend(modifiers); + } + + public int getTargetActions() + { + return targetActions; + } + + public int getGestureModifiers() + { + return EventModifier.revert(gestureModifiers); + } + + public int getGestureModifiersEx() + { + return gestureModifiers; + } + + public int getUserAction() + { + return dropAction; + } + + public int getDropAction() + { + return (dropAction + & targetActions + & ((DragSourceContext) source).getSourceActions()); + } +} // class DragSourceDragEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java b/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java new file mode 100644 index 00000000000..7621262d839 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java @@ -0,0 +1,89 @@ +/* DragSourceDragEvent.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * + * Written using JDK 1.4.1 Online API + * Status: JDK 1.4 complete + */ +public class DragSourceDropEvent extends DragSourceEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -5571321229470821891L; + + private final int dropAction; + private final boolean dropSuccess; + + public DragSourceDropEvent (DragSourceContext context) + { + super (context); + this.dropAction = 0; + this.dropSuccess = false; + } + + public DragSourceDropEvent (DragSourceContext context, int dropAction, + boolean dropSuccess) + { + super (context); + this.dropAction = dropAction; + this.dropSuccess = dropSuccess; + } + + public DragSourceDropEvent (DragSourceContext context, int dropAction, + boolean dropSuccess, int x, int y) + { + super (context, x, y); + this.dropAction = dropAction; + this.dropSuccess = dropSuccess; + } + + public int getDropAction() + { + return dropAction & ((DragSourceContext) source).getSourceActions(); + } + + public boolean getDropSuccess() + { + return dropSuccess; + } +} // class DragSourceDropEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceEvent.java b/libjava/classpath/java/awt/dnd/DragSourceEvent.java new file mode 100644 index 00000000000..c5cd42a4e2b --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceEvent.java @@ -0,0 +1,93 @@ +/* DragSourceEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Point; +import java.util.EventObject; + +/** + * @since 1.2 + */ +public class DragSourceEvent extends EventObject +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -763287114604032641L; + + private final boolean locationSpecified; + private final int x; + private final int y; + + public DragSourceEvent(DragSourceContext context) + { + super(context); + locationSpecified = false; + x = 0; + y = 0; + } + + public DragSourceEvent(DragSourceContext context, int x, int y) + { + super(context); + locationSpecified = true; + this.x = x; + this.y = y; + } + + public DragSourceContext getDragSourceContext() + { + return (DragSourceContext) source; + } + + public Point getLocation() + { + return locationSpecified ? new Point(x, y) : null; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } +} // class DragSourceEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceListener.java b/libjava/classpath/java/awt/dnd/DragSourceListener.java new file mode 100644 index 00000000000..aac6e94ebe7 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceListener.java @@ -0,0 +1,97 @@ +/* DragSourceListener.java -- listen to events during the drag + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This class allows an object to listen for drag and drop events. It can + * be used to provide appropriate feedback for "drag over" actions. You can + * also use a <code>DragSourceAdapter</code> to filter the events you are + * interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public interface DragSourceListener extends EventListener +{ + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the drag source drag event + */ + void dragEnter(DragSourceDragEvent e); + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the drag source drag event + */ + void dragOver(DragSourceDragEvent e); + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the drag source drag event + */ + void dropActionChanged(DragSourceDragEvent e); + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the drag source drag event + */ + void dragExit(DragSourceEvent e); + + /** + * Called when the drag and drop operation is complete. After this event, + * <code>getDropSuccess</code> of the event is valid, and + * <code>getDropAction</code> holds the action requested by the drop site. + * Furthermore, the <code>DragSourceContext</code> is invalidated. + * + * @param e the drag source drag event + */ + void dragDropEnd(DragSourceDropEvent e); +} // interface DragSourceListener diff --git a/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java b/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java new file mode 100644 index 00000000000..5d04c227100 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java @@ -0,0 +1,64 @@ +/* DragSourceMotionListener.java -- tracks motion in the drag source + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This is a listener for mouse motion in the drag source before the drop + * event occurs. You can also use a <code>DragSourceAdapter</code> to filter + * the events you are interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragSourceDragEvent + * @see DragSource + * @see DragSourceListener + * @see DragSourceAdapter + * @since 1.4 + * @status updated to 1.4 + */ +public interface DragSourceMotionListener extends EventListener +{ + /** + * Called whenever the mouse is moved during a drag-and-drop operation. + * + * @param e the event + */ + void dragMouseMoved(DragSourceDragEvent e); +} // interface DragSourceMotionListener diff --git a/libjava/classpath/java/awt/dnd/DropTarget.java b/libjava/classpath/java/awt/dnd/DropTarget.java new file mode 100644 index 00000000000..9fd7ef896fb --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTarget.java @@ -0,0 +1,293 @@ +/* DropTarget.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Point; +import java.awt.datatransfer.FlavorMap; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.Serializable; +import java.util.EventListener; +import java.util.TooManyListenersException; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class DropTarget + implements DropTargetListener, EventListener, Serializable +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -6283860791671019047L; + + /** @specnote According to the online documentation, this is + * protected, but in reality it is public. */ + public static class DropTargetAutoScroller + implements ActionListener + { + private Component component; + private Point point; + + protected DropTargetAutoScroller (Component c, Point p) + { + component = c; + point = p; + } + + protected void updateLocation (Point newLocn) + { + point = newLocn; + } + + protected void stop () + { + } + + public void actionPerformed (ActionEvent e) + { + } + } + + private Component component; + private FlavorMap flavorMap; + private int actions; + private DropTargetContext dropTargetContext; + private DropTargetListener dropTargetListener; + private boolean active = true; + + /** + * Creates a <code>DropTarget</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget () + { + this (null, 0, null, true, null); + } + + /** + * Creates a <code>DropTarget</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, DropTargetListener dtl) + { + this (c, 0, dtl, true, null); + } + + /** + * Creates a <code>DropTarget</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl) + { + this (c, i, dtl, true, null); + } + + /** + * Creates a <code>DropTarget</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl, boolean b) + { + this (c, i, dtl, b, null); + } + + /** + * Creates a <code>DropTarget</code> object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl, boolean b, + FlavorMap fm) + { + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + component = c; + actions = i; + dropTargetListener = dtl; + flavorMap = fm; + + setActive (b); + } + + /** + * Sets the component associated with this drop target object. + */ + public void setComponent (Component c) + { + component = c; + } + + /** + * Returns the component associated with this drop target object. + */ + public Component getComponent () + { + return component; + } + + /** + * Sets the default actions. + */ + public void setDefaultActions (int ops) + { + actions = ops; + } + + /** + * Returns the default actions. + */ + public int getDefaultActions () + { + return actions; + } + + public void setActive (boolean active) + { + this.active = active; + } + + public boolean isActive() + { + return active; + } + + /** + * Adds a new <code>DropTargetListener</code>. + * + * @exception TooManyListenersException Sun's JDK does not, despite + * documentation, throw this exception here when you install an additional + * <code>DropTargetListener</code>. So to be compatible, we do the same + * thing. + */ + public void addDropTargetListener (DropTargetListener dtl) + throws TooManyListenersException + { + dropTargetListener = dtl; + } + + public void removeDropTargetListener(DropTargetListener dtl) + { + // FIXME: Do we need to do something with dtl ? + dropTargetListener = null; + } + + public void dragEnter(DropTargetDragEvent dtde) + { + } + + public void dragOver(DropTargetDragEvent dtde) + { + } + + public void dropActionChanged(DropTargetDragEvent dtde) + { + } + + public void dragExit(DropTargetEvent dte) + { + } + + public void drop(DropTargetDropEvent dtde) + { + } + + public FlavorMap getFlavorMap() + { + return flavorMap; + } + + public void setFlavorMap(FlavorMap fm) + { + flavorMap = fm; + } + + public void addNotify(java.awt.peer.ComponentPeer peer) + { + } + + public void removeNotify(java.awt.peer.ComponentPeer peer) + { + } + + public DropTargetContext getDropTargetContext() + { + if (dropTargetContext == null) + dropTargetContext = createDropTargetContext (); + + return dropTargetContext; + } + + protected DropTargetContext createDropTargetContext() + { + return new DropTargetContext (this); + } + + protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller + (Component c, Point p) + { + return new DropTarget.DropTargetAutoScroller (c, p); + } + + protected void initializeAutoscrolling(Point p) + { + } + + protected void updateAutoscroll(Point dragCursorLocn) + { + } + + protected void clearAutoscroll() + { + } +} // class DropTarget diff --git a/libjava/classpath/java/awt/dnd/DropTargetAdapter.java b/libjava/classpath/java/awt/dnd/DropTargetAdapter.java new file mode 100644 index 00000000000..13c6b9f4b6d --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetAdapter.java @@ -0,0 +1,100 @@ +/* DragSourceAdapter.java -- drag-and-drop listener adapter + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +/** + * This class implements <code>DropTargetListener</code>, and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of these interfaces to extend this class and override only the + * desired methods. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class DropTargetAdapter + implements DropTargetListener +{ + /** + * Default constructor. + */ + public DropTargetAdapter() + { + } + + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the event + */ + public void dragEnter (DropTargetDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the event + */ + public void dragOver (DropTargetDragEvent e) + { + } + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the event + */ + public void dropActionChanged (DropTargetDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the event + */ + public void dragExit(DropTargetEvent e) + { + } +} // class DropTargetAdapter diff --git a/libjava/classpath/java/awt/dnd/DropTargetContext.java b/libjava/classpath/java/awt/dnd/DropTargetContext.java new file mode 100644 index 00000000000..d1fb66e6f35 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetContext.java @@ -0,0 +1,188 @@ +/* DropTargetContext.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + */ +public class DropTargetContext implements Serializable +{ + static final long serialVersionUID = -634158968993743371L; + + /** @specnote According to the online documentation, this is + * protected, but in reality it is public. */ + public class TransferableProxy implements Transferable + { + protected boolean isLocal; + protected Transferable transferable; + + TransferableProxy (Transferable t, boolean local) + { + this.transferable = t; + this.isLocal = local; + } + + public DataFlavor[] getTransferDataFlavors () + { + return transferable.getTransferDataFlavors (); + } + + public boolean isDataFlavorSupported (DataFlavor flavor) + { + return transferable.isDataFlavorSupported (flavor); + } + + public Object getTransferData (DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + return transferable.getTransferData (flavor); + } + } + + private DropTarget dropTarget; + private int targetActions; + private java.awt.dnd.peer.DropTargetContextPeer dtcp; + + // package private + DropTargetContext (DropTarget dropTarget) + { + this.dropTarget = dropTarget; + } + + public DropTarget getDropTarget () + { + return dropTarget; + } + + public Component getComponent () + { + return dropTarget.getComponent (); + } + + public void addNotify (java.awt.dnd.peer.DropTargetContextPeer dtcp) + { + this.dtcp = dtcp; + } + + public void removeNotify () + { + this.dtcp = null; + } + + protected void setTargetActions (int actions) + { + targetActions = actions; + } + + protected int getTargetActions() + { + return targetActions; + } + + /** + * Signals that the drop is completed. + * + * @exception InvalidDnDOperationException If a drop is not outstanding. + */ + public void dropComplete (boolean success) + { + // FIXME: implement this + } + + protected void acceptDrag (int dragOperation) + { + // FIXME: implement this + } + + protected void rejectDrag () + { + // FIXME: implement this + } + + protected void acceptDrop (int dropOperation) + { + // FIXME: implement this + } + + protected void rejectDrop () + { + // FIXME: implement this + } + + protected DataFlavor[] getCurrentDataFlavors () + { + // FIXME: implement this + return null; + } + + protected List getCurrentDataFlavorsAsList () + { + return Arrays.asList (getCurrentDataFlavors ()); + } + + protected boolean isDataFlavorSupported (DataFlavor flavor) + { + return getCurrentDataFlavorsAsList ().contains (flavor); + } + + /** + * Return the <code>Transferable</code> operandof this operation. + * + * @exception InvalidDnDOperationException If a drag is not outstanding. + */ + protected Transferable getTransferable() throws InvalidDnDOperationException + { + // FIXME: implement this + return null; + } + + protected Transferable createTransferableProxy(Transferable t, boolean local) + { + return new TransferableProxy (t, local); + } +} // class DropTargetContext diff --git a/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java new file mode 100644 index 00000000000..6cdc3a292be --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java @@ -0,0 +1,140 @@ +/* DropTargetDragEvent.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.util.List; + +/** + * @since 1.2 + */ +public class DropTargetDragEvent extends DropTargetEvent +{ + /** + * Compatible with 1.2+ + */ + private static final long serialVersionUID = -8422265619058953682L; + + private final int dropAction; + private final int srcActions; + private final Point location; + + /** + * Initializes a <code>DropTargetDragEvent</code>. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * srcActions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDragEvent (DropTargetContext context, Point location, + int dropAction, int srcActions) + { + super (context); + + if (location == null) + throw new NullPointerException (); + + if (context == null) + throw new IllegalArgumentException (); + + if (dropAction != DnDConstants.ACTION_NONE + && dropAction != DnDConstants.ACTION_COPY + && dropAction != DnDConstants.ACTION_MOVE + && dropAction != DnDConstants.ACTION_COPY_OR_MOVE + && dropAction != DnDConstants.ACTION_LINK + && dropAction != DnDConstants.ACTION_REFERENCE) + throw new IllegalArgumentException (); + + int srcActionsMask = DnDConstants.ACTION_NONE + | DnDConstants.ACTION_COPY + | DnDConstants.ACTION_MOVE + | DnDConstants.ACTION_COPY_OR_MOVE + | DnDConstants.ACTION_LINK + | DnDConstants.ACTION_REFERENCE; + + if (~(srcActions ^ srcActionsMask) != 0) + throw new IllegalArgumentException (); + + this.dropAction = dropAction; + this.srcActions = srcActions; + this.location = location; + } + + public void acceptDrag (int dragOperation) + { + context.acceptDrag (dragOperation); + } + + public DataFlavor[] getCurrentDataFlavors () + { + return context.getCurrentDataFlavors (); + } + + public List getCurrentDataFlavorsAsList () + { + return context.getCurrentDataFlavorsAsList (); + } + + public int getDropAction() + { + return 0; + //return dropAction & ((DropTargetContext) source).getTargetActions(); + } + + public Point getLocation () + { + return location; + } + + public int getSourceActions () + { + return srcActions; + } + + public boolean isDataFlavorSupported (DataFlavor df) + { + return context.isDataFlavorSupported (df); + } + + public void rejectDrag () + { + context.rejectDrag (); + } +} // class DropTargetDragEvent diff --git a/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java new file mode 100644 index 00000000000..0c0777f78d0 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java @@ -0,0 +1,170 @@ +/* DropTargetDropEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.util.List; + +/** + * @since 1.2 + */ +public class DropTargetDropEvent extends DropTargetEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -1721911170440459322L; + + private final int dropAction; + private final int actions; + private final Point location; + private final boolean isLocalTx; + + /** + * Initializes a <code>DropTargetDropEvent</code>. By default this constructor + * assumes that the target is not int same JVM. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * actions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDropEvent (DropTargetContext dtc, Point location, + int dropAction, int actions) + { + this (dtc, location, dropAction, actions, false); + } + + /** + * Initializes a <code>DropTargetDropEvent</code>. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * actions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDropEvent (DropTargetContext dtc, Point location, + int dropAction, int actions, boolean isLocalTx) + { + super (dtc); + + if (location == null) + throw new NullPointerException (); + + if (dtc == null) + throw new IllegalArgumentException (); + + if (dropAction != DnDConstants.ACTION_NONE + && dropAction != DnDConstants.ACTION_COPY + && dropAction != DnDConstants.ACTION_MOVE + && dropAction != DnDConstants.ACTION_COPY_OR_MOVE + && dropAction != DnDConstants.ACTION_LINK + && dropAction != DnDConstants.ACTION_REFERENCE) + throw new IllegalArgumentException (); + + int actionsMask = DnDConstants.ACTION_NONE + | DnDConstants.ACTION_COPY + | DnDConstants.ACTION_MOVE + | DnDConstants.ACTION_COPY_OR_MOVE + | DnDConstants.ACTION_LINK + | DnDConstants.ACTION_REFERENCE; + + if (~(actions ^ actionsMask) != 0) + throw new IllegalArgumentException (); + + this.dropAction = dropAction; + this.actions = actions; + this.location = location; + this.isLocalTx = isLocalTx; + } + + public Point getLocation () + { + return location; + } + + public DataFlavor[] getCurrentDataFlavors () + { + return context.getCurrentDataFlavors (); + } + + public List getCurrentDataFlavorsAsList () + { + return context.getCurrentDataFlavorsAsList (); + } + + public boolean isDataFlavorSupported (DataFlavor flavor) + { + return context.isDataFlavorSupported (flavor); + } + + public int getSourceActions () + { + return actions; + } + + public int getDropAction () + { + return dropAction; + } + + public Transferable getTransferable () + { + return context.getTransferable (); + } + + public void acceptDrop (int dropAction) + { + context.acceptDrop (dropAction); + } + + public void rejectDrop () + { + context.rejectDrop (); + } + + public void dropComplete (boolean success) + { + // FIXME: implement this + } + + public boolean isLocalTransfer() + { + return isLocalTx; + } +} // class DropTargetDropEvent diff --git a/libjava/classpath/java/awt/dnd/DropTargetEvent.java b/libjava/classpath/java/awt/dnd/DropTargetEvent.java new file mode 100644 index 00000000000..56a4d481a16 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetEvent.java @@ -0,0 +1,56 @@ +/* DropTarget.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.util.EventObject; + +public class DropTargetEvent extends EventObject +{ + protected DropTargetContext context; + + public DropTargetEvent (DropTargetContext context) + { + super (context); + this.context = context; + } + + public DropTargetContext getDropTargetContext () + { + return context; + } +} diff --git a/libjava/classpath/java/awt/dnd/DropTargetListener.java b/libjava/classpath/java/awt/dnd/DropTargetListener.java new file mode 100644 index 00000000000..ceb839bac27 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetListener.java @@ -0,0 +1,89 @@ +/* DropTargetListener.java -- listen to events during the drop + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * @status updated to 1.4 + */ +public interface DropTargetListener extends EventListener +{ + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the drag source drag event + */ + void dragEnter (DropTargetDragEvent e); + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the drag source drag event + */ + void dragOver (DropTargetDragEvent e); + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the drag source drag event + */ + void dropActionChanged (DropTargetDragEvent e); + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the drag source drag event + */ + void dragExit (DropTargetEvent e); + + /** + * Called when the drag operation has terminated with a drop. + * + * @param e the drag source drag event + */ + void drop (DropTargetDropEvent e); +} // interface DropTargetListener diff --git a/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java new file mode 100644 index 00000000000..2fd9767e03d --- /dev/null +++ b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java @@ -0,0 +1,73 @@ +/* InvalidDnDOperationException.java -- thrown when drag-and-drop fails + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * Thrown when a method in the java.awt.dnd package is unable to perform a + * requested operation, usually because the underlying DnD system is in the + * wrong state. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public class InvalidDnDOperationException extends IllegalStateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -6062568741193956678L; + + /** + * Create an exception without a message. + */ + public InvalidDnDOperationException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public InvalidDnDOperationException(String s) + { + super(s); + } +} // class InvalidDnDOperationException diff --git a/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java b/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java new file mode 100644 index 00000000000..9a2a7bc4c79 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java @@ -0,0 +1,131 @@ +/* MouseDragGestureRecognizer.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +public abstract class MouseDragGestureRecognizer + extends DragGestureRecognizer + implements MouseListener, MouseMotionListener +{ + /** + * Creates a <code>MouseDragGestureRecognizer</code> object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c, int act, + DragGestureListener dgl) + { + super (ds, c, act, dgl); + } + + /** + * Creates a <code>MouseDragGestureRecognizer</code> object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c, int act) + { + super (ds, c, act); + } + + /** + * Creates a <code>MouseDragGestureRecognizer</code> object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c) + { + super (ds, c); + } + + /** + * Creates a <code>MouseDragGestureRecognizer</code> object. + */ + protected MouseDragGestureRecognizer (DragSource ds) + { + super (ds); + } + + protected void registerListeners () + { + component.addMouseListener (this); + component.addMouseMotionListener (this); + } + + protected void unregisterListeners () + { + component.removeMouseListener (this); + component.removeMouseMotionListener (this); + } + + public void mouseClicked (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mousePressed (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseReleased (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseEntered (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseExited (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseDragged (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseMoved (MouseEvent e) + { + // Do nothing in here by default. + } +} // class MouseDragGestureRecognizer diff --git a/libjava/classpath/java/awt/dnd/package.html b/libjava/classpath/java/awt/dnd/package.html new file mode 100644 index 00000000000..d1ae5215cb4 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.dnd package. + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.dnd</title></head> + +<body> +<p>Events and listeners for drag and drop sources and targets.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java b/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java new file mode 100644 index 00000000000..8c134b623a7 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java @@ -0,0 +1,57 @@ +/* DragSourceContextPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd.peer; + +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.dnd.DragSourceContext; +import java.awt.dnd.InvalidDnDOperationException; + +/** + * STUBBED + */ +public interface DragSourceContextPeer +{ + void startDrag(DragSourceContext context, Cursor c, Image i, Point p) + throws InvalidDnDOperationException; + Cursor getCursor(); + void setCursor(Cursor c) throws InvalidDnDOperationException; + void transferablesFlavorsChanged(); +} // interface DragSourceContextPeer diff --git a/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java b/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java new file mode 100644 index 00000000000..6eae29b3810 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java @@ -0,0 +1,68 @@ +/* DropTargetContextPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd.peer; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; + + +/** + * Used to control state of recipient protocol from the + * <code>DropTargetListener</code>. Occurs when a <code>Component</code> + * with an associated <code>DropTarget</code> and visible geometry is first + * intersected by a logical cursor. + * + * @author Michael Koch (konqueror@gmx.de) + */ +public interface DropTargetContextPeer +{ + void setTargetActions(int actions); + int getTargetActions(); + DropTarget getDropTarget(); + DataFlavor[] getTransferDataFlavors(); + Transferable getTransferable() throws InvalidDnDOperationException; + boolean isTransferableJVMLocal(); + void acceptDrag(int dragAction); + void rejectDrag(); + void acceptDrop(int dropAction); + void rejectDrop(); + void dropComplete(boolean success); +} diff --git a/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java b/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java new file mode 100644 index 00000000000..ec17cbe4be9 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java @@ -0,0 +1,48 @@ +/* DropTargetPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd.peer; + +import java.awt.dnd.DropTarget; + +/** + */ +public interface DropTargetPeer +{ + void addDropTarget (DropTarget target); + void removeDropTarget (DropTarget target); +} // interface DropTargetContextPeer diff --git a/libjava/classpath/java/awt/dnd/peer/package.html b/libjava/classpath/java/awt/dnd/peer/package.html new file mode 100644 index 00000000000..52ec19cb4f0 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.dnd.peer package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.dnd.peer</title></head> + +<body> +<p>Interfaces for using native interface components.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/event/AWTEventListener.java b/libjava/classpath/java/awt/event/AWTEventListener.java new file mode 100644 index 00000000000..079a91dd4c9 --- /dev/null +++ b/libjava/classpath/java/awt/event/AWTEventListener.java @@ -0,0 +1,65 @@ +/* AWTEventListener.java -- listen for all events in the AWT system + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.util.EventListener; + +/** + * This listener is for classes that need to listen to all events in the AWT + * system. In general, this should not be used except for classes like + * javax.accessibility or by event recorders. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AWTEvent + * @see Toolkit#addAWTEventListener(AWTEventListener, long) + * @see Toolkit#removeAWTEventListener(AWTEventListener) + * @since 1.2 + * @status updated to 1.4 + */ +public interface AWTEventListener extends EventListener +{ + /** + * This method is called when any event in the AWT system is dispatched. + * + * @param event the AWTEvent that was dispatched + */ + void eventDispatched(AWTEvent event); +} // interface AWTEventListener diff --git a/libjava/classpath/java/awt/event/AWTEventListenerProxy.java b/libjava/classpath/java/awt/event/AWTEventListenerProxy.java new file mode 100644 index 00000000000..3d9958b1abd --- /dev/null +++ b/libjava/classpath/java/awt/event/AWTEventListenerProxy.java @@ -0,0 +1,155 @@ +/* AWTEventListenerProxy.java -- wrapper/filter for AWTEventListener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.util.EventListenerProxy; + +/** + * This class allows adding an AWTEventListener which only pays attention to + * a specific event mask. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Toolkit + * @see EventListenerProxy + * @since 1.4 + * @status updated to 1.4 + */ +public class AWTEventListenerProxy extends EventListenerProxy + implements AWTEventListener +{ + /** The event mask. */ + private final long mask; + + /** + * Construct an AWT Event Listener which only listens to events in the given + * mask, passing the work on to the real listener. + * + * @param eventMask the mask of events to listen to + * @param listener the wrapped listener + */ + public AWTEventListenerProxy(long eventMask, AWTEventListener listener) + { + super(listener); + mask = eventMask; + } + + /** + * Forwards events on to the delegate if they meet the event mask. + * + * @param event the property change event to filter + * @throws NullPointerException if the delegate this was created with is null + */ + public void eventDispatched(AWTEvent event) + { + int id = event == null ? 0 : event.getID(); + if (((mask & AWTEvent.ACTION_EVENT_MASK) != 0 + && event instanceof ActionEvent) + || ((mask & AWTEvent.ADJUSTMENT_EVENT_MASK) != 0 + && event instanceof AdjustmentEvent) + || ((mask & AWTEvent.COMPONENT_EVENT_MASK) != 0 + && event instanceof ComponentEvent + && (id >= ComponentEvent.COMPONENT_FIRST + && id <= ComponentEvent.COMPONENT_LAST)) + || ((mask & AWTEvent.CONTAINER_EVENT_MASK) != 0 + && event instanceof ContainerEvent) + || ((mask & AWTEvent.FOCUS_EVENT_MASK) != 0 + && event instanceof FocusEvent) + || ((mask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0 + && event instanceof HierarchyEvent + && (id == HierarchyEvent.ANCESTOR_MOVED + || id == HierarchyEvent.ANCESTOR_RESIZED)) + || ((mask & AWTEvent.HIERARCHY_EVENT_MASK) != 0 + && event instanceof HierarchyEvent + && id == HierarchyEvent.HIERARCHY_CHANGED) + || ((mask & AWTEvent.INPUT_METHOD_EVENT_MASK) != 0 + && event instanceof InputMethodEvent) + || ((mask & AWTEvent.INVOCATION_EVENT_MASK) != 0 + && event instanceof InvocationEvent) + || ((mask & AWTEvent.ITEM_EVENT_MASK) != 0 + && event instanceof ItemEvent) + || ((mask & AWTEvent.KEY_EVENT_MASK) != 0 + && event instanceof KeyEvent) + || ((mask & AWTEvent.MOUSE_EVENT_MASK) != 0 + && event instanceof MouseEvent + && (id == MouseEvent.MOUSE_PRESSED + || id == MouseEvent.MOUSE_RELEASED + || id == MouseEvent.MOUSE_CLICKED + || id == MouseEvent.MOUSE_ENTERED + || id == MouseEvent.MOUSE_EXITED)) + || ((mask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0 + && event instanceof MouseEvent + && (id == MouseEvent.MOUSE_MOVED + || id == MouseEvent.MOUSE_DRAGGED)) + || ((mask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 + && event instanceof MouseWheelEvent) + || ((mask & AWTEvent.PAINT_EVENT_MASK) != 0 + && event instanceof PaintEvent) + || ((mask & AWTEvent.TEXT_EVENT_MASK) != 0 + && event instanceof TextEvent) + || ((mask & AWTEvent.WINDOW_EVENT_MASK) != 0 + && event instanceof WindowEvent + && (id == WindowEvent.WINDOW_OPENED + || id == WindowEvent.WINDOW_CLOSING + || id == WindowEvent.WINDOW_CLOSED + || id == WindowEvent.WINDOW_ICONIFIED + || id == WindowEvent.WINDOW_DEICONIFIED + || id == WindowEvent.WINDOW_ACTIVATED + || id == WindowEvent.WINDOW_DEACTIVATED)) + || ((mask & AWTEvent.WINDOW_FOCUS_EVENT_MASK) != 0 + && event instanceof WindowEvent + && (id == WindowEvent.WINDOW_GAINED_FOCUS + || id == WindowEvent.WINDOW_LOST_FOCUS)) + || ((mask & AWTEvent.WINDOW_STATE_EVENT_MASK) != 0 + && event instanceof WindowEvent + && id == WindowEvent.WINDOW_STATE_CHANGED)) + ((AWTEventListener) getListener()).eventDispatched(event); + } + + /** + * This returns the event mask associated with this listener. + * + * @return the event mask + */ + public long getEventMask() + { + return mask; + } +} // class AWTEventListenerProxy diff --git a/libjava/classpath/java/awt/event/ActionEvent.java b/libjava/classpath/java/awt/event/ActionEvent.java new file mode 100644 index 00000000000..4bce7d45ce0 --- /dev/null +++ b/libjava/classpath/java/awt/event/ActionEvent.java @@ -0,0 +1,226 @@ +/* ActionEvent.java -- an action has been triggered + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.EventQueue; + +/** + * This event is generated when an action on a component (such as a + * button press) occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActionListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ActionEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7671078796273832149L; + + /** Bit mask indicating the shift key was pressed. */ + public static final int SHIFT_MASK = InputEvent.SHIFT_MASK; + + /** Bit mask indicating the control key was pressed. */ + public static final int CTRL_MASK = InputEvent.CTRL_MASK; + + /** Bit mask indicating the that meta key was pressed. */ + public static final int META_MASK = InputEvent.META_MASK; + + /** Bit mask indicating that the alt key was pressed. */ + public static final int ALT_MASK = InputEvent.ALT_MASK; + + /** The first id number in the range of action id's. */ + public static final int ACTION_FIRST = 1001; + + /** The last id number in the range of action id's. */ + public static final int ACTION_LAST = 1001; + + /** An event id indicating that an action has occurred. */ + public static final int ACTION_PERFORMED = 1001; + + /** + * A nonlocalized string that gives more specific details of the event cause. + * + * @see #getActionCommand() + * @serial the command for this event + */ + private final String actionCommand; + + /** + * The bitmask of the modifiers that were pressed during the action. + * + * @see #getModifiers() + * @serial modifiers for this event + */ + private final int modifiers; + + /** + * The timestamp of this event; usually the same as the underlying input + * event. + * + * @see #getWhen() + * @serial the timestamp of the event + * @since 1.4 + */ + private final long when; + + /** + * Initializes a new instance of <code>ActionEvent</code> with the + * specified source, id, and command. Note that an invalid id leads to + * unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @throws IllegalArgumentException if source is null + */ + public ActionEvent(Object source, int id, String command) + { + this(source, id, command, EventQueue.getMostRecentEventTime(), 0); + } + + /** + * Initializes a new instance of <code>ActionEvent</code> with the + * specified source, id, command, and modifiers. Note that an invalid id + * leads to unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @param modifiers the bitwise or of modifier keys down during the action + * @throws IllegalArgumentException if source is null + */ + public ActionEvent(Object source, int id, String command, int modifiers) + { + this(source, id, command, EventQueue.getMostRecentEventTime(), modifiers); + } + + /** + * Initializes a new instance of <code>ActionEvent</code> with the + * specified source, id, command, and modifiers, and timestamp. Note that + * an invalid id leads to unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @param when the timestamp of the event + * @param modifiers the bitwise or of modifier keys down during the action + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public ActionEvent(Object source, int id, String command, long when, + int modifiers) + { + super(source, id); + actionCommand = command; + this.when = when; + this.modifiers = modifiers; + } + + /** + * Returns the command string associated with this action. + * + * @return the command string associated with this action + */ + public String getActionCommand() + { + return actionCommand; + } + + /** + * Gets the timestamp of when this action took place. Usually, this + * corresponds to the timestamp of the underlying InputEvent. + * + * @return the timestamp of this action + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * Returns the keys held down during the action. This value will be a + * combination of the bit mask constants defined in this class, or 0 if no + * modifiers were pressed. + * + * @return the modifier bits + */ + public int getModifiers() + { + return modifiers; + } + + /** + * Returns a string that identifies the action event. This is in the format + * <code>"ACTION_PERFORMED,cmd=" + getActionCommand() + ",when=" + getWhen() + * + ",modifiers=" + <modifier string></code>, where the modifier + * string is in the order "Meta", "Ctrl", "Alt", "Shift", "Alt Graph", and + * "Button1", separated by '+', according to the bits set in getModifiers(). + * + * @return a string identifying the event + */ + public String paramString() + { + StringBuffer s = new StringBuffer(id == ACTION_PERFORMED + ? "ACTION_PERFORMED,cmd=" + : "unknown type,cmd="); + s.append(actionCommand).append(",when=").append(when).append(",modifiers"); + int len = s.length(); + s.setLength(len + 1); + if ((modifiers & META_MASK) != 0) + s.append("+Meta"); + if ((modifiers & CTRL_MASK) != 0) + s.append("+Ctrl"); + if ((modifiers & ALT_MASK) != 0) + s.append("+Alt"); + if ((modifiers & SHIFT_MASK) != 0) + s.append("+Shift"); + if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) + s.append("+Alt Graph"); + if ((modifiers & InputEvent.BUTTON1_MASK) != 0) + s.append("+Button1"); + s.setCharAt(len, '='); + return s.toString(); + } +} // class ActionEvent diff --git a/libjava/classpath/java/awt/event/ActionListener.java b/libjava/classpath/java/awt/event/ActionListener.java new file mode 100644 index 00000000000..4c302cca310 --- /dev/null +++ b/libjava/classpath/java/awt/event/ActionListener.java @@ -0,0 +1,59 @@ +/* ActionListener.java -- listens for action events + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that listen for action events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActionEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ActionListener extends EventListener +{ + /** + * This method is invoked when an action occurs. + * + * @param event the <code>ActionEvent</code> that occurred + */ + void actionPerformed(ActionEvent event); +} diff --git a/libjava/classpath/java/awt/event/AdjustmentEvent.java b/libjava/classpath/java/awt/event/AdjustmentEvent.java new file mode 100644 index 00000000000..867c577d356 --- /dev/null +++ b/libjava/classpath/java/awt/event/AdjustmentEvent.java @@ -0,0 +1,222 @@ +/* AdjustmentEvent.java -- an adjustable value was changed + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Adjustable; + +/** + * This class represents an event that is generated when an adjustable + * value is changed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Adjustable + * @see AdjustmentListener + * @since 1.1 + * @status updated to 1.4 + */ +public class AdjustmentEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5700290645205279921L; + + /** This is the first id in the range of ids used by adjustment events. */ + public static final int ADJUSTMENT_FIRST = 601; + + /** This is the last id in the range of ids used by adjustment events. */ + public static final int ADJUSTMENT_LAST = 601; + + /** This is the id indicating an adjustment value changed. */ + public static final int ADJUSTMENT_VALUE_CHANGED = 601; + + /** Adjustment type for unit increments. */ + public static final int UNIT_INCREMENT = 1; + + /** Adjustment type for unit decrements. */ + public static final int UNIT_DECREMENT = 2; + + /** Adjustment type for block decrements. */ + public static final int BLOCK_DECREMENT = 3; + + /** Adjustment type for block increments. */ + public static final int BLOCK_INCREMENT = 4; + + /** Adjustment type for tracking adjustments. */ + public static final int TRACK = 5; + + /** + * The adjustable object that caused the event. + * + * @see #getAdjustable() + * @serial the cause + */ + private final Adjustable adjustable; + + /** + * The type of adjustment, one of {@link #UNIT_INCREMENT}, + * {@link #UNIT_DECREMENT}, {@link #BLOCK_INCREMENT}, + * {@link #BLOCK_DECREMENT}, or {@link #TRACK}. + * + * @see #getAdjustmentType() + * @serial the adjustment type + */ + private final int adjustmentType; + + /** + * The new value of the adjustable; it should be in the range of the + * adjustable cause. + * + * @see #getValue() + * @serial the adjustment value + */ + private final int value; + + /** + * True if this is in a series of multiple adjustment events. + * + * @see #getValueIsAdjusting() + * @serial true if this is not the last adjustment + * @since 1.4 + */ + private final boolean isAdjusting; + + /** + * Initializes an instance of <code>AdjustmentEvent</code> with the + * specified source, id, type, and value. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param type the event type, one of the constants of this class + * @param value the value of the adjustment + * @throws IllegalArgumentException if source is null + */ + public AdjustmentEvent(Adjustable source, int id, int type, int value) + { + this(source, id, type, value, false); + } + + /** + * Initializes an instance of <code>AdjustmentEvent</code> with the + * specified source, id, type, and value. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param type the event type, one of the constants of this class + * @param value the value of the adjustment + * @param isAdjusting if this event is in a chain of adjustments + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public AdjustmentEvent(Adjustable source, int id, int type, int value, + boolean isAdjusting) + { + super(source, id); + this.adjustmentType = type; + this.value = value; + adjustable = source; + this.isAdjusting = isAdjusting; + } + + /** + * This method returns the source of the event as an <code>Adjustable</code>. + * + * @return the <code>Adjustable</code> source of the event + */ + public Adjustable getAdjustable() + { + return adjustable; + } + + /** + * Returns the new value of the adjustable object. + * + * @return the value of the event + */ + public int getValue() + { + return value; + } + + /** + * Returns the type of the event, which will be one of + * {@link #UNIT_INCREMENT}, {@link #UNIT_DECREMENT}, + * {@link #BLOCK_INCREMENT}, {@link #BLOCK_DECREMENT}, or {@link #TRACK}. + * + * @return the type of the event + */ + public int getAdjustmentType() + { + return adjustmentType; + } + + /** + * Test if this event is part of a sequence of multiple adjustements. + * + * @return true if this is not the last adjustment + * @since 1.4 + */ + public boolean getValueIsAdjusting() + { + return isAdjusting; + } + + /** + * Returns a string that describes the event. This is in the format + * <code>"ADJUSTMENT_VALUE_CHANGED,adjType=" + <type> + ",value=" + * + getValue() + ",isAdjusting=" + getValueIsAdjusting()</code>, where + * type is the name of the constant returned by getAdjustmentType(). + * + * @return a string that describes the event + */ + public String paramString() + { + return (id == ADJUSTMENT_VALUE_CHANGED + ? "ADJUSTMENT_VALUE_CHANGED,adjType=" : "unknown type,adjType=") + + (adjustmentType == UNIT_INCREMENT ? "UNIT_INCREMENT,value=" + : adjustmentType == UNIT_DECREMENT ? "UNIT_DECREMENT,value=" + : adjustmentType == BLOCK_INCREMENT ? "BLOCK_INCREMENT,value=" + : adjustmentType == BLOCK_DECREMENT ? "BLOCK_DECREMENT,value=" + : adjustmentType == TRACK ? "TRACK,value=" : "unknown type,value=") + + value + ",isAdjusting=" + isAdjusting; + } +} // class AdjustmentEvent diff --git a/libjava/classpath/java/awt/event/AdjustmentListener.java b/libjava/classpath/java/awt/event/AdjustmentListener.java new file mode 100644 index 00000000000..1eb2e3bcfa1 --- /dev/null +++ b/libjava/classpath/java/awt/event/AdjustmentListener.java @@ -0,0 +1,58 @@ +/* AdjustmentListener.java -- listen for adjustment events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * Interface for classes that listen for adjustment events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface AdjustmentListener extends EventListener +{ + /** + * This method is called when an adjustable value changes. + * + * @param event the <code>AdjustmentEvent</code> that occurred + */ + void adjustmentValueChanged(AdjustmentEvent event); +} // interface AdjustmentListener diff --git a/libjava/classpath/java/awt/event/ComponentAdapter.java b/libjava/classpath/java/awt/event/ComponentAdapter.java new file mode 100644 index 00000000000..6b4893f0fc2 --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentAdapter.java @@ -0,0 +1,97 @@ +/* ComponentAdapter.java -- convenience class for writing component listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>ComponentListener</code> and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the <code>ComponentListener</code> + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentEvent + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ComponentAdapter implements ComponentListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public ComponentAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentResized(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentMoved(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentShown(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentHidden(ComponentEvent event) + { + } +} // class ComponentAdapter diff --git a/libjava/classpath/java/awt/event/ComponentEvent.java b/libjava/classpath/java/awt/event/ComponentEvent.java new file mode 100644 index 00000000000..ba9c2a5b3f2 --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentEvent.java @@ -0,0 +1,137 @@ +/* ComponentEvent.java -- notification of events for components + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This class is for events generated when a component is moved, resized, + * hidden, or shown. These events normally do not need to be handled by the + * application, since the AWT system automatically takes care of them. This + * is also the superclass for other events on components, but + * ComponentListeners ignore such subclasses. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentAdapter + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ComponentEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8101406823902992965L; + + /** This is the first id in the range of ids used by this class. */ + public static final int COMPONENT_FIRST = 100; + + /** This is the last id in the range of ids used by this class. */ + public static final int COMPONENT_LAST = 103; + + /** This id indicates that a component was moved. */ + public static final int COMPONENT_MOVED = 100; + + /** This id indicates that a component was resized. */ + public static final int COMPONENT_RESIZED = 101; + + /** This id indicates that a component was shown. */ + public static final int COMPONENT_SHOWN = 102; + + /** This id indicates that a component was hidden. */ + public static final int COMPONENT_HIDDEN = 103; + + /** + * Initializes a new instance of <code>ComponentEvent</code> with the + * specified source and id. Note that an invalid id leads to unspecified + * results. + * + * @param source the source of the event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public ComponentEvent(Component source, int id) + { + super(source, id); + } + + /** + * This method returns the event source as a <code>Component</code>. If the + * source has subsequently been modified to a non-Component, this returns + * null. + * + * @return the event source as a <code>Component</code>, or null + */ + public Component getComponent() + { + return source instanceof Component ? (Component) source : null; + } + + /** + * This method returns a string identifying this event. This is the field + * name of the id type, and for COMPONENT_MOVED or COMPONENT_RESIZED, the + * new bounding box of the component. + * + * @return a string identifying this event + */ + public String paramString() + { + // Unlike Sun, we don't throw NullPointerException or ClassCastException + // when source was illegally changed. + switch (id) + { + case COMPONENT_MOVED: + return "COMPONENT_MOVED " + + (source instanceof Component + ? ((Component) source).getBounds() : (Object) ""); + case COMPONENT_RESIZED: + return "COMPONENT_RESIZED " + + (source instanceof Component + ? ((Component) source).getBounds() : (Object) ""); + case COMPONENT_SHOWN: + return "COMPONENT_SHOWN"; + case COMPONENT_HIDDEN: + return "COMPONENT_HIDDEN"; + default: + return "unknown type"; + } + } +} // class ComponentEvent diff --git a/libjava/classpath/java/awt/event/ComponentListener.java b/libjava/classpath/java/awt/event/ComponentListener.java new file mode 100644 index 00000000000..b43faaed7ff --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentListener.java @@ -0,0 +1,84 @@ +/* ComponentListener.java -- receive all events for a component + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that receive all events from a component. + * Normally it is not necessary to process these events since the AWT + * handles them internally, taking all appropriate actions. To watch a subset + * of these events, use a ComponentAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentAdapter + * @see ComponentEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ComponentListener extends EventListener +{ + /** + * This method is called when the component is resized. + * + * @param event the <code>ComponentEvent</code> indicating the resize + */ + void componentResized(ComponentEvent event); + + /** + * This method is called when the component is moved. + * + * @param event the <code>ComponentEvent</code> indicating the move + */ + void componentMoved(ComponentEvent event); + + /** + * This method is called when the component is made visible. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + void componentShown(ComponentEvent event); + + /** + * This method is called when the component is hidden. + * + * @param event the <code>ComponentEvent</code> indicating the visibility + */ + void componentHidden(ComponentEvent event); +} // interface ComponentListener diff --git a/libjava/classpath/java/awt/event/ContainerAdapter.java b/libjava/classpath/java/awt/event/ContainerAdapter.java new file mode 100644 index 00000000000..c847adfa211 --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerAdapter.java @@ -0,0 +1,79 @@ +/* ContainerAdapter.java -- convenience class for writing container listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>ContainerListener</code> and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the <code>ContainerListener</code> + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerEvent + * @see ContainerListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ContainerAdapter implements ContainerListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public ContainerAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentAdded(ContainerEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentRemoved(ContainerEvent event) + { + } +} // class ContainerAdapter diff --git a/libjava/classpath/java/awt/event/ContainerEvent.java b/libjava/classpath/java/awt/event/ContainerEvent.java new file mode 100644 index 00000000000..3c401fe1a04 --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerEvent.java @@ -0,0 +1,135 @@ +/* ContainerEvent.java -- components added/removed from a container + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; +import java.awt.Container; + +/** + * This event is generated when a component is added or removed from a + * container. Applications do not ordinarily need to handle these events + * since the AWT system handles them internally. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerAdapter + * @see ContainerListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ContainerEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4114942250539772041L; + + /** This is the first id in the id range used by this class. */ + public static final int CONTAINER_FIRST = 300; + + /** This is the last id in the id range used by this class. */ + public static final int CONTAINER_LAST = 301; + + /** This id indicates a component was added to the container. */ + public static final int COMPONENT_ADDED = 300; + + /** This id indicates a component was removed from the container. */ + public static final int COMPONENT_REMOVED = 301; + + /** + * The non-null child component that was added or removed. + * + * @serial the child component that changed + */ + private final Component child; + + /** + * Initializes a new instance of <code>ContainerEvent</code> with the + * specified source and id. Additionally, the affected child component + * is also passed as a parameter. Note that an invalid id leads to + * unspecified results. + * + * @param source the source container of the event + * @param id the event id + * @param child the child component affected by this event + * @throws IllegalArgumentException if source is null + */ + public ContainerEvent(Component source, int id, Component child) + { + super(source, id); + this.child = child; + } + + /** + * Returns the source of this event as a <code>Container</code>. + * + * @return the source of the event + * @throws ClassCastException if the source is changed to a non-Container + */ + public Container getContainer() + { + return (Container) source; + } + + /** + * This method returns the child object that was added or removed from + * the container. + * + * @return the child object added or removed + */ + public Component getChild() + { + return child; + } + + /** + * This method returns a string identifying this event. It is formatted as: + * <code>(getID() == COMPONENT_ADDED ? "COMPONENT_ADDED" + * : "COMPONENT_REMOVED") + ",child=" + getChild().getName()</code>. + * + * @return a string identifying this event + */ + public String paramString() + { + // Unlike Sun, we don't throw NullPointerException if child is illegally + // null. + return (id == COMPONENT_ADDED ? "COMPONENT_ADDED,child=" + : id == COMPONENT_REMOVED ? "COMPONENT_REMOVED,child=" + : "unknown type,child=") + (child == null ? "" : child.getName()); + } +} // class ContainerEvent diff --git a/libjava/classpath/java/awt/event/ContainerListener.java b/libjava/classpath/java/awt/event/ContainerListener.java new file mode 100644 index 00000000000..b37d4340839 --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerListener.java @@ -0,0 +1,70 @@ +/* ContainerListener.java -- listen for container events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to listen for all events from + * container objects. This is normally not necessary since the AWT system + * listens for and processes these events. To watch a subset of these events, + * use a ContainerAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerAdapter + * @see ContainerEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ContainerListener extends EventListener +{ + /** + * This method is called when a component is added to the container. + * + * @param event the <code>ContainerEvent</code> indicating component addition + */ + void componentAdded(ContainerEvent event); + + /** + * This method is called when a component is removed from the container. + * + * @param event the <code>ContainerEvent</code> indicating component removal + */ + void componentRemoved(ContainerEvent event); +} // interface ContainerListener diff --git a/libjava/classpath/java/awt/event/FocusAdapter.java b/libjava/classpath/java/awt/event/FocusAdapter.java new file mode 100644 index 00000000000..fb0532a3a91 --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusAdapter.java @@ -0,0 +1,79 @@ +/* FocusAdapter.java -- convenience class for writing focus listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>FocusListener</code> and implements all + * methods with empty bodies. This allows a listener interested in + * implementing only a subset of the <code>FocusListener</code> interface to + * extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusEvent + * @see FocusListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class FocusAdapter implements FocusListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public FocusAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void focusGained(FocusEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void focusLost(FocusEvent event) + { + } +} // class FocusAdapter diff --git a/libjava/classpath/java/awt/event/FocusEvent.java b/libjava/classpath/java/awt/event/FocusEvent.java new file mode 100644 index 00000000000..a44284aea75 --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusEvent.java @@ -0,0 +1,181 @@ +/* FocusEvent.java -- generated for a focus change + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; + +/** + * This class represents an event generated when a focus change occurs for a + * component. There are both temporary changes, such as when focus is stolen + * during a sroll then returned, and permanent changes, such as when the user + * TABs through focusable components. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusAdapter + * @see FocusListener + * @since 1.1 + * @status updated to 1.4 + */ +public class FocusEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 523753786457416396L; + + /** This is the first id in the range of ids used by this class. */ + public static final int FOCUS_FIRST = 1004; + + /** This is the last id in the range of ids used by this class. */ + public static final int FOCUS_LAST = 1005; + + /** This is the event id for a focus gained event. */ + public static final int FOCUS_GAINED = 1004; + + /** This is the event id for a focus lost event. */ + public static final int FOCUS_LOST = 1005; + + /** + * Indicates whether or not the focus change is temporary. + * + * @see #isTemporary() + * @serial true if the focus change is temporary + */ + private final boolean temporary; + + /** + * The other component which is giving up or stealing focus from this + * component, if known. + * + * @see #getOppositeComponent() + * @serial the component with the opposite focus event, or null + * @since 1.4 + */ + private final Component opposite; + + /** + * Initializes a new instance of <code>FocusEvent</code> with the + * specified source, id, temporary status, and opposite counterpart. Note + * that an invalid id leads to unspecified results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @param temporary true if the focus change is temporary + * @param opposite the component receiving the opposite focus event, or null + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id, boolean temporary, + Component opposite) + { + super(source, id); + this.temporary = temporary; + this.opposite = opposite; + } + + /** + * Initializes a new instance of <code>FocusEvent</code> with the + * specified source, id, and temporary status. Note that an invalid id + * leads to unspecified results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @param temporary true if the focus change is temporary + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id, boolean temporary) + { + this(source, id, temporary, null); + } + + /** + * Initializes a new instance of <code>FocusEvent</code> with the + * specified source and id. Note that an invalid id leads to unspecified + * results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id) + { + this(source, id, false, null); + } + + /** + * This method tests whether or not the focus change is temporary or + * permanent. + * + * @return true if the focus change is temporary + */ + public boolean isTemporary() + { + return temporary; + } + + /** + * Returns the component which received the opposite focus event. If this + * component gained focus, the opposite lost focus; likewise if this + * component is giving up focus, the opposite is gaining it. If this + * information is unknown, perhaps because the opposite is a native + * application, this returns null. + * + * @return the component with the focus opposite, or null + * @since 1.4 + */ + public Component getOppositeComponent() + { + return opposite; + } + + /** + * Returns a string identifying this event. This is formatted as: + * <code>(getID() == FOCUS_GAINED ? "FOCUS_GAINED" : "FOCUS_LOST") + * + (isTemporary() ? ",temporary," : ",permanent,") + "opposite=" + * + getOppositeComponent()</code>. + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == FOCUS_GAINED ? "FOCUS_GAINED" + : id == FOCUS_LOST ? "FOCUS_LOST" : "unknown type") + + (temporary ? ",temporary,opposite=" : ",permanent,opposite=") + + opposite; + } +} // class FocusEvent diff --git a/libjava/classpath/java/awt/event/FocusListener.java b/libjava/classpath/java/awt/event/FocusListener.java new file mode 100644 index 00000000000..1f7201825a7 --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusListener.java @@ -0,0 +1,69 @@ +/* FocusListener.java -- listen for focus changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified of changes of + * keyboard focus for a component. To watch a subset of these events, use a + * FocusAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusAdapter + * @see FocusEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface FocusListener extends EventListener +{ + /** + * This method is called when a component gains the keyboard focus. + * + * @param event the <code>FocusEvent</code> indicating that focus was gained + */ + void focusGained(FocusEvent event); + + /** + * This method is invoked when a component loses the keyboard focus. + * + * @param event the <code>FocusEvent</code> indicating that focus was lost + */ + void focusLost(FocusEvent event); +} // interface FocusListener diff --git a/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java b/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java new file mode 100644 index 00000000000..340cf01edf1 --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java @@ -0,0 +1,78 @@ +/* HierarchyBoundsAdapter.java -- convenience class for writing listeners + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.event; + +/** + * This class implements <code>HierarchyBoundsListener</code> and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the <code>HierarchyBoundsListener</code> + * interface to extend this class and override only the desired methods. + * + * @author Bryce McKinlay + * @see HierarchyBoundsListener + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public abstract class HierarchyBoundsAdapter implements HierarchyBoundsListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public HierarchyBoundsAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void ancestorMoved(HierarchyEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void ancestorResized(HierarchyEvent event) + { + } +} diff --git a/libjava/classpath/java/awt/event/HierarchyBoundsListener.java b/libjava/classpath/java/awt/event/HierarchyBoundsListener.java new file mode 100644 index 00000000000..689623744e3 --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyBoundsListener.java @@ -0,0 +1,70 @@ +/* HierarchyBoundsListener.java -- listens to bounds changes of parents + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This listens for changes in an ancestors size or location. Normally it is + * not necessary to process these events since the AWT handles them + * internally, taking all appropriate actions. To watch a subset of these + * events, use a HierarchyBoundsAdapter. + * + * @author Bryce McKinlay + * @see HierarchyBoundsAdapter + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public interface HierarchyBoundsListener extends EventListener +{ + /** + * Called when an ancestor component of the source is moved. + * + * @param e the event describing the ancestor's motion + */ + void ancestorMoved(HierarchyEvent e); + + /** + * Called when an ancestor component is resized. + * + * @param e the event describing the ancestor's resizing + */ + void ancestorResized(HierarchyEvent e); +} // interface HierarchyBoundsListener diff --git a/libjava/classpath/java/awt/event/HierarchyEvent.java b/libjava/classpath/java/awt/event/HierarchyEvent.java new file mode 100644 index 00000000000..e10cefbefcb --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyEvent.java @@ -0,0 +1,253 @@ +/* HierarchyEvent.java -- generated for a change in hierarchy + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Container; + +/** + * This class represents an event generated for an ancestor component which + * may affect this component. These events normally do not need to be handled + * by the application, since the AWT system automatically takes care of them. + * + * <p>There are two types of hierarchy events. The first type is handled by + * HierarchyListener, and includes addition or removal of an ancestor, or + * an ancestor changing its on-screen status (visible and/or displayble). The + * second type is handled by HierarchyBoundsListener, and includes resizing + * or moving of an ancestor. + * + * @author Bryce McKinlay + * @see HierarchyListener + * @see HierarchyBoundsAdapter + * @see HierarchyBoundsListener + * @since 1.3 + * @status updated to 1.4 + */ +public class HierarchyEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = -5337576970038043990L; + + /** This is the first id in the range of ids used by this class. */ + public static final int HIERARCHY_FIRST = 1400; + + /** This id indicates that the hierarchy tree changed. */ + public static final int HIERARCHY_CHANGED = 1400; + + /** This id indicates that an ancestor was moved. */ + public static final int ANCESTOR_MOVED = 1401; + + /** This id indicates that an ancestor was resized. */ + public static final int ANCESTOR_RESIZED = 1402; + + /** This is the last id in the range of ids used by this class. */ + public static final int HIERARCHY_LAST = 1402; + + /** This indicates that the HIERARCHY_CHANGED is a changed parent. */ + public static final int PARENT_CHANGED = 1; + + /** + * This indicates that the HIERARCHY_CHANGED is caused by a change in + * displayability. + * + * @see Component#isDisplayable() + * @see Component#addNotify() + * @see Component#removeNotify() + */ + public static final int DISPLAYABILITY_CHANGED = 2; + + /** + * This indicates that the HIERARCHY_CHANGED is a changed visibility. + * + * @see Component#isShowing() + * @see Component#addNotify() + * @see Component#removeNotify() + * @see Component#show() + * @see Component#hide() + */ + public static final int SHOWING_CHANGED = 4; + + /** + * The component at the top of the changed hierarchy. + * + * @serial the top component changed + */ + private final Component changed; + + /** + * The parent of this component, either before or after the change depending + * on the type of change. + * + * @serial the parent component changed + */ + private final Container changedParent; + + /** + * The bitmask of HIERARCHY_CHANGED event types. + * + * @serial the change flags + */ + private final long changeFlags; + + /** + * Initializes a new instance of <code>HierarchyEvent</code> with the + * specified parameters. Note that an invalid id leads to unspecified + * results. + * + * @param source the component whose hierarchy changed + * @param id the event id + * @param changed the top component in the tree of changed hierarchy + * @param changedParent the updated parent of this object + * @throws IllegalArgumentException if source is null + */ + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent) + { + this(source, id, changed, changedParent, 0); + } + + /** + * Initializes a new instance of <code>HierarchyEvent</code> with the + * specified parameters. Note that an invalid id leads to unspecified + * results. + * + * @param source the component whose hierarchy changed + * @param id the event id + * @param changed the top component in the tree of changed hierarchy + * @param changedParent the updated parent of this object + * @param changeFlags the bitmask of specific HIERARCHY_CHANGED events + * @throws IllegalArgumentException if source is null + */ + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent, long changeFlags) + { + super(source, id); + this.changed = changed; + this.changedParent = changedParent; + this.changeFlags = changeFlags; + } + + /** + * This method returns the event source as a <code>Component</code>. If the + * source has subsequently been modified to a non-Component, this returns + * null. + * + * @return the event source as a <code>Component</code>, or null + */ + public Component getComponent() + { + return source instanceof Component ? (Component) source : null; + } + + /** + * Returns the component at the top of the hierarchy which changed. + * + * @return the top changed component + */ + public Component getChanged() + { + return changed; + } + + /** + * Returns the parent of the component listed in <code>getChanged()</code>. + * If the cause of this event was <code>Container.add</code>, this is the + * new parent; if the cause was <code>Container.remove</code>, this is the + * old parent; otherwise it is the unchanged parent. + * + * @return the parent container of the changed component + */ + public Container getChangedParent() + { + return changedParent; + } + + /** + * If this is a HIERARCHY_CHANGED event, this returns a bitmask of the + * types of changes that took place. + * + * @return the bitwise or of hierarchy change types, or 0 + * @see #PARENT_CHANGED + * @see #DISPLAYABILITY_CHANGED + * @see #SHOWING_CHANGED + */ + public long getChangeFlags() + { + return changeFlags; + } + + /** + * This method returns a string identifying this event. This is the field + * name of the id type, followed by a parenthesized listing of the changed + * component and its parent container. In addition, if the type is + * HIERARCHY_CHANGED, the flags preceed the changed component, in the + * order PARENT_CHANGED, DISPLAYABILITY_CHANGED, and SHOWING_CHANGED. + * + * @return a string identifying this event + */ + public String paramString() + { + StringBuffer r = new StringBuffer(); + switch (id) + { + case HIERARCHY_CHANGED: + r.append("HIERARCHY_CHANGED ("); + if ((changeFlags & PARENT_CHANGED) != 0) + r.append("PARENT_CHANGED,"); + if ((changeFlags & DISPLAYABILITY_CHANGED) != 0) + r.append("DISPLAYABILITY_CHANGED,"); + if ((changeFlags & SHOWING_CHANGED) != 0) + r.append("SHOWING_CHANGED,"); + break; + case ANCESTOR_MOVED: + r.append("ANCESTOR_MOVED ("); + break; + case ANCESTOR_RESIZED: + r.append("ANCESTOR_RESIZED ("); + break; + default: + return "unknown type"; + } + r.append(changed).append(',').append(changedParent).append(')'); + return r.toString(); + } +} // class HierarchyEvent diff --git a/libjava/classpath/java/awt/event/HierarchyListener.java b/libjava/classpath/java/awt/event/HierarchyListener.java new file mode 100644 index 00000000000..f90414b866b --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyListener.java @@ -0,0 +1,62 @@ +/* HierarchyListener.java -- listens to changes in the component hierarchy + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This listens for changes in the hierarchy tree of components. Normally it is + * not necessary to process these events since the AWT handles them + * internally, taking all appropriate actions. + * + * @author Bryce McKinlay + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public interface HierarchyListener extends EventListener +{ + /** + * Called when the hierarchy of this component changes. Use + * <code>getChangeFlags()</code> on the event to see what exactly changed. + * + * @param e the event describing the change + */ + void hierarchyChanged(HierarchyEvent e); +} // interface HierarchyListener diff --git a/libjava/classpath/java/awt/event/InputEvent.java b/libjava/classpath/java/awt/event/InputEvent.java new file mode 100644 index 00000000000..8f9aed611f8 --- /dev/null +++ b/libjava/classpath/java/awt/event/InputEvent.java @@ -0,0 +1,381 @@ +/* InputEvent.java -- common superclass of component input events + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; + +import java.awt.Component; + +/** + * This is the common superclass for all component input classes. These are + * passed to listeners before the component, so that listeners can consume + * the event before it does its default behavior. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyEvent + * @see KeyAdapter + * @see MouseEvent + * @see MouseAdapter + * @see MouseMotionAdapter + * @see MouseWheelEvent + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class InputEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2482525981698309786L; + + /** + * This is the bit mask which indicates the shift key is down. It is + * recommended that SHIFT_DOWN_MASK be used instead. + * + * @see #SHIFT_DOWN_MASK + */ + public static final int SHIFT_MASK = 1; + + /** + * This is the bit mask which indicates the control key is down. It is + * recommended that CTRL_DOWN_MASK be used instead. + * + * @see #CTRL_DOWN_MASK + */ + public static final int CTRL_MASK = 2; + + /** + * This is the bit mask which indicates the meta key is down. It is + * recommended that META_DOWN_MASK be used instead. + * + * @see #META_DOWN_MASK + */ + public static final int META_MASK = 4; + + /** + * This is the bit mask which indicates the alt key is down. It is + * recommended that ALT_DOWN_MASK be used instead. + * + * @see #ALT_DOWN_MASK + */ + public static final int ALT_MASK = 8; + + /** + * This is the bit mask which indicates the alt-graph modifier is in effect. + * It is recommended that ALT_GRAPH_DOWN_MASK be used instead. + * + * @see #ALT_GRAPH_DOWN_MASK + */ + public static final int ALT_GRAPH_MASK = 0x20; + + /** + * This bit mask indicates mouse button one is down. It is recommended that + * BUTTON1_DOWN_MASK be used instead. + * + * @see #BUTTON1_DOWN_MASK + */ + public static final int BUTTON1_MASK = 0x10; + + /** + * This bit mask indicates mouse button two is down. It is recommended that + * BUTTON2_DOWN_MASK be used instead. + * + * @see #BUTTON2_DOWN_MASK + */ + public static final int BUTTON2_MASK = 8; + + /** + * This bit mask indicates mouse button three is down. It is recommended + * that BUTTON3_DOWN_MASK be used instead. + * + * @see #BUTTON3_DOWN_MASK + */ + public static final int BUTTON3_MASK = 4; + + /** + * The SHIFT key extended modifier. + * + * @since 1.4 + */ + public static final int SHIFT_DOWN_MASK = 0x0040; + + /** + * The CTRL key extended modifier. + * + * @since 1.4 + */ + public static final int CTRL_DOWN_MASK = 0x0080; + + /** + * The META key extended modifier. + * + * @since 1.4 + */ + public static final int META_DOWN_MASK = 0x0100; + + /** + * The ALT key extended modifier. + * + * @since 1.4 + */ + public static final int ALT_DOWN_MASK = 0x0200; + + /** + * The mouse button1 key extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON1_DOWN_MASK = 0x0400; + + /** + * The mouse button2 extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON2_DOWN_MASK = 0x0800; + + /** + * The mouse button3 extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON3_DOWN_MASK = 0x1000; + + /** + * The ALT_GRAPH key extended modifier. + * + * @since 1.4 + */ + public static final int ALT_GRAPH_DOWN_MASK = 0x2000; + + /** The mask to convert new to old, package visible for use in subclasses. */ + static final int CONVERT_MASK + = EventModifier.NEW_MASK & ~(BUTTON2_DOWN_MASK | BUTTON3_DOWN_MASK); + + /** + * The timestamp when this event occurred. + * + * @see #getWhen() + * @serial the timestamp + */ + private final long when; + + /** + * The modifiers in effect for this event. Package visible for use by + * subclasses. The old style (bitmask 0x3f) should not be mixed with the + * new style (bitmasks 0xffffffc0). + * + * @see #getModifiers() + * @see MouseEvent + * @serial the modifier state, stored in the new style + */ + int modifiers; + + /** + * Initializes a new instance of <code>InputEvent</code> with the specified + * source, id, timestamp, and modifiers. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp when the event occurred + * @param modifiers the modifiers in effect for this event, old or new style + * @throws IllegalArgumentException if source is null + */ + InputEvent(Component source, int id, long when, int modifiers) + { + super(source, id); + this.when = when; + this.modifiers = EventModifier.extend(modifiers); + } + + /** + * This method tests whether or not the shift key was down during the event. + * + * @return true if the shift key is down + */ + public boolean isShiftDown() + { + return (modifiers & SHIFT_DOWN_MASK) != 0; + } + + /** + * This method tests whether or not the control key was down during the + * event. + * + * @return true if the control key is down + */ + public boolean isControlDown() + { + return (modifiers & CTRL_DOWN_MASK) != 0; + } + + /** + * This method tests whether or not the meta key was down during the event. + * + * @return true if the meta key is down + */ + public boolean isMetaDown() + { + return (modifiers & META_DOWN_MASK) != 0; + } + + /** + * This method tests whether or not the alt key was down during the event. + * + * @return true if the alt key is down + */ + public boolean isAltDown() + { + return (modifiers & ALT_DOWN_MASK) != 0; + } + + /** + * This method tests whether or not the alt-graph modifier was in effect + * during the event. + * + * @return true if the alt-graph modifier is down + */ + public boolean isAltGraphDown() + { + return (modifiers & ALT_GRAPH_DOWN_MASK) != 0; + } + + /** + * This method returns the timestamp when this event occurred. + * + * @return the timestamp when this event occurred + */ + public long getWhen() + { + return when; + } + + /** + * This method returns the old-style modifiers in effect for this event. + * Note that this is ambiguous between button2 and alt, and between + * button3 and meta. Also, code which generated these modifiers tends to + * only list the modifier that just changed, even if others were down at + * the time. Consider using getModifiersEx instead. This will be a union + * of the bit masks defined in this class that are applicable to the event. + * + * @return the modifiers in effect for this event + * @see #getModifiersEx() + */ + public int getModifiers() + { + return EventModifier.revert(modifiers); + } + + /** + * Returns the extended modifiers (new-style) for this event. This represents + * the state of all modal keys and mouse buttons at the time of the event, + * and does not suffer from the problems mentioned in getModifiers. + * + * <p>For an example of checking multiple modifiers, this code will return + * true only if SHIFT and BUTTON1 were pressed and CTRL was not: + * <pre> + * int onmask = InputEvent.SHIFT_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK; + * int offmask = InputEvent.CTRL_DOWN_MASK; + * return (event.getModifiersEx() & (onmask | offmask)) == onmask; + * </pre> + * + * @return the bitwise or of all modifiers pressed during the event + * @since 1.4 + */ + public int getModifiersEx() + { + return modifiers; + } + + /** + * Consumes this event. A consumed event is not processed further by the AWT + * system. + */ + public void consume() + { + consumed = true; + } + + /** + * This method tests whether or not this event has been consumed. + * + * @return true if this event has been consumed + */ + public boolean isConsumed() + { + return consumed; + } + + /** + * Convert the extended modifier bitmask into a String, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the modifiers + * @return a string representation of the modifiers in this bitmask + * @since 1.4 + */ + public static String getModifiersExText(int modifiers) + { + modifiers &= EventModifier.NEW_MASK; + if (modifiers == 0) + return ""; + StringBuffer s = new StringBuffer(); + if ((modifiers & META_DOWN_MASK) != 0) + s.append("Meta+"); + if ((modifiers & CTRL_DOWN_MASK) != 0) + s.append("Ctrl+"); + if ((modifiers & ALT_DOWN_MASK) != 0) + s.append("Alt+"); + if ((modifiers & SHIFT_DOWN_MASK) != 0) + s.append("Shift+"); + if ((modifiers & ALT_GRAPH_DOWN_MASK) != 0) + s.append("Alt Graph+"); + if ((modifiers & BUTTON1_DOWN_MASK) != 0) + s.append("Button1+"); + if ((modifiers & BUTTON2_DOWN_MASK) != 0) + s.append("Button2+"); + if ((modifiers & BUTTON3_DOWN_MASK) != 0) + s.append("Button3+"); + return s.substring(0, s.length() - 1); + } +} // class InputEvent diff --git a/libjava/classpath/java/awt/event/InputMethodEvent.java b/libjava/classpath/java/awt/event/InputMethodEvent.java new file mode 100644 index 00000000000..f6711a8fa5a --- /dev/null +++ b/libjava/classpath/java/awt/event/InputMethodEvent.java @@ -0,0 +1,303 @@ +/* InputMethodEvent.java -- events from a text input method + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.font.TextHitInfo; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.text.AttributedCharacterIterator; + +/** + * This class is for event generated by change in a text input method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see InputMethodListener + * @since 1.2 + * @status updated to 1.4 + */ +public class InputMethodEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4727190874778922661L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int INPUT_METHOD_FIRST = 1100; + + /** This event id indicates that the text in the input method has changed. */ + public static final int INPUT_METHOD_TEXT_CHANGED = 1100; + + /** This event id indicates that the input method curor point has changed. */ + public static final int CARET_POSITION_CHANGED = 1101; + + /** This is the last id in the range of event ids used by this class. */ + public static final int INPUT_METHOD_LAST = 1101; + + /** + * The timestamp when this event was created. + * + * @serial the timestamp + * @since 1.4 + */ + private long when; + + /** The input method text. */ + private final transient AttributedCharacterIterator text; + + /** The number of committed characters in the text. */ + private final transient int committedCharacterCount; + + /** The caret. */ + private final transient TextHitInfo caret; + + /** The most important position to be visible. */ + private final transient TextHitInfo visiblePosition; + + /** + * Initializes a new instance of <code>InputMethodEvent</code> with the + * specified source, id, timestamp, text, char count, caret, and visible + * position. + * + * @param source the source that generated the event + * @param id the event id + * @param when the timestamp of the event + * @param text the input text + * @param committedCharacterCount the number of committed characters + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null, id is invalid, id is + * CARET_POSITION_CHANGED and text is non-null, or if + * committedCharacterCount is out of range + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, long when, + AttributedCharacterIterator text, + int committedCharacterCount, TextHitInfo caret, + TextHitInfo visiblePosition) + { + super(source, id); + this.when = when; + this.text = text; + this.committedCharacterCount = committedCharacterCount; + this.caret = caret; + this.visiblePosition = visiblePosition; + if (id < INPUT_METHOD_FIRST || id > INPUT_METHOD_LAST + || (id == CARET_POSITION_CHANGED && text != null) + || committedCharacterCount < 0 + || (committedCharacterCount + > (text == null ? 0 : text.getEndIndex() - text.getBeginIndex()))) + throw new IllegalArgumentException(); + } + + /** + * Initializes a new instance of <code>InputMethodEvent</code> with the + * specified source, id, text, char count, caret, and visible position. + * + * @param source the source that generated the event + * @param id the event id + * @param text the input text + * @param committedCharacterCount the number of committed characters + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null, id is invalid, id is + * CARET_POSITION_CHANGED and text is non-null, or if + * committedCharacterCount is out of range + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, + AttributedCharacterIterator text, + int committedCharacterCount, TextHitInfo caret, + TextHitInfo visiblePosition) + { + this(source, id, EventQueue.getMostRecentEventTime(), text, + committedCharacterCount, caret, visiblePosition); + } + + /** + * Initializes a new instance of <code>InputMethodEvent</code> with the + * specified source, id, caret, and visible position, and with a null + * text and char count. + * + * @param source the source that generated the event + * @param id the event id + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null or id is invalid + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, TextHitInfo caret, + TextHitInfo visiblePosition) + { + this(source, id, EventQueue.getMostRecentEventTime(), null, 0, caret, + visiblePosition); + } + + /** + * This method returns the input method text. This can be <code>null</code>, + * and will always be null for <code>CARET_POSITION_CHANGED</code> events. + * Characters from 0 to <code>getCommittedCharacterCount()-1</code> have + * been committed, the remaining characters are composed text. + * + * @return the input method text, or null + */ + public AttributedCharacterIterator getText() + { + return text; + } + + /** + * Returns the number of committed characters in the input method text. + * + * @return the number of committed characters in the input method text + */ + public int getCommittedCharacterCount() + { + return committedCharacterCount; + } + + /** + * Returns the caret position. The caret offset is relative to the composed + * text of the most recent <code>INPUT_METHOD_TEXT_CHANGED</code> event. + * + * @return the caret position, or null + */ + public TextHitInfo getCaret() + { + return caret; + } + + /** + * Returns the position that is most important to be visible, or null if + * such a hint is not necessary. The caret offset is relative to the composed + * text of the most recent <code>INPUT_METHOD_TEXT_CHANGED</code> event. + * + * @return the position that is most important to be visible + */ + public TextHitInfo getVisiblePosition() + { + return visiblePosition; + } + + /** + * This method consumes the event. A consumed event is not processed + * in the default manner by the component that generated it. + */ + public void consume() + { + consumed = true; + } + + /** + * This method tests whether or not this event has been consumed. + * + * @return true if the event has been consumed + */ + public boolean isConsumed() + { + return consumed; + } + + /** + * Return the timestamp of this event. + * + * @return the timestamp + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * This method returns a string identifying the event. This contains the + * event ID, the committed and composed characters separated by '+', the + * number of committed characters, the caret, and the visible position. + * + * @return a string identifying the event + */ + public String paramString() + { + StringBuffer s + = new StringBuffer(80 + (text == null ? 0 + : text.getEndIndex() - text.getBeginIndex())); + s.append(id == INPUT_METHOD_TEXT_CHANGED ? "INPUT_METHOD_TEXT_CHANGED, " + : "CARET_POSITION_CHANGED, "); + if (text == null) + s.append("no text, 0 characters committed, caret: "); + else + { + s.append('"'); + int i = text.getBeginIndex(); + int j = committedCharacterCount; + while (--j >= 0) + s.append(text.setIndex(i++)); + s.append("\" + \""); + j = text.getEndIndex() - i; + while (--j >= 0) + s.append(text.setIndex(i++)); + s.append("\", ").append(committedCharacterCount) + .append(" characters committed, caret: "); + } + s.append(caret == null ? (Object) "no caret" : caret).append(", ") + .append(visiblePosition == null ? (Object) "no visible position" + : visiblePosition); + return s.toString(); + } + + /** + * Reads in the object from a serial stream, updating when to + * {@link EventQueue#getMostRecentEventTime()} if necessary. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except for updating when + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + if (when == 0) + when = EventQueue.getMostRecentEventTime(); + } +} // class InputMethodEvent diff --git a/libjava/classpath/java/awt/event/InputMethodListener.java b/libjava/classpath/java/awt/event/InputMethodListener.java new file mode 100644 index 00000000000..e2f6a4e6755 --- /dev/null +++ b/libjava/classpath/java/awt/event/InputMethodListener.java @@ -0,0 +1,70 @@ +/* InputMethodListener.java -- listen for input method events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.im.InputMethodRequests; +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive events from an input + * method. For a text component to use input methods, it must also install + * an InputMethodRequests handler. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see InputMethodEvent + * @see InputMethodRequests + * @since 1.2 + * @status updated to 1.4 + */ +public interface InputMethodListener extends EventListener +{ + /** + * This method is called when the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the text change + */ + void inputMethodTextChanged(InputMethodEvent event); + + /** + * This method is called when the cursor position within the text is changed. + * + * @param event the <code>InputMethodEvent</code> indicating the change + */ + void caretPositionChanged(InputMethodEvent event); +} // interface InputMethodListener diff --git a/libjava/classpath/java/awt/event/InvocationEvent.java b/libjava/classpath/java/awt/event/InvocationEvent.java new file mode 100644 index 00000000000..75feb62bd94 --- /dev/null +++ b/libjava/classpath/java/awt/event/InvocationEvent.java @@ -0,0 +1,237 @@ +/* InvocationEvent.java -- call a runnable when dispatched + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ActiveEvent; +import java.awt.EventQueue; + +/** + * This event executes {@link Runnable#run()} of a target object when it is + * dispatched. This class is used by calls to <code>invokeLater</code> and + * <code>invokeAndWait</code>, so client code can use this fact to avoid + * writing special-casing AWTEventListener objects. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActiveEvent + * @see EventQueue#invokeLater(Runnable) + * @see EventQueue#invokeAndWait(Runnable) + * @see AWTEventListener + * @since 1.2 + * @status updated to 1.4 + */ +public class InvocationEvent extends AWTEvent implements ActiveEvent +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 436056344909459450L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int INVOCATION_FIRST = 1200; + + /** This is the default id for this event type. */ + public static final int INVOCATION_DEFAULT = 1200; + + /** This is the last id in the range of event ids used by this class. */ + public static final int INVOCATION_LAST = 1200; + + /** + * This is the <code>Runnable</code> object to call when dispatched. + * + * @serial the runnable to execute + */ + protected Runnable runnable; + + /** + * This is the object to call <code>notifyAll()</code> on when + * the call to <code>run()</code> returns, or <code>null</code> if no + * object is to be notified. + * + * @serial the object to notify + */ + protected Object notifier; + + /** + * This variable is set to <code>true</code> if exceptions are caught + * and stored in a variable during the call to <code>run()</code>, otherwise + * exceptions are ignored and propagate up. + * + * @serial true to catch exceptions + */ + protected boolean catchExceptions; + + /** + * This is the caught exception thrown in the <code>run()</code> method. It + * is null if exceptions are ignored, the run method hasn't completed, or + * there were no exceptions. + * + * @serial the caught exception, if any + */ + private Exception exception; + + /** + * The timestamp when this event was created. + * + * @see #getWhen() + * @serial the timestamp + * @since 1.4 + */ + private final long when = EventQueue.getMostRecentEventTime(); + + /** + * Initializes a new instance of <code>InvocationEvent</code> with the + * specified source and runnable. + * + * @param source the source of the event + * @param runnable the <code>Runnable</code> object to invoke + * @throws IllegalArgumentException if source is null + */ + public InvocationEvent(Object source, Runnable runnable) + { + this(source, INVOCATION_DEFAULT, runnable, null, false); + } + + /** + * Initializes a new instance of <code>InvocationEvent</code> with the + * specified source, runnable, and notifier. It will also catch exceptions + * if specified. If notifier is non-null, this will call notifyAll() on + * the object when the runnable is complete. If catchExceptions is true, + * this traps any exception in the runnable, otherwise it lets the exception + * propagate up the Event Dispatch thread. + * + * @param source the source of the event + * @param runnable the <code>Runnable</code> object to invoke + * @param notifier the object to notify, or null + * @param catchExceptions true to catch exceptions from the runnable + */ + public InvocationEvent(Object source, Runnable runnable, Object notifier, + boolean catchExceptions) + { + this(source, INVOCATION_DEFAULT, runnable, notifier, catchExceptions); + } + + /** + * Initializes a new instance of <code>InvocationEvent</code> with the + * specified source, runnable, and notifier. It will also catch exceptions + * if specified. If notifier is non-null, this will call notifyAll() on + * the object when the runnable is complete. If catchExceptions is true, + * this traps any exception in the runnable, otherwise it lets the exception + * propagate up the Event Dispatch thread. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param runnable the <code>Runnable</code> object to invoke + * @param notifier the object to notify, or null + * @param catchExceptions true to catch exceptions from the runnable + */ + protected InvocationEvent(Object source, int id, Runnable runnable, + Object notifier, boolean catchExceptions) + { + super(source, id); + this.runnable = runnable; + this.notifier = notifier; + this.catchExceptions = catchExceptions; + } + + /** + * This method calls the <code>run()</code> method of the runnable, traps + * exceptions if instructed to do so, and calls <code>notifyAll()</code> + * on any notifier if all worked successfully. + */ + public void dispatch() + { + if (catchExceptions) + try + { + runnable.run(); + } + catch (Exception e) + { + exception = e; + } + else + runnable.run(); + + Object o = notifier; + if (o != null) + synchronized(o) + { + o.notifyAll(); + } + } + + /** + * This method returns the exception that occurred during the execution of + * the runnable, or <code>null</code> if not exception was thrown or + * exceptions were not caught. + * + * @return the exception thrown by the runnable + */ + public Exception getException() + { + return exception; + } + + /** + * Gets the timestamp of when this event was created. + * + * @return the timestamp of this event + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * This method returns a string identifying this event. This is formatted as: + * <code>"INVOCATION_DEFAULT,runnable=" + runnable + ",notifier=" + notifier + * + ",catchExceptions=" + catchExceptions + ",when=" + getWhen()</code>. + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == INVOCATION_DEFAULT ? "INVOCATION_DEFAULT,runnable=" + : "unknown type,runnable=") + runnable + ",notifier=" + notifier + + ",catchExceptions=" + catchExceptions + ",when=" + when; + } +} // class InvocationEvent diff --git a/libjava/classpath/java/awt/event/ItemEvent.java b/libjava/classpath/java/awt/event/ItemEvent.java new file mode 100644 index 00000000000..467815b1608 --- /dev/null +++ b/libjava/classpath/java/awt/event/ItemEvent.java @@ -0,0 +1,155 @@ +/* ItemEvent.java -- event for item state changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ItemSelectable; + +/** + * This event is generated when a selection item changes state. This is an + * abstraction that distills a large number of individual mouse or keyboard + * events into a simpler "item selected" and "item deselected" events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ItemSelectable + * @see ItemListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ItemEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -608708132447206933L; + + /** This is the first id in the event id range used by this class. */ + public static final int ITEM_FIRST = 701; + + /** This is the last id in the event id range used by this class. */ + public static final int ITEM_LAST = 701; + + /** This event id indicates a state change occurred. */ + public static final int ITEM_STATE_CHANGED = 701; + + /** This type indicates that the item was selected. */ + public static final int SELECTED = 1; + + /** This type indicates that the item was deselected. */ + public static final int DESELECTED = 2; + + /** + * The item affected by this event. + * + * @serial the item of the selection + */ + private final Object item; + + /** + * The state change direction, one of {@link #SELECTED} or + * {@link #DESELECTED}. + * + * @serial the selection state + */ + private final int stateChange; + + /** + * Initializes a new instance of <code>ItemEvent</code> with the specified + * source, id, and state change constant. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param item the item affected by the state change + * @param stateChange one of {@link #SELECTED} or {@link #DESELECTED} + */ + public ItemEvent(ItemSelectable source, int id, Object item, int stateChange) + { + super(source, id); + this.item = item; + this.stateChange = stateChange; + } + + /** + * This method returns the event source as an <code>ItemSelectable</code>. + * + * @return the event source as an <code>ItemSelected</code> + * @throws ClassCastException if source is changed to a non-ItemSelectable + */ + public ItemSelectable getItemSelectable() + { + return (ItemSelectable) source; + } + + /** + * Returns the item affected by this state change. + * + * @return the item affected by this state change + */ + public Object getItem() + { + return item; + } + + /** + * Returns the type of state change, either {@link #SELECTED} or + * {@link #DESELECTED}. + * + * @return the type of state change + */ + public int getStateChange() + { + return stateChange; + } + + /** + * Returns a string identifying this event. This is in the format: + * <code>"ITEM_STATE_CHANGED,item=" + item + ",stateChange=" + * + (getStateChange() == DESELECTED ? "DESELECTED" : "SELECTED")</code>. + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == ITEM_STATE_CHANGED ? "ITEM_STATE_CHANGED,item=" + : "unknown type,item=") + item + ",stateChange=" + + (stateChange == SELECTED ? "SELECTED" + : stateChange == DESELECTED ? "DESELECTED" : "unknown type"); + } +} // class ItemEvent diff --git a/libjava/classpath/java/awt/event/ItemListener.java b/libjava/classpath/java/awt/event/ItemListener.java new file mode 100644 index 00000000000..fa5f3aad3fb --- /dev/null +++ b/libjava/classpath/java/awt/event/ItemListener.java @@ -0,0 +1,62 @@ +/* ItemListener.java -- listen for item events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.ItemSelectable; +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive events when an + * item's selection state changes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ItemSelectable + * @see ItemEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ItemListener extends EventListener +{ + /** + * This method is called when an item's state is changed. + * + * @param event the <code>ItemEvent</code> indicating the change + */ + void itemStateChanged(ItemEvent event); +} // interface ItemListener diff --git a/libjava/classpath/java/awt/event/KeyAdapter.java b/libjava/classpath/java/awt/event/KeyAdapter.java new file mode 100644 index 00000000000..c01d61ff339 --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyAdapter.java @@ -0,0 +1,88 @@ +/* KeyAdapter.java -- convenience class for writing key listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>KeyListener</code> and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of the <code>KeyListener</code> interface to extend this class + * and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyEvent + * @see KeyListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class KeyAdapter implements KeyListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public KeyAdapter() + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyTyped(KeyEvent event) + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyPressed(KeyEvent event) + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyReleased(KeyEvent event) + { + } +} // class KeyAdapter diff --git a/libjava/classpath/java/awt/event/KeyEvent.java b/libjava/classpath/java/awt/event/KeyEvent.java new file mode 100644 index 00000000000..a40a8e15c04 --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyEvent.java @@ -0,0 +1,1740 @@ +/* KeyEvent.java -- event for key presses + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; + +import java.awt.Component; +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * This event is generated when a key is pressed or released. There are two + * categories of key events: + * + * <p><em>"Key typed" events</em> are higher-level, and have already + * compensated for modifiers and keyboard layout to generate a single Unicode + * character. It may take several key press events to generate one key typed. + * The <code>getKeyCode</code> method will return <code>VK_UNDEFINED</code>, + * and <code>getKeyChar</code> will return a valid Unicode character or + * <code>CHAR_UNDEFINED</code>. + * + * <p><em>"Key pressed" and "key released" events</em> are lower-level, and + * are platform and keyboard dependent. They correspond to the actaul motion + * on a keyboard, and return a virtual key code which labels the key that was + * pressed. The <code>getKeyCode</code> method will return one of the + * <code>VK_*</code> constants (except VK_UNDEFINED), and the + * <code>getKeyChar</code> method is undefined. + * + * <p>Some keys do not generate key typed events, such as the F1 or HELP keys. + * Not all keyboards can generate all virtual keys, and no attempt is made to + * simulate the ones that can't be typed. Virtual keys correspond to the + * keyboard layout, so for example, VK_Q in English is VK_A in French. Also, + * there are some additional virtual keys to ease handling of actions, such + * as VK_ALL_CANDIDATES in place of ALT+VK_CONVERT. Do not rely on the value + * of the VK_* constants, except for VK_ENTER, VK_BACK_SPACE, and VK_TAB. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyAdapter + * @see KeyListener + * @since 1.1 + * @status updated to 1.4 + */ +public class KeyEvent extends InputEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2352130953028126954L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int KEY_FIRST = 400; + + /** This is the last id in the range of event ids used by this class. */ + public static final int KEY_LAST = 402; + + /** + * This event id indicates a key was typed, which is a key press followed + * by a key release to generate an actual Unicode character. It may take + * several key presses to generate one key typed event, and some action + * keys have no corresponding key typed. + */ + public static final int KEY_TYPED = 400; + + /** This event id indicates a key was pressed. */ + public static final int KEY_PRESSED = 401; + + /** This event it indicates a key was released. */ + public static final int KEY_RELEASED = 402; + + /** The virtual key Enter, which will always map to '\n'. */ + public static final int VK_ENTER = '\n'; + + /** The virtual key Backspace, which will always map to '\b'. */ + public static final int VK_BACK_SPACE = '\b'; + + /** The virtual key Tab, which will always map to '\t'. */ + public static final int VK_TAB = '\t'; + + /** The virtual key Cancel. */ + public static final int VK_CANCEL = 3; + + /** The virtual key VK_CLEAR. */ + public static final int VK_CLEAR = 12; + + /** The virtual key VK_SHIFT. */ + public static final int VK_SHIFT = 16; + + /** The virtual key VK_CONTROL. */ + public static final int VK_CONTROL = 17; + + /** The virtual key VK_ALT. */ + public static final int VK_ALT = 18; + + /** The virtual key VK_PAUSE. */ + public static final int VK_PAUSE = 19; + + /** The virtual key VK_CAPS_LOCK. */ + public static final int VK_CAPS_LOCK = 20; + + /** The virtual key VK_ESCAPE. */ + public static final int VK_ESCAPE = 27; + + /** The virtual key VK_SPACE. */ + public static final int VK_SPACE = ' '; + + /** The virtual key VK_PAGE_UP. */ + public static final int VK_PAGE_UP = 33; + + /** The virtual key VK_PAGE_DOWN. */ + public static final int VK_PAGE_DOWN = 34; + + /** The virtual key VK_END. */ + public static final int VK_END = 35; + + /** The virtual key VK_HOME. */ + public static final int VK_HOME = 36; + + /** + * The virtual key for the non-numpad VK_LEFT. + * + * @see #VK_KP_LEFT + */ + public static final int VK_LEFT = 37; + + /** + * The virtual key for the non-numpad VK_UP. + * + * @see #VK_KP_UP + */ + public static final int VK_UP = 38; + + /** + * The virtual key for the non-numpad VK_RIGHT. + * + * @see #VK_KP_RIGHT + */ + public static final int VK_RIGHT = 39; + + /** + * The virtual key for the non-numpad VK_DOWN. + * + * @see #VK_KP_DOWN + */ + public static final int VK_DOWN = 40; + + /** The virtual key VK_COMMA. */ + public static final int VK_COMMA = ','; + + /** + * The virtual key VK_MINUS. + * + * @since 1.2 + */ + public static final int VK_MINUS = '-'; + + /** The virtual key VK_PERIOD. */ + public static final int VK_PERIOD = '.'; + + /** The virtual key VK_SLASH. */ + public static final int VK_SLASH = '/'; + + /** The virtual key VK_0. */ + public static final int VK_0 = '0'; + + /** The virtual key VK_1. */ + public static final int VK_1 = '1'; + + /** The virtual key VK_2. */ + public static final int VK_2 = '2'; + + /** The virtual key VK_3. */ + public static final int VK_3 = '3'; + + /** The virtual key VK_4. */ + public static final int VK_4 = '4'; + + /** The virtual key VK_5. */ + public static final int VK_5 = '5'; + + /** The virtual key VK_6. */ + public static final int VK_6 = '6'; + + /** The virtual key VK_7. */ + public static final int VK_7 = '7'; + + /** The virtual key VK_8. */ + public static final int VK_8 = '8'; + + /** The virtual key VK_9. */ + public static final int VK_9 = '9'; + + /** The virtual key VK_SEMICOLON. */ + public static final int VK_SEMICOLON = ';'; + + /** The virtual key VK_EQUALS. */ + public static final int VK_EQUALS = '='; + + /** The virtual key VK_A. */ + public static final int VK_A = 'A'; + + /** The virtual key VK_B. */ + public static final int VK_B = 'B'; + + /** The virtual key VK_C. */ + public static final int VK_C = 'C'; + + /** The virtual key VK_D. */ + public static final int VK_D = 'D'; + + /** The virtual key VK_E. */ + public static final int VK_E = 'E'; + + /** The virtual key VK_F. */ + public static final int VK_F = 'F'; + + /** The virtual key VK_G. */ + public static final int VK_G = 'G'; + + /** The virtual key VK_H. */ + public static final int VK_H = 'H'; + + /** The virtual key VK_I. */ + public static final int VK_I = 'I'; + + /** The virtual key VK_J. */ + public static final int VK_J = 'J'; + + /** The virtual key VK_K. */ + public static final int VK_K = 'K'; + + /** The virtual key VK_L. */ + public static final int VK_L = 'L'; + + /** The virtual key VK_M. */ + public static final int VK_M = 'M'; + + /** The virtual key VK_N. */ + public static final int VK_N = 'N'; + + /** The virtual key VK_O. */ + public static final int VK_O = 'O'; + + /** The virtual key VK_P. */ + public static final int VK_P = 'P'; + + /** The virtual key VK_Q. */ + public static final int VK_Q = 'Q'; + + /** The virtual key VK_R. */ + public static final int VK_R = 'R'; + + /** The virtual key VK_S. */ + public static final int VK_S = 'S'; + + /** The virtual key VK_T. */ + public static final int VK_T = 'T'; + + /** The virtual key VK_U. */ + public static final int VK_U = 'U'; + + /** The virtual key VK_V. */ + public static final int VK_V = 'V'; + + /** The virtual key VK_W. */ + public static final int VK_W = 'W'; + + /** The virtual key VK_X. */ + public static final int VK_X = 'X'; + + /** The virtual key VK_Y. */ + public static final int VK_Y = 'Y'; + + /** The virtual key VK_Z. */ + public static final int VK_Z = 'Z'; + + /** The virtual key VK_OPEN_BRACKET. */ + public static final int VK_OPEN_BRACKET = '['; + + /** The virtual key VK_BACK_SLASH. */ + public static final int VK_BACK_SLASH = '\\'; + + /** The virtual key VK_CLOSE_BRACKET. */ + public static final int VK_CLOSE_BRACKET = ']'; + + /** The virtual key VK_NUMPAD0. */ + public static final int VK_NUMPAD0 = 96; + + /** The virtual key VK_NUMPAD1. */ + public static final int VK_NUMPAD1 = 97; + + /** The virtual key VK_NUMPAD2. */ + public static final int VK_NUMPAD2 = 98; + + /** The virtual key VK_NUMPAD3. */ + public static final int VK_NUMPAD3 = 99; + + /** The virtual key VK_NUMPAD4. */ + public static final int VK_NUMPAD4 = 100; + + /** The virtual key VK_NUMPAD5. */ + public static final int VK_NUMPAD5 = 101; + + /** The virtual key VK_NUMPAD6. */ + public static final int VK_NUMPAD6 = 102; + + /** The virtual key VK_NUMPAD7. */ + public static final int VK_NUMPAD7 = 103; + + /** The virtual key VK_NUMPAD8. */ + public static final int VK_NUMPAD8 = 104; + + /** The virtual key VK_NUMPAD9. */ + public static final int VK_NUMPAD9 = 105; + + /** The virtual key VK_MULTIPLY. */ + public static final int VK_MULTIPLY = 106; + + /** The virtual key VK_ADD. */ + public static final int VK_ADD = 107; + + /** + * The virtual key VK_SEPARATOR, handily mispelled for those who can't + * figure it out. + * + * @deprecated use {@link #VK_SEPARATOR} + */ + public static final int VK_SEPARATER = 108; + + /** + * The virtual key VK_SEPARATOR. + * + * @since 1.4 + */ + public static final int VK_SEPARATOR = 108; + + /** The virtual key VK_SUBTRACT. */ + public static final int VK_SUBTRACT = 109; + + /** The virtual key VK_DECIMAL. */ + public static final int VK_DECIMAL = 110; + + /** The virtual key VK_DIVIDE. */ + public static final int VK_DIVIDE = 111; + + /** The virtual key VK_DELETE. */ + public static final int VK_DELETE = 127; + + /** The virtual key VK_NUM_LOCK. */ + public static final int VK_NUM_LOCK = 144; + + /** The virtual key VK_SCROLL_LOCK. */ + public static final int VK_SCROLL_LOCK = 145; + + /** The virtual key VK_F1. */ + public static final int VK_F1 = 112; + + /** The virtual key VK_F2. */ + public static final int VK_F2 = 113; + + /** The virtual key VK_F3. */ + public static final int VK_F3 = 114; + + /** The virtual key VK_F4. */ + public static final int VK_F4 = 115; + + /** The virtual key VK_F5. */ + public static final int VK_F5 = 116; + + /** The virtual key VK_F6. */ + public static final int VK_F6 = 117; + + /** The virtual key VK_F7. */ + public static final int VK_F7 = 118; + + /** The virtual key VK_F8. */ + public static final int VK_F8 = 119; + + /** The virtual key VK_F9. */ + public static final int VK_F9 = 120; + + /** The virtual key VK_F10. */ + public static final int VK_F10 = 121; + + /** The virtual key VK_F11. */ + public static final int VK_F11 = 122; + + /** The virtual key VK_F12. */ + public static final int VK_F12 = 123; + + /** + * The virtual key VK_F13. + * + * @since 1.2 + */ + public static final int VK_F13 = 61440; + + /** + * The virtual key VK_F14. + * + * @since 1.2 + */ + public static final int VK_F14 = 61441; + + /** + * The virtual key VK_F15. + * + * @since 1.2 + */ + public static final int VK_F15 = 61442; + + /** + * The virtual key VK_F16. + * + * @since 1.2 + */ + public static final int VK_F16 = 61443; + + /** + * The virtual key VK_F17. + * + * @since 1.2 + */ + public static final int VK_F17 = 61444; + + /** + * The virtual key VK_F18. + * + * @since 1.2 + */ + public static final int VK_F18 = 61445; + + /** + * The virtual key VK_F19. + * + * @since 1.2 + */ + public static final int VK_F19 = 61446; + + /** + * The virtual key VK_F20. + * + * @since 1.2 + */ + public static final int VK_F20 = 61447; + + /** + * The virtual key VK_F21. + * + * @since 1.2 + */ + public static final int VK_F21 = 61448; + + /** + * The virtual key VK_F22. + * + * @since 1.2 + */ + public static final int VK_F22 = 61449; + + /** + * The virtual key VK_F23. + * + * @since 1.2 + */ + public static final int VK_F23 = 61450; + + /** + * The virtual key VK_F24. + * + * @since 1.2 + */ + public static final int VK_F24 = 61451; + + /** The virtual key VK_PRINTSCREEN. */ + public static final int VK_PRINTSCREEN = 154; + + /** The virtual key VK_INSERT. */ + public static final int VK_INSERT = 155; + + /** The virtual key VK_HELP. */ + public static final int VK_HELP = 156; + + /** The virtual key VK_META. */ + public static final int VK_META = 157; + + /** The virtual key VK_BACK_QUOTE. */ + public static final int VK_BACK_QUOTE = 192; + + /** The virtual key VK_QUOTE. */ + public static final int VK_QUOTE = 222; + + /** + * The virtual key for the numpad VK_KP_UP. + * + * @see #VK_UP + * @since 1.2 + */ + public static final int VK_KP_UP = 224; + + /** + * The virtual key for the numpad VK_KP_DOWN. + * + * @see #VK_DOWN + * @since 1.2 + */ + public static final int VK_KP_DOWN = 225; + + /** + * The virtual key for the numpad VK_KP_LEFT. + * + * @see #VK_LEFT + * @since 1.2 + */ + public static final int VK_KP_LEFT = 226; + + /** + * The virtual key for the numpad VK_KP_RIGHT. + * + * @see #VK_RIGHT + * @since 1.2 + */ + public static final int VK_KP_RIGHT = 227; + + /** + * The virtual key VK_DEAD_GRAVE. + * + * @since 1.2 + */ + public static final int VK_DEAD_GRAVE = 128; + + /** + * The virtual key VK_DEAD_ACUTE. + * + * @since 1.2 + */ + public static final int VK_DEAD_ACUTE = 129; + + /** + * The virtual key VK_DEAD_CIRCUMFLEX. + * + * @since 1.2 + */ + public static final int VK_DEAD_CIRCUMFLEX = 130; + + /** + * The virtual key VK_DEAD_TILDE. + * + * @since 1.2 + */ + public static final int VK_DEAD_TILDE = 131; + + /** + * The virtual key VK_DEAD_MACRON. + * + * @since 1.2 + */ + public static final int VK_DEAD_MACRON = 132; + + /** + * The virtual key VK_DEAD_BREVE. + * + * @since 1.2 + */ + public static final int VK_DEAD_BREVE = 133; + + /** + * The virtual key VK_DEAD_ABOVEDOT. + * + * @since 1.2 + */ + public static final int VK_DEAD_ABOVEDOT = 134; + + /** + * The virtual key VK_DEAD_DIAERESIS. + * + * @since 1.2 + */ + public static final int VK_DEAD_DIAERESIS = 135; + + /** + * The virtual key VK_DEAD_ABOVERING. + * + * @since 1.2 + */ + public static final int VK_DEAD_ABOVERING = 136; + + /** + * The virtual key VK_DEAD_DOUBLEACUTE. + * + * @since 1.2 + */ + public static final int VK_DEAD_DOUBLEACUTE = 137; + + /** + * The virtual key VK_DEAD_CARON. + * + * @since 1.2 + */ + public static final int VK_DEAD_CARON = 138; + + /** + * The virtual key VK_DEAD_CEDILLA. + * + * @since 1.2 + */ + public static final int VK_DEAD_CEDILLA = 139; + + /** + * The virtual key VK_DEAD_OGONEK. + * + * @since 1.2 + */ + public static final int VK_DEAD_OGONEK = 140; + + /** + * The virtual key VK_DEAD_IOTA. + * + * @since 1.2 + */ + public static final int VK_DEAD_IOTA = 141; + + /** + * The virtual key VK_DEAD_VOICED_SOUND. + * + * @since 1.2 + */ + public static final int VK_DEAD_VOICED_SOUND = 142; + + /** + * The virtual key VK_DEAD_SEMIVOICED_SOUND. + * + * @since 1.2 + */ + public static final int VK_DEAD_SEMIVOICED_SOUND = 143; + + /** + * The virtual key VK_AMPERSAND. + * + * @since 1.2 + */ + public static final int VK_AMPERSAND = 150; + + /** + * The virtual key VK_ASTERISK. + * + * @since 1.2 + */ + public static final int VK_ASTERISK = 151; + + /** + * The virtual key VK_QUOTEDBL. + * + * @since 1.2 + */ + public static final int VK_QUOTEDBL = 152; + + /** + * The virtual key VK_LESS. + * + * @since 1.2 + */ + public static final int VK_LESS = 153; + + /** + * The virtual key VK_GREATER. + * + * @since 1.2 + */ + public static final int VK_GREATER = 160; + + /** + * The virtual key VK_BRACELEFT. + * + * @since 1.2 + */ + public static final int VK_BRACELEFT = 161; + + /** + * The virtual key VK_BRACERIGHT. + * + * @since 1.2 + */ + public static final int VK_BRACERIGHT = 162; + + /** + * The virtual key VK_AT. + * + * @since 1.2 + */ + public static final int VK_AT = 512; + + /** + * The virtual key VK_COLON. + * + * @since 1.2 + */ + public static final int VK_COLON = 513; + + /** + * The virtual key VK_CIRCUMFLEX. + * + * @since 1.2 + */ + public static final int VK_CIRCUMFLEX = 514; + + /** + * The virtual key VK_DOLLAR. + * + * @since 1.2 + */ + public static final int VK_DOLLAR = 515; + + /** + * The virtual key VK_EURO_SIGN. + * + * @since 1.2 + */ + public static final int VK_EURO_SIGN = 516; + + /** + * The virtual key VK_EXCLAMATION_MARK. + * + * @since 1.2 + */ + public static final int VK_EXCLAMATION_MARK = 517; + + /** + * The virtual key VK_INVERTED_EXCLAMATION_MARK. + * + * @since 1.2 + */ + public static final int VK_INVERTED_EXCLAMATION_MARK = 518; + + /** + * The virtual key VK_LEFT_PARENTHESIS. + * + * @since 1.2 + */ + public static final int VK_LEFT_PARENTHESIS = 519; + + /** + * The virtual key VK_NUMBER_SIGN. + * + * @since 1.2 + */ + public static final int VK_NUMBER_SIGN = 520; + + /** + * The virtual key VK_PLUS. + * + * @since 1.2 + */ + public static final int VK_PLUS = 521; + + /** + * The virtual key VK_RIGHT_PARENTHESIS. + * + * @since 1.2 + */ + public static final int VK_RIGHT_PARENTHESIS = 522; + + /** + * The virtual key VK_UNDERSCORE. + * + * @since 1.2 + */ + public static final int VK_UNDERSCORE = 523; + + /** The virtual key VK_FINAL. */ + public static final int VK_FINAL = 24; + + /** The virtual key VK_CONVERT. */ + public static final int VK_CONVERT = 28; + + /** The virtual key VK_NONCONVERT. */ + public static final int VK_NONCONVERT = 29; + + /** The virtual key VK_ACCEPT. */ + public static final int VK_ACCEPT = 30; + + /** The virtual key VK_MODECHANGE. */ + public static final int VK_MODECHANGE = 31; + + /** The virtual key VK_KANA. */ + public static final int VK_KANA = 21; + + /** The virtual key VK_KANJI. */ + public static final int VK_KANJI = 25; + + /** + * The virtual key VK_ALPHANUMERIC. + * + * @since 1.2 + */ + public static final int VK_ALPHANUMERIC = 240; + + /** + * The virtual key VK_KATAKANA. + * + * @since 1.2 + */ + public static final int VK_KATAKANA = 241; + + /** + * The virtual key VK_HIRAGANA. + * + * @since 1.2 + */ + public static final int VK_HIRAGANA = 242; + + /** + * The virtual key VK_FULL_WIDTH. + * + * @since 1.2 + */ + public static final int VK_FULL_WIDTH = 243; + + /** + * The virtual key VK_HALF_WIDTH. + * + * @since 1.2 + */ + public static final int VK_HALF_WIDTH = 244; + + /** + * The virtual key VK_ROMAN_CHARACTERS. + * + * @since 1.2 + */ + public static final int VK_ROMAN_CHARACTERS = 245; + + /** + * The virtual key VK_ALL_CANDIDATES. + * + * @since 1.2 + */ + public static final int VK_ALL_CANDIDATES = 256; + + /** + * The virtual key VK_PREVIOUS_CANDIDATE. + * + * @since 1.2 + */ + public static final int VK_PREVIOUS_CANDIDATE = 257; + + /** + * The virtual key VK_CODE_INPUT. + * + * @since 1.2 + */ + public static final int VK_CODE_INPUT = 258; + + /** + * The virtual key VK_JAPANESE_KATAKANA. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_KATAKANA = 259; + + /** + * The virtual key VK_JAPANESE_HIRAGANA. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_HIRAGANA = 260; + + /** + * The virtual key VK_JAPANESE_ROMAN. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_ROMAN = 261; + + /** + * The virtual key VK_KANA_LOCK. + * + * @since 1.3 + */ + public static final int VK_KANA_LOCK = 262; + + /** + * The virtual key VK_INPUT_METHOD_ON_OFF. + * + * @since 1.3 + */ + public static final int VK_INPUT_METHOD_ON_OFF = 263; + + /** + * The virtual key VK_CUT. + * + * @since 1.2 + */ + public static final int VK_CUT = 65489; + + /** + * The virtual key VK_COPY. + * + * @since 1.2 + */ + public static final int VK_COPY = 65485; + + /** + * The virtual key VK_PASTE. + * + * @since 1.2 + */ + public static final int VK_PASTE = 65487; + + /** + * The virtual key VK_UNDO. + * + * @since 1.2 + */ + public static final int VK_UNDO = 65483; + + /** + * The virtual key VK_AGAIN. + * + * @since 1.2 + */ + public static final int VK_AGAIN = 65481; + + /** + * The virtual key VK_FIND. + * + * @since 1.2 + */ + public static final int VK_FIND = 65488; + + /** + * The virtual key VK_PROPS. + * + * @since 1.2 + */ + public static final int VK_PROPS = 65482; + + /** + * The virtual key VK_STOP. + * + * @since 1.2 + */ + public static final int VK_STOP = 65480; + + /** + * The virtual key VK_COMPOSE. + * + * @since 1.2 + */ + public static final int VK_COMPOSE = 65312; + + /** + * The virtual key VK_ALT_GRAPH. + * + * @since 1.2 + */ + public static final int VK_ALT_GRAPH = 65406; + + /** + * The virtual key VK_UNDEFINED. This is used for key typed events, which + * do not have a virtual key. + */ + public static final int VK_UNDEFINED = 0; + + /** + * The only char with no valid Unicode interpretation. This is used for + * key pressed and key released events which do not have a valid keyChar. + */ + public static final char CHAR_UNDEFINED = '\uffff'; + + /** + * Indicates unknown or irrelavent key location. This is also used for + * key typed events, which do not need a location. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_UNKNOWN = 0; + + /** + * Indicates a standard key location, with no left/right variants and not + * on the numeric pad. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_STANDARD = 1; + + /** + * Indicates the key is on the left side of the keyboard, such as the left + * shift. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_LEFT = 2; + + /** + * Indicates the key is on the right side of the keyboard, such as the right + * shift. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_RIGHT = 3; + + /** + * Indicates the key is on the numeric pad, such as the numpad 0. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_NUMPAD = 4; + + /** + * The code assigned to the physical keyboard location (as adjusted by the + * keyboard layout). Use the symbolic VK_* names instead of numbers. + * + * @see #getKeyCode() + * @serial the VK_ code for this key + */ + private int keyCode; + + /** + * The Unicode character produced by the key type event. This has no meaning + * for key pressed and key released events. + * + * @see #getKeyChar() + * @serial the Unicode value for this key + */ + private char keyChar; + + /** + * The keyboard location of the key. One of {@link #KEY_LOCATION_UNKNOWN}, + * {@link #KEY_LOCATION_STANDARD}, {@link #KEY_LOCATION_LEFT}, + * {@link #KEY_LOCATION_RIGHT}, or {@link #KEY_LOCATION_NUMPAD}. + * + * @see #getKeyLocation() + * @serial the key location + * @since 1.4 + */ + private final int keyLocation; + + /** + * Stores the state of the native event dispatching system, to correctly + * dispatch in Component#dispatchEventImpl when a proxy is active. + * + * XXX Does this matter in Classpath? + * + * @serial whether the proxy is active + */ + private boolean isProxyActive; + + + /** + * Initializes a new instance of <code>KeyEvent</code> with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @param keyChar the Unicode value of the key + * @param keyLocation the location of the key + * @throws IllegalArgumentException if source is null, if keyLocation is + * invalid, or if (id == KEY_TYPED && (keyCode != VK_UNDEFINED + * || keyChar == CHAR_UNDEFINED)) + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode, char keyChar, int keyLocation) + { + super(source, id, when, modifiers); + this.keyCode = keyCode; + this.keyChar = keyChar; + this.keyLocation = keyLocation; + if ((id == KEY_TYPED && (keyCode != VK_UNDEFINED + || keyChar == CHAR_UNDEFINED)) + || keyLocation < KEY_LOCATION_UNKNOWN + || keyLocation > KEY_LOCATION_NUMPAD) + throw new IllegalArgumentException(); + } + + /** + * Initializes a new instance of <code>KeyEvent</code> with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @param keyChar the Unicode value of the key + * @throws IllegalArgumentException if source is null, or if + * (id == KEY_TYPED && (keyCode != VK_UNDEFINED + * || keyChar == CHAR_UNDEFINED)) + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode, char keyChar) + { + this(source, id, when, modifiers, keyCode, keyChar, KEY_LOCATION_UNKNOWN); + } + + /** + * Initializes a new instance of <code>KeyEvent</code> with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @throws IllegalArgumentException if source is null, or if + * id == KEY_TYPED but keyCode != VK_UNDEFINED + * + * @deprecated + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode) + { + this(source, id, when, modifiers, keyCode, '\0', KEY_LOCATION_UNKNOWN); + } + + /** + * Returns the key code for the event key. This will be one of the + * <code>VK_*</code> constants defined in this class. If the event type is + * KEY_TYPED, the result will be VK_UNDEFINED. + * + * @return the key code for this event + */ + public int getKeyCode() + { + return keyCode; + } + + /** + * Sets the key code for this event. This must be one of the + * <code>VK_*</code> constants defined in this class. + * + * @param keyCode the new key code for this event + */ + public void setKeyCode(int keyCode) + { + this.keyCode = keyCode; + } + + /** + * Returns the Unicode value for the event key. This will be + * <code>CHAR_UNDEFINED</code> if there is no Unicode equivalent for + * this key, usually when this is a KEY_PRESSED or KEY_RELEASED event. + * + * @return the Unicode character for this event + */ + public char getKeyChar() + { + return keyChar; + } + + /** + * Sets the Unicode character for this event to the specified value. + * + * @param keyChar the new Unicode character for this event + */ + public void setKeyChar(char keyChar) + { + this.keyChar = keyChar; + } + + /** + * Sets the modifier keys to the specified value. This should be a union + * of the bit mask constants from <code>InputEvent</code>. The use of this + * method is not recommended, particularly for KEY_TYPED events, which do + * not check if the modifiers were changed. + * + * @param modifiers the new modifier value, in either old or new style + * @see InputEvent + * + * @deprecated + */ + public void setModifiers(int modifiers) + { + this.modifiers = EventModifier.extend(modifiers); + } + + /** + * Returns the keyboard location of the key that generated this event. This + * provides a way to distinguish between keys like left and right shift + * which share a common key code. The result will be one of + * {@link #KEY_LOCATION_UNKNOWN}, {@link #KEY_LOCATION_STANDARD}, + * {@link #KEY_LOCATION_LEFT}, {@link #KEY_LOCATION_RIGHT}, or + * {@link #KEY_LOCATION_NUMPAD}. + * + * @return the key location + * @since 1.4 + */ + public int getKeyLocation() + { + return keyLocation; + } + + /** + * Returns the text name of key code, such as "HOME", "F1", or "A". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @return the text name of the key code + */ + public static String getKeyText(int keyCode) + { + switch (keyCode) + { + case VK_CANCEL: + return "Cancel"; + case VK_BACK_SPACE: + return "Backspace"; + case VK_TAB: + return "Tab"; + case VK_ENTER: + return "Enter"; + case VK_CLEAR: + return "Clear"; + case VK_SHIFT: + return "Shift"; + case VK_CONTROL: + return "Ctrl"; + case VK_ALT: + return "Alt"; + case VK_PAUSE: + return "Pause"; + case VK_CAPS_LOCK: + return "Caps Lock"; + case VK_KANA: + return "Kana"; + case VK_FINAL: + return "Final"; + case VK_KANJI: + return "Kanji"; + case VK_ESCAPE: + return "Escape"; + case VK_CONVERT: + return "Convert"; + case VK_NONCONVERT: + return "No Convert"; + case VK_ACCEPT: + return "Accept"; + case VK_MODECHANGE: + return "Mode Change"; + case VK_SPACE: + return "Space"; + case VK_PAGE_UP: + return "Page Up"; + case VK_PAGE_DOWN: + return "Page Down"; + case VK_END: + return "End"; + case VK_HOME: + return "Home"; + case VK_LEFT: + case VK_KP_LEFT: + return "Left"; + case VK_UP: + case VK_KP_UP: + return "Up"; + case VK_RIGHT: + case VK_KP_RIGHT: + return "Right"; + case VK_DOWN: + case VK_KP_DOWN: + return "Down"; + case VK_MINUS: + return "Minus"; + case VK_MULTIPLY: + return "NumPad *"; + case VK_ADD: + return "NumPad +"; + case VK_SEPARATOR: + return "NumPad ,"; + case VK_SUBTRACT: + return "NumPad -"; + case VK_DECIMAL: + return "NumPad ."; + case VK_DIVIDE: + return "NumPad /"; + case VK_DELETE: + return "Delete"; + case VK_DEAD_GRAVE: + return "Dead Grave"; + case VK_DEAD_ACUTE: + return "Dead Acute"; + case VK_DEAD_CIRCUMFLEX: + return "Dead Circumflex"; + case VK_DEAD_TILDE: + return "Dead Tilde"; + case VK_DEAD_MACRON: + return "Dead Macron"; + case VK_DEAD_BREVE: + return "Dead Breve"; + case VK_DEAD_ABOVEDOT: + return "Dead Above Dot"; + case VK_DEAD_DIAERESIS: + return "Dead Diaeresis"; + case VK_DEAD_ABOVERING: + return "Dead Above Ring"; + case VK_DEAD_DOUBLEACUTE: + return "Dead Double Acute"; + case VK_DEAD_CARON: + return "Dead Caron"; + case VK_DEAD_CEDILLA: + return "Dead Cedilla"; + case VK_DEAD_OGONEK: + return "Dead Ogonek"; + case VK_DEAD_IOTA: + return "Dead Iota"; + case VK_DEAD_VOICED_SOUND: + return "Dead Voiced Sound"; + case VK_DEAD_SEMIVOICED_SOUND: + return "Dead Semivoiced Sound"; + case VK_NUM_LOCK: + return "Num Lock"; + case VK_SCROLL_LOCK: + return "Scroll Lock"; + case VK_AMPERSAND: + return "Ampersand"; + case VK_ASTERISK: + return "Asterisk"; + case VK_QUOTEDBL: + return "Double Quote"; + case VK_LESS: + return "Less"; + case VK_PRINTSCREEN: + return "Print Screen"; + case VK_INSERT: + return "Insert"; + case VK_HELP: + return "Help"; + case VK_META: + return "Meta"; + case VK_GREATER: + return "Greater"; + case VK_BRACELEFT: + return "Left Brace"; + case VK_BRACERIGHT: + return "Right Brace"; + case VK_BACK_QUOTE: + return "Back Quote"; + case VK_QUOTE: + return "Quote"; + case VK_ALPHANUMERIC: + return "Alphanumeric"; + case VK_KATAKANA: + return "Katakana"; + case VK_HIRAGANA: + return "Hiragana"; + case VK_FULL_WIDTH: + return "Full-Width"; + case VK_HALF_WIDTH: + return "Half-Width"; + case VK_ROMAN_CHARACTERS: + return "Roman Characters"; + case VK_ALL_CANDIDATES: + return "All Candidates"; + case VK_PREVIOUS_CANDIDATE: + return "Previous Candidate"; + case VK_CODE_INPUT: + return "Code Input"; + case VK_JAPANESE_KATAKANA: + return "Japanese Katakana"; + case VK_JAPANESE_HIRAGANA: + return "Japanese Hiragana"; + case VK_JAPANESE_ROMAN: + return "Japanese Roman"; + case VK_KANA_LOCK: + return "Kana Lock"; + case VK_INPUT_METHOD_ON_OFF: + return "Input Method On/Off"; + case VK_AT: + return "At"; + case VK_COLON: + return "Colon"; + case VK_CIRCUMFLEX: + return "Circumflex"; + case VK_DOLLAR: + return "Dollar"; + case VK_EURO_SIGN: + return "Euro"; + case VK_EXCLAMATION_MARK: + return "Exclamation Mark"; + case VK_INVERTED_EXCLAMATION_MARK: + return "Inverted Exclamation Mark"; + case VK_LEFT_PARENTHESIS: + return "Left Parenthesis"; + case VK_NUMBER_SIGN: + return "Number Sign"; + case VK_PLUS: + return "Plus"; + case VK_RIGHT_PARENTHESIS: + return "Right Parenthesis"; + case VK_UNDERSCORE: + return "Underscore"; + case VK_COMPOSE: + return "Compose"; + case VK_ALT_GRAPH: + return "Alt Graph"; + case VK_STOP: + return "Stop"; + case VK_AGAIN: + return "Again"; + case VK_PROPS: + return "Props"; + case VK_UNDO: + return "Undo"; + case VK_COPY: + return "Copy"; + case VK_PASTE: + return "Paste"; + case VK_FIND: + return "Find"; + case VK_CUT: + return "Cut"; + case VK_COMMA: + case VK_PERIOD: + case VK_SLASH: + case VK_0: + case VK_1: + case VK_2: + case VK_3: + case VK_4: + case VK_5: + case VK_6: + case VK_7: + case VK_8: + case VK_9: + case VK_SEMICOLON: + case VK_EQUALS: + case VK_A: + case VK_B: + case VK_C: + case VK_D: + case VK_E: + case VK_F: + case VK_G: + case VK_H: + case VK_I: + case VK_J: + case VK_K: + case VK_L: + case VK_M: + case VK_N: + case VK_O: + case VK_P: + case VK_Q: + case VK_R: + case VK_S: + case VK_T: + case VK_U: + case VK_V: + case VK_W: + case VK_X: + case VK_Y: + case VK_Z: + case VK_OPEN_BRACKET: + case VK_BACK_SLASH: + case VK_CLOSE_BRACKET: + return "" + (char) keyCode; + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + return "NumPad-" + (keyCode - VK_NUMPAD0); + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + return "F" + (keyCode - (VK_F1 - 1)); + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + return "F" + (keyCode - (VK_F13 - 13)); + default: + // This is funky on negative numbers, but that's Sun's fault. + return "Unknown keyCode: 0x" + (keyCode < 0 ? "-" : "") + + Integer.toHexString(Math.abs(keyCode)); + } + } + + /** + * Returns a string describing the modifiers, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the old-style modifiers to convert to text + * @return a string representation of the modifiers in this bitmask + */ + public static String getKeyModifiersText(int modifiers) + { + return getModifiersExText(EventModifier.extend(modifiers + & EventModifier.OLD_MASK)); + } + + /** + * Tests whether or not this key is an action key. An action key typically + * does not fire a KEY_TYPED event, and is not a modifier. + * + * @return true if this is an action key + */ + public boolean isActionKey() + { + switch (keyCode) + { + case VK_PAUSE: + case VK_CAPS_LOCK: + case VK_KANA: + case VK_FINAL: + case VK_KANJI: + case VK_CONVERT: + case VK_NONCONVERT: + case VK_ACCEPT: + case VK_MODECHANGE: + case VK_PAGE_UP: + case VK_PAGE_DOWN: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + case VK_NUM_LOCK: + case VK_SCROLL_LOCK: + case VK_PRINTSCREEN: + case VK_INSERT: + case VK_HELP: + case VK_KP_UP: + case VK_KP_DOWN: + case VK_KP_LEFT: + case VK_KP_RIGHT: + case VK_ALPHANUMERIC: + case VK_KATAKANA: + case VK_HIRAGANA: + case VK_FULL_WIDTH: + case VK_HALF_WIDTH: + case VK_ROMAN_CHARACTERS: + case VK_ALL_CANDIDATES: + case VK_PREVIOUS_CANDIDATE: + case VK_CODE_INPUT: + case VK_JAPANESE_KATAKANA: + case VK_JAPANESE_HIRAGANA: + case VK_JAPANESE_ROMAN: + case VK_KANA_LOCK: + case VK_INPUT_METHOD_ON_OFF: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + case VK_STOP: + case VK_AGAIN: + case VK_PROPS: + case VK_UNDO: + case VK_COPY: + case VK_PASTE: + case VK_FIND: + case VK_CUT: + return true; + default: + return false; + } + } + + /** + * Returns a string identifying the event. This is formatted as the + * field name of the id type, followed by the keyCode, then the + * keyChar, modifiers (if any), extModifiers (if any), and + * keyLocation. + * + * @return a string identifying the event + */ + public String paramString() + { + StringBuffer s = new StringBuffer(); + + switch (id) + { + case KEY_PRESSED: + s.append("KEY_PRESSED"); + break; + case KEY_RELEASED: + s.append("KEY_RELEASED"); + break; + case KEY_TYPED: + s.append("KEY_TYPED"); + break; + default: + s.append("unknown type"); + } + + s.append(",keyCode=").append(keyCode); + + s.append(",keyText=").append(getKeyText(keyCode)); + + s.append(",keyChar="); + if (isActionKey() + || keyCode == VK_SHIFT + || keyCode == VK_CONTROL + || keyCode == VK_ALT) + s.append("Undefined keyChar"); + else + { + /* This output string must be selected by examining keyChar + * rather than keyCode, because key code information is not + * included in KEY_TYPED events. + */ + if (keyChar == VK_BACK_SPACE + || keyChar == VK_TAB + || keyChar == VK_ENTER + || keyChar == VK_ESCAPE + || keyChar == VK_DELETE) + s.append(getKeyText(keyChar)); + else + s.append("'").append(keyChar).append("'"); + } + + if ((modifiers & CONVERT_MASK) != 0) + s.append(",modifiers=").append(getModifiersExText(modifiers + & CONVERT_MASK)); + if (modifiers != 0) + s.append(",extModifiers=").append(getModifiersExText(modifiers)); + + s.append(",keyLocation=KEY_LOCATION_"); + switch (keyLocation) + { + case KEY_LOCATION_UNKNOWN: + s.append("UNKNOWN"); + break; + case KEY_LOCATION_STANDARD: + s.append("STANDARD"); + break; + case KEY_LOCATION_LEFT: + s.append("LEFT"); + break; + case KEY_LOCATION_RIGHT: + s.append("RIGHT"); + break; + case KEY_LOCATION_NUMPAD: + s.append("NUMPAD"); + } + + return s.toString(); + } + + /** + * Reads in the object from a serial stream. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except that the modifiers are converted to new style + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + modifiers = EventModifier.extend(modifiers); + } +} // class KeyEvent diff --git a/libjava/classpath/java/awt/event/KeyListener.java b/libjava/classpath/java/awt/event/KeyListener.java new file mode 100644 index 00000000000..5c0a640f67e --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyListener.java @@ -0,0 +1,77 @@ +/* KeyListener.java -- listen for keyboard presses + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive keyboard events. To + * watch a subset of these events, use a KeyAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyAdapter + * @see KeyEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface KeyListener extends EventListener +{ + /** + * This method is called when a key is typed. A key is considered typed + * when it and all modifiers have been pressed and released, mapping to + * a single virtual key. + * + * @param event the <code>KeyEvent</code> indicating that a key was typed + */ + void keyTyped(KeyEvent event); + + /** + * This method is called when a key is pressed. + * + * @param event the <code>KeyEvent</code> indicating the key press + */ + void keyPressed(KeyEvent event); + + /** + * This method is called when a key is released. + * + * @param event the <code>KeyEvent</code> indicating the key release + */ + void keyReleased(KeyEvent event); +} // interface KeyListener diff --git a/libjava/classpath/java/awt/event/MouseAdapter.java b/libjava/classpath/java/awt/event/MouseAdapter.java new file mode 100644 index 00000000000..9f40c285ac7 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseAdapter.java @@ -0,0 +1,106 @@ +/* MouseAdapter.java -- convenience class for writing mouse listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>MouseListener</code> and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of the <code>MouseListener</code> interface to extend this class + * and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseEvent + * @see MouseListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class MouseAdapter implements MouseListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public MouseAdapter() + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseClicked(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mousePressed(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseReleased(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseEntered(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseExited(MouseEvent event) + { + } +} // class MouseAdapter diff --git a/libjava/classpath/java/awt/event/MouseEvent.java b/libjava/classpath/java/awt/event/MouseEvent.java new file mode 100644 index 00000000000..249c3d112e4 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseEvent.java @@ -0,0 +1,432 @@ +/* MouseEvent.java -- a mouse event + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; + +import java.awt.Component; +import java.awt.Point; +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * This event is generated for a mouse event. There are three main categories + * of mouse events: Regular events include pressing, releasing, and clicking + * buttons, as well as moving over the boundary of the unobscured portion of + * a component. Motion events include movement and dragging. Wheel events are + * covered separately by the subclass MouseWheelEvent. + * + * <p>A mouse event is tied to the unobstructed visible component that the + * mouse cursor was over at the time of the action. The button that was + * most recently pressed is the only one that shows up in + * <code>getModifiers</code>, and is returned by <code>getButton</code>, + * while all buttons that are down show up in <code>getModifiersEx</code>. + * + * <p>Drag events may be cut short if native drag-and-drop operations steal + * the event. Likewise, if a mouse drag exceeds the bounds of a window or + * virtual device, some platforms may clip the path to fit in the bounds of + * the component. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseAdapter + * @see MouseListener + * @see MouseMotionAdapter + * @see MouseMotionListener + * @see MouseWheelListener + * @since 1.1 + * @status updated to 1.4 + */ +public class MouseEvent extends InputEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -991214153494842848L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int MOUSE_FIRST = 500; + + /** This is the last id in the range of event ids used by this class. */ + public static final int MOUSE_LAST = 507; + + /** This event id indicates that the mouse was clicked. */ + public static final int MOUSE_CLICKED = 500; + + /** This event id indicates that the mouse was pressed. */ + public static final int MOUSE_PRESSED = 501; + + /** This event id indicates that the mouse was released. */ + public static final int MOUSE_RELEASED = 502; + + /** This event id indicates that the mouse was moved. */ + public static final int MOUSE_MOVED = 503; + + /** This event id indicates that the mouse entered a component. */ + public static final int MOUSE_ENTERED = 504; + + /** This event id indicates that the mouse exited a component. */ + public static final int MOUSE_EXITED = 505; + + /** + * This indicates that no button changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int NOBUTTON = 0; + + /** + * This indicates that button 1 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON1 = 1; + + /** + * This indicates that button 2 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON2 = 2; + + /** + * This indicates that button 3 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON3 = 3; + + /** This event id indicates that the mouse was dragged over a component. */ + public static final int MOUSE_DRAGGED = 506; + + /** + * This event id indicates that the mouse wheel was rotated. + * + * @since 1.4 + */ + public static final int MOUSE_WHEEL = 507; + + /** + * The X coordinate of the mouse cursor at the time of the event. + * + * @see #getX() + * @serial the x coordinate + */ + private int x; + + /** + * The Y coordinate of the mouse cursor at the time of the event. + * + * @see #getY() + * @serial the y coordinate + */ + private int y; + + /** + * The number of clicks that took place. For MOUSE_CLICKED, MOUSE_PRESSED, + * and MOUSE_RELEASED, this will be at least 1; otherwise it is 0. + * + * see #getClickCount() + * @serial the number of clicks + */ + private final int clickCount; + + /** + * Indicates which mouse button changed state. Can only be one of + * {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or + * {@link #BUTTON3}. + * + * @see #getButton() + * @since 1.4 + */ + private int button; + + /** + * Whether or not this event should trigger a popup menu. + * + * @see PopupMenu + * @see #isPopupTrigger() + * @serial true if this is a popup trigger + */ + private final boolean popupTrigger; + + /** + * Initializes a new instance of <code>MouseEvent</code> with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @param button the most recent mouse button to change state + * @throws IllegalArgumentException if source is null or button is invalid + * @since 1.4 + */ + public MouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, + int button) + { + super(source, id, when, modifiers); + this.x = x; + this.y = y; + this.clickCount = clickCount; + this.popupTrigger = popupTrigger; + this.button = button; + if (button < NOBUTTON || button > BUTTON3) + throw new IllegalArgumentException(); + if ((modifiers & EventModifier.OLD_MASK) != 0) + { + if ((modifiers & BUTTON1_MASK) != 0) + this.button = BUTTON1; + else if ((modifiers & BUTTON2_MASK) != 0) + this.button = BUTTON2; + else if ((modifiers & BUTTON3_MASK) != 0) + this.button = BUTTON3; + } + } + + /** + * Initializes a new instance of <code>MouseEvent</code> with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @throws IllegalArgumentException if source is null + */ + public MouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger) + { + this(source, id, when, modifiers, x, y, clickCount, popupTrigger, + NOBUTTON); + } + + /** + * This method returns the X coordinate of the mouse position. This is + * relative to the source component. + * + * @return the x coordinate + */ + public int getX() + { + return x; + } + + /** + * This method returns the Y coordinate of the mouse position. This is + * relative to the source component. + * + * @return the y coordinate + */ + public int getY() + { + return y; + } + + /** + * This method returns a <code>Point</code> for the x,y position of + * the mouse pointer. This is relative to the source component. + * + * @return a <code>Point</code> for the event position + */ + public Point getPoint() + { + return new Point(x, y); + } + + /** + * Translates the event coordinates by the specified x and y offsets. + * + * @param dx the value to add to the X coordinate of this event + * @param dy the value to add to the Y coordiante of this event + */ + public void translatePoint(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * This method returns the number of mouse clicks associated with this + * event. + * + * @return the number of mouse clicks for this event + */ + public int getClickCount() + { + return clickCount; + } + + /** + * Returns which button, if any, was the most recent to change state. This + * will be one of {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or + * {@link #BUTTON3}. + * + * @return the button that changed state + * @since 1.4 + */ + public int getButton() + { + return button; + } + + /** + * This method tests whether or not the event is a popup menu trigger. This + * should be checked in both MousePressed and MouseReleased to be + * cross-platform compatible, as different systems have different popup + * triggers. + * + * @return true if the event is a popup menu trigger + */ + public boolean isPopupTrigger() + { + return popupTrigger; + } + + /** + * Returns a string describing the modifiers, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the old-style modifiers to convert to text + * @return a string representation of the modifiers in this bitmask + */ + public static String getMouseModifiersText(int modifiers) + { + modifiers &= EventModifier.OLD_MASK; + if ((modifiers & BUTTON2_MASK) != 0) + modifiers |= BUTTON2_DOWN_MASK; + if ((modifiers & BUTTON3_MASK) != 0) + modifiers |= BUTTON3_DOWN_MASK; + return getModifiersExText(EventModifier.extend(modifiers)); + } + + /** + * Returns a string identifying this event. This is formatted as the field + * name of the id type, followed by the (x,y) point, the most recent button + * changed, modifiers (if any), extModifiers (if any), and clickCount. + * + * @return a string identifying this event + */ + public String paramString() + { + StringBuffer s = new StringBuffer(); + switch (id) + { + case MOUSE_CLICKED: + s.append("MOUSE_CLICKED,("); + break; + case MOUSE_PRESSED: + s.append("MOUSE_PRESSED,("); + break; + case MOUSE_RELEASED: + s.append("MOUSE_RELEASED,("); + break; + case MOUSE_MOVED: + s.append("MOUSE_MOVED,("); + break; + case MOUSE_ENTERED: + s.append("MOUSE_ENTERED,("); + break; + case MOUSE_EXITED: + s.append("MOUSE_EXITED,("); + break; + case MOUSE_DRAGGED: + s.append("MOUSE_DRAGGED,("); + break; + case MOUSE_WHEEL: + s.append("MOUSE_WHEEL,("); + break; + default: + s.append("unknown type,("); + } + s.append(x).append(',').append(y).append("),button=").append(button); + if ((modifiers & EventModifier.NEW_MASK) != 0) + { + int mod = modifiers; + if ((mod & (ALT_DOWN_MASK | BUTTON2_DOWN_MASK)) != 0) + mod |= ALT_DOWN_MASK | BUTTON2_DOWN_MASK; + if ((mod & (META_DOWN_MASK | BUTTON3_DOWN_MASK)) != 0) + mod |= META_DOWN_MASK | BUTTON3_DOWN_MASK; + s.append(",modifiers=").append(getModifiersExText(mod)); + } + if (modifiers != 0) + s.append(",extModifiers=").append(getModifiersExText(modifiers)); + return s.append(",clickCount=").append(clickCount).toString(); + } + + /** + * Reads in the object from a serial stream. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except that the modifiers are converted to new style + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + if ((modifiers & EventModifier.OLD_MASK) != 0) + { + if ((modifiers & BUTTON1_MASK) != 0) + button = BUTTON1; + else if ((modifiers & BUTTON2_MASK) != 0) + button = BUTTON2; + else if ((modifiers & BUTTON3_MASK) != 0) + button = BUTTON3; + modifiers = EventModifier.extend(modifiers); + } + } +} // class MouseEvent diff --git a/libjava/classpath/java/awt/event/MouseListener.java b/libjava/classpath/java/awt/event/MouseListener.java new file mode 100644 index 00000000000..4508019e108 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseListener.java @@ -0,0 +1,94 @@ +/* MouseListener.java -- listen for mouse clicks and crossing component edges + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive mouse events other than + * simple motion events. This includes clicks (but not mouse wheel events), + * and crossing component boundaries without change in button status. To + * track moves and drags, use MouseMotionListener, and to track wheel events, + * use MouseWheelListener. To watch a subset of these events, use a + * MouseAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseAdapter + * @see MouseEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface MouseListener extends EventListener +{ + /** + * This method is called when the mouse is clicked (pressed and released + * in short succession) on a component. + * + * @param event the <code>MouseEvent</code> indicating the click + */ + void mouseClicked(MouseEvent event); + + /** + * This method is called when the mouse is pressed over a component. + * + * @param event the <code>MouseEvent</code> for the press + */ + void mousePressed(MouseEvent event); + + /** + * This method is called when the mouse is released over a component. + * + * @param event the <code>MouseEvent</code> for the release + */ + void mouseReleased(MouseEvent event); + + /** + * This method is called when the mouse enters a component. + * + * @param event the <code>MouseEvent</code> for the entry + */ + void mouseEntered(MouseEvent event); + + /** + * This method is called when the mouse exits a component. + * + * @param event the <code>MouseEvent</code> for the exit + */ + void mouseExited(MouseEvent event); +} // interface MouseListener diff --git a/libjava/classpath/java/awt/event/MouseMotionAdapter.java b/libjava/classpath/java/awt/event/MouseMotionAdapter.java new file mode 100644 index 00000000000..8a295f66cc0 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseMotionAdapter.java @@ -0,0 +1,79 @@ +/* MouseMotionAdapter.java -- convenience class for mouse motion listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>MouseMotionListener</code> and implements all + * methods with empty bodies. This allows a listener interested in + * implementing only a subset of the <code>MouseMotionListener</code> + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseEvent + * @see MouseMotionListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class MouseMotionAdapter implements MouseMotionListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public MouseMotionAdapter() + { + } + + /** + * Implement this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void mouseDragged(MouseEvent event) + { + } + + /** + * Implement this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void mouseMoved(MouseEvent event) + { + } +} // class MouseMotionAdapter diff --git a/libjava/classpath/java/awt/event/MouseMotionListener.java b/libjava/classpath/java/awt/event/MouseMotionListener.java new file mode 100644 index 00000000000..ba2c5698b11 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseMotionListener.java @@ -0,0 +1,72 @@ +/* MouseMotionListener.java -- listen to mouse motion events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified of mouse movements. + * This includes moves and drags, but not crossing component boundaries. To + * track other mouse events, use MouseListener or MouseWheelListener. To + * watch a subset of these events, use a MouseMotionAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseMotionAdapter + * @see MouseEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface MouseMotionListener extends EventListener +{ + /** + * This method is called when the mouse is moved over a component + * while a button has been pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + void mouseDragged(MouseEvent event); + + /** + * This method is called when the mouse is moved over a component + * while no button is pressed. + * + * @param event the <code>MouseEvent</code> indicating the motion + */ + void mouseMoved(MouseEvent event); +} // interface MouseMotionListener diff --git a/libjava/classpath/java/awt/event/MouseWheelEvent.java b/libjava/classpath/java/awt/event/MouseWheelEvent.java new file mode 100644 index 00000000000..bc603aaa979 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseWheelEvent.java @@ -0,0 +1,232 @@ +/* MouseWheelEvent.java -- a mouse wheel event + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Adjustable; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.ScrollPane; + +import javax.swing.JScrollPane; +import javax.swing.Scrollable; + +/** + * This event is generated for a mouse wheel rotation. The wheel (the middle + * mouse button on most modern mice) can be rotated towards or away from the + * user, and is ofteh used for scrolling. + * + * <p>Because of the special use for scrolling components, MouseWheelEvents + * often affect a different component than the one located at the point of + * the event. If the component under the mouse cursor does not accept wheel + * events, the event is passed to the first ancestor container which does. This + * is often a ScrollPane, which knows how to scroll. If an AWT component is + * built from a native widget that knows how to use mouse wheel events, that + * component will consume the event. + * + * <p>The two most common scroll types are "units" (lines at a time) or + * "blocks" (pages at a time). The initial setting is taken from the platform, + * although the user can adjust the setting at any time. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseWheelListener + * @see ScrollPane + * @see ScrollPane#setWheelScrollingEnabled(boolean) + * @see JScrollPane + * @see JScrollPane#setWheelScrollingEnabled(boolean) + * @since 1.4 + * @status updated to 1.4 + */ +public class MouseWheelEvent extends MouseEvent +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6459879390515399677L; + + /** + * Indicates scrolling by units (lines). + * + * @see #getScrollType() + */ + public static final int WHEEL_UNIT_SCROLL = 0; + + /** + * Indicates scrolling by blocks (pages). + * + * @see #getScrollType() + */ + public static final int WHEEL_BLOCK_SCROLL = 1; + + /** + * Indicates what scroll type should take place. This should be limited + * to {@link #WHEEL_UNIT_SCROLL} and {@link #WHEEL_BLOCK_SCROLL}. + * + * @serial the scroll type + */ + private final int scrollType; + + /** + * Indicates the scroll amount. This is only meaningful if scrollType is + * WHEEL_UNIT_SCROLL. + * + * @serial the number of lines to scroll + */ + private final int scrollAmount; + + /** + * Indicates how far the mouse wheel was rotated. + * + * @serial the rotation amount + */ + private final int wheelRotation; + + /** + * Initializes a new instance of <code>MouseWheelEvent</code> with the + * specified information. Note that an invalid id leads to unspecified + * results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers any modifier bits for this event + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @param scrollType one of {@link #WHEEL_UNIT_SCROLL}, + * {@link #WHEEL_BLOCK_SCROLL} + * @param scrollAmount the number of units to scroll, ignored for block type + * @param wheelRotation the number of rotation "clicks" + * @throws IllegalArgumentException if source is null + * @see MouseEvent#MouseEvent(Component, int, long, int, int, int, int, + * boolean) + */ + public MouseWheelEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, + int scrollType, int scrollAmount, int wheelRotation) + { + super(source, id, when, modifiers, x, y, clickCount, popupTrigger); + this.scrollType = scrollType; + this.scrollAmount = scrollAmount; + this.wheelRotation = wheelRotation; + } + + /** + * This method returns the scrolling pattern this event requests. Legal + * values are {@link #WHEEL_UNIT_SCROLL} and {@link #WHEEL_BLOCK_SCROLL}. + * + * @return the scroll type + * @see Adjustable#getUnitIncrement() + * @see Adjustable#getBlockIncrement() + * @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int) + * @see Scrollable#getScrollableBlockIncrement(Rectangle, int, int) + */ + public int getScrollType() + { + return scrollType; + } + + /** + * Returns the number of units to scroll in response to this event. This + * only makes sense when the scroll type is WHEEL_UNIT_SCROLL. + * + * @return the number of scroll units, if defined + * @see #getScrollType() + */ + public int getScrollAmount() + { + return scrollAmount; + } + + /** + * Gets the number of "clicks" the wheel was rotated. Negative values move + * up (away) from the user, positive values move down (towards) the user. + * + * @return the number of rotation clicks + */ + public int getWheelRotation() + { + return wheelRotation; + } + + /** + * This is a convenience method which aids in a common listener for scrolling + * a scrollpane (although this is already built into ScrollPane and + * JScrollPane). This method only makes sense when getScrollType() returns + * WHEEL_UNIT_SCROLL. + * + * <p>This accounts for direction of scroll and amount of wheel movement, as + * interpreted by the platform settings. + * + * @return the number of units to scroll + * @see #getScrollType() + * @see #getScrollAmount() + * @see MouseWheelListener + * @see Adjustable + * @see Adjustable#getUnitIncrement() + * @see Scrollable + * @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int) + * @see ScrollPane + * @see ScrollPane#setWheelScrollingEnabled(boolean) + * @see JScrollPane + * @see JScrollPane#setWheelScrollingEnabled(boolean) + */ + public int getUnitsToScroll() + { + return wheelRotation * scrollAmount; + } + + /** + * Returns a string identifying this event. For mouse wheel events, this + * is <code>super.paramString() + ",scrollType=WHEEL_" + + * (getScrollType() == WHEEL_UNIT_SCROLL ? "UNIT" : "BLOCK") + * + "_SCROLL,scrollAmount=" + getScrollAmount() + ",wheelRotation=" + * + getWheelRotation()</code>. + * + * @return a string identifying this event + */ + public String paramString() + { + return super.paramString() + ",scrollType=" + + (scrollType == WHEEL_UNIT_SCROLL ? "WHEEL_UNIT_SCROLL" + : scrollType == WHEEL_BLOCK_SCROLL ? "WHEEL_BLOCK_SCROLL" + : "unknown scroll type") + + ",scrollAmount=" + scrollAmount + ",wheelRotation=" + wheelRotation; + } +} // class MouseWheelEvent diff --git a/libjava/classpath/java/awt/event/MouseWheelListener.java b/libjava/classpath/java/awt/event/MouseWheelListener.java new file mode 100644 index 00000000000..1125582e1df --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseWheelListener.java @@ -0,0 +1,60 @@ +/* MouseWheelListener.java -- listen for mouse wheel events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive mouse wheel events. For + * other events, use MouseListener or MouseMotionListener. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseWheelEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface MouseWheelListener extends EventListener +{ + /** + * This method is called when the mouse wheel is rotated. + * + * @param event the <code>MouseWheelEvent</code> indicating the rotation + */ + void mouseWheelMoved(MouseWheelEvent event); +} // interface MouseWheelListener diff --git a/libjava/classpath/java/awt/event/PaintEvent.java b/libjava/classpath/java/awt/event/PaintEvent.java new file mode 100644 index 00000000000..bb89c3722b5 --- /dev/null +++ b/libjava/classpath/java/awt/event/PaintEvent.java @@ -0,0 +1,127 @@ +/* PaintEvent.java -- an area of the screen needs to be repainted + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; +import java.awt.Rectangle; + +/** + * This event is generated when an area of the screen needs to be painted. + * This event is not meant for users, but exists to allow proper serialization + * behavior in the EventQueue with user-accessible events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class PaintEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 1267492026433337593L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int PAINT_FIRST = 800; + + /** This is the last id in the range of event ids used by this class. */ + public static final int PAINT_LAST = 801; + + /** This id is for paint event types. */ + public static final int PAINT = 800; + + /** This id is for update event types. */ + public static final int UPDATE = 801; + + /** + * This is the rectange to be painted or updated. + * + * @see #getUpdateRect() + * @see #setUpdateRect(Rectangle) + * @serial the non-null rectangle to be painted + */ + private Rectangle updateRect; + + /** + * Initializes a new instance of <code>PaintEvent</code> with the specified + * source, id, and update region. Note that an invalid id leads to + * unspecified results. + * + * @param source the event source + * @param id the event id + * @param updateRect the rectangle to repaint + * @throws IllegalArgumentException if source is null + */ + public PaintEvent(Component source, int id, Rectangle updateRect) + { + super(source, id); + this.updateRect = updateRect; + } + + /** + * Returns the rectange to be updated for this event. + * + * @return the rectangle to update + */ + public Rectangle getUpdateRect() + { + return updateRect; + } + + /** + * Sets the rectangle to be updated for this event. + * + * @param updateRect the new update rectangle for this event + */ + public void setUpdateRect(Rectangle updateRect) + { + this.updateRect = updateRect; + } + + /** + * Returns a string identifying this event. + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == PAINT ? "PAINT,updateRect=" : id == UPDATE + ? "UPDATE,updateRect=" : "unknown type,updateRect=") + updateRect; + } +} // class PaintEvent diff --git a/libjava/classpath/java/awt/event/TextEvent.java b/libjava/classpath/java/awt/event/TextEvent.java new file mode 100644 index 00000000000..0288abbb752 --- /dev/null +++ b/libjava/classpath/java/awt/event/TextEvent.java @@ -0,0 +1,93 @@ +/* TextEvent.java -- event for text changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.TextComponent; + +/** + * This event is generated when a text box changes contents. This is an + * abstraction that distills a large number of individual mouse or keyboard + * events into a simpler "text changed" event. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see TextComponent + * @see TextListener + * @since 1.1 + * @status updated to 1.4 + */ +public class TextEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6269902291250941179L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int TEXT_FIRST = 900; + + /** This is the last id in the range of event ids used by this class. */ + public static final int TEXT_LAST = 900; + + /** This event id indicates that the text of an object has changed. */ + public static final int TEXT_VALUE_CHANGED = 900; + + /** + * Initializes a new instance of <code>TextEvent</code> with the specified + * source and id. Note that an invalid id leads to unspecified results. + * + * @param source the (TextComponent) object that generated this event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public TextEvent(Object source, int id) + { + super(source, id); + } + + /** + * Returns a string identifying this event. This is "TEXT_VALUE_CHANGED". + * + * @return a string identifying this event + */ + public String paramString() + { + return id == TEXT_VALUE_CHANGED ? "TEXT_VALUE_CHANGED" : "unknown type"; + } +} // class TextEvent diff --git a/libjava/classpath/java/awt/event/TextListener.java b/libjava/classpath/java/awt/event/TextListener.java new file mode 100644 index 00000000000..bcdd7fa7a11 --- /dev/null +++ b/libjava/classpath/java/awt/event/TextListener.java @@ -0,0 +1,60 @@ +/* TextListener.java -- listen for text changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified when text changes + * in a component. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see TextEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface TextListener extends EventListener +{ + /** + * This method is called when the text being monitored changes. + * + * @param event the <code>TextEvent</code> indicating the change + */ + void textValueChanged(TextEvent event); +} // interface TextListener diff --git a/libjava/classpath/java/awt/event/WindowAdapter.java b/libjava/classpath/java/awt/event/WindowAdapter.java new file mode 100644 index 00000000000..708de588c05 --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowAdapter.java @@ -0,0 +1,156 @@ +/* WindowAdapter.java -- convenience class for writing window listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements <code>WindowListener</code>, + * <code>WindowStateListener</code>, and <code>WindowFocusListener</code>, and + * implements all methods with empty bodies. This allows a listener + * interested in listening to only a subset of any <code>WindowEvent</code> + * actions to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentEvent + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class WindowAdapter + implements WindowListener, WindowStateListener, WindowFocusListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public WindowAdapter() + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowOpened(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowClosing(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowClosed(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowIconified(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowDeiconified(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowActivated(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowDeactivated(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowStateChanged(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowGainedFocus(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowLostFocus(WindowEvent event) + { + } +} // class WindowAdapter diff --git a/libjava/classpath/java/awt/event/WindowEvent.java b/libjava/classpath/java/awt/event/WindowEvent.java new file mode 100644 index 00000000000..2186889e601 --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowEvent.java @@ -0,0 +1,312 @@ +/* WindowEvent.java -- window change event + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Frame; +import java.awt.Window; + +/** + * This event is generated when there is a change in a window. This includes + * creation, closing, iconification, activation, and focus changes. There + * are three listeners, for three types of events: WindowListeners deal with + * the lifecycle of a window, WindowStateListeners deal with window state + * like maximization, and WindowFocusListeners deal with focus switching to + * or from a window. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see WindowAdapter + * @see WindowListener + * @see WindowFocusListener + * @see WindowStateListener + * @since 1.1 + * @status updated to 1.4 + */ +public class WindowEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1567959133147912127L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int WINDOW_FIRST = 200; + + /** This is the id for a window that is opened. */ + public static final int WINDOW_OPENED = 200; + + /** This is the id for a window that is about to close. */ + public static final int WINDOW_CLOSING = 201; + + /** This is the id for a window that finished closing. */ + public static final int WINDOW_CLOSED = 202; + + /** This is the id for a window that is iconified. */ + public static final int WINDOW_ICONIFIED = 203; + + /** This is the id for a window that is de-iconified. */ + public static final int WINDOW_DEICONIFIED = 204; + + /** This is the id for a window that is activated. */ + public static final int WINDOW_ACTIVATED = 205; + + /** This is the id for a window that is de-activated. */ + public static final int WINDOW_DEACTIVATED = 206; + + /** + * This is the id for a window becoming the focused window. + * + * @since 1.4 + */ + public static final int WINDOW_GAINED_FOCUS = 207; + + /** + * This is the id for a window losing all focus. + * + * @since 1.4 + */ + public static final int WINDOW_LOST_FOCUS = 208; + + /** + * This is the id for a window state change, such as maximization. + * + * @since 1.4 + */ + public static final int WINDOW_STATE_CHANGED = 209; + + /** This is the last id in the range of event ids used by this class. */ + public static final int WINDOW_LAST = 209; + + /** + * The other Window involved in a focus or activation change. For + * WINDOW_ACTIVATED and WINDOW_GAINED_FOCUS events, this is the window that + * lost focus; for WINDOW_DEACTIVATED and WINDOW_LOST_FOCUS, this is the + * window that stole focus; and for other events (or when native + * implementation does not have the data available), this is null. + * + * @see #getOppositeWindow() + * @serial the opposite window, or null + * @since 1.4 + */ + private final Window opposite; + + /** + * The former state of the window. + * + * @serial bitmask of the old window state + * @since 1.4 + */ + private final int oldState; + + /** + * The present state of the window. + * + * @serial bitmask of the new window state + * @since 1.4 + */ + private final int newState; + + /** + * Initializes a new instance of <code>WindowEvent</code> with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param opposite the window that received the opposite event, or null + * @param oldState the previous state of this window + * @param newState the new state of this window + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, Window opposite, + int oldState, int newState) + { + super(source, id); + this.opposite = opposite; + this.oldState = oldState; + this.newState = newState; + } + + /** + * Initializes a new instance of <code>WindowEvent</code> with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param opposite the window that received the opposite event, or null + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, Window opposite) + { + this(source, id, opposite, 0, 0); + } + + /** + * Initializes a new instance of <code>WindowEvent</code> with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param oldState the previous state of this window + * @param newState the new state of this window + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, int oldState, int newState) + { + this(source, id, null, oldState, newState); + } + + /** + * Initializes a new instance of <code>WindowEvent</code> with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public WindowEvent(Window source, int id) + { + this(source, id, null, 0, 0); + } + + /** + * Returns the event source as a <code>Window</code>. If the source has + * subsequently been modified to a non-Window, this returns null. + * + * @return the event source as a <code>Window</code> + */ + public Window getWindow() + { + return source instanceof Window ? (Window) source : null; + } + + /** + * Returns the opposite window if this window was involved in an activation + * or focus change. For WINDOW_ACTIVATED and WINDOW_GAINED_FOCUS events, + * this is the window that lost focus; for WINDOW_DEACTIVATED and + * WINDOW_LOST_FOCUS, this is the window that stole focus; and for other + * events (or when native implementation does not have the data available), + * this is null. + * + * @return the opposite window, or null + * @since 1.4 + */ + public Window getOppositeWindow() + { + return opposite; + } + + /** + * Returns the state of this window before the event. This is the bitwise + * or of fields in Frame: NORMAL, ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT, + * and MAXIMIZED_BOTH. + * + * @return the former state + * @see Frame#getExtendedState() + * @since 1.4 + */ + public int getOldState() + { + return oldState; + } + + /** + * Returns the state of this window after the event. This is the bitwise + * or of fields in Frame: NORMAL, ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT, + * and MAXIMIZED_BOTH. + * + * @return the updated state + * @see Frame#getExtendedState() + * @since 1.4 + */ + public int getNewState() + { + return newState; + } + + /** + * Returns a string that identifies this event. This is formatted as the + * field name of the id, followed by the opposite window, old state, and + * new state. + * + * @return a string that identifies this event + */ + public String paramString() + { + StringBuffer s = new StringBuffer(); + switch (id) + { + case WINDOW_OPENED: + s.append("WINDOW_OPENED,opposite="); + break; + case WINDOW_CLOSING: + s.append("WINDOW_CLOSING,opposite="); + break; + case WINDOW_CLOSED: + s.append("WINDOW_CLOSED,opposite="); + break; + case WINDOW_ICONIFIED: + s.append("WINDOW_ICONIFIED,opposite="); + break; + case WINDOW_DEICONIFIED: + s.append("WINDOW_DEICONIFIED,opposite="); + break; + case WINDOW_ACTIVATED: + s.append("WINDOW_ACTIVATED,opposite="); + break; + case WINDOW_DEACTIVATED: + s.append("WINDOW_DEACTIVATED,opposite="); + break; + case WINDOW_GAINED_FOCUS: + s.append("WINDOW_GAINED_FOCUS,opposite="); + break; + case WINDOW_LOST_FOCUS: + s.append("WINDOW_LOST_FOCUS,opposite="); + break; + case WINDOW_STATE_CHANGED: + s.append("WINDOW_STATE_CHANGED,opposite="); + break; + default: + s.append("unknown type,opposite="); + } + return s.append(opposite).append(",oldState=").append(oldState) + .append(",newState=").append(newState).toString(); + } +} // class WindowEvent diff --git a/libjava/classpath/java/awt/event/WindowFocusListener.java b/libjava/classpath/java/awt/event/WindowFocusListener.java new file mode 100644 index 00000000000..738425353e3 --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowFocusListener.java @@ -0,0 +1,68 @@ +/* WindowFocusListener.java -- listens for window focus events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * focus changes. To watch a subset of these events, use a WindowAdapter. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see WindowAdapter + * @see WindowEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface WindowFocusListener extends EventListener +{ + /** + * This method is called when a window gains focus. + * + * @param event the <code>WindowEvent</code> indicating the focus change + */ + void windowGainedFocus(WindowEvent event); + + /** + * This method is called when a window loses focus. + * + * @param event the <code>WindowEvent</code> indicating the focus change + */ + void windowLostFocus(WindowEvent event); +} // interface WindowFocusListener diff --git a/libjava/classpath/java/awt/event/WindowListener.java b/libjava/classpath/java/awt/event/WindowListener.java new file mode 100644 index 00000000000..52213eb3d8c --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowListener.java @@ -0,0 +1,109 @@ +/* WindowListener.java -- listens for window events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Frame; +import java.awt.Image; +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * changes. To watch a subset of these events, use a WindowAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see WindowAdapter + * @see WindowEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface WindowListener extends EventListener +{ + /** + * This method is called when the window is made visible. + * + * @param event the <code>WindowEvent</code> indicating the change + */ + void windowOpened(WindowEvent event); + + /** + * This method is called when the user calls the system menu close + * function, giving the program a chance to cancel the close. + * + * @param event the <code>WindowEvent</code> indicating the close attempt + */ + void windowClosing(WindowEvent event); + + /** + * This method is called when the window is closed. + * + * @param event the <code>WindowEvent</code> indicating the dispose + */ + void windowClosed(WindowEvent event); + + /** + * This method is called when the window is iconified. + * + * @param event the <code>WindowEvent</code> indicating the iconification + * @see Frame#setIconImage(Image) + */ + void windowIconified(WindowEvent event); + + /** + * This method is called when the window is deiconified. + * + * @param event the <code>WindowEvent</code> indicating the deiconification + */ + void windowDeiconified(WindowEvent event); + + /** + * This method is called when a window is activated. Only Frames and Dialogs + * can be active, and the active window always contains the component with + * focus. + * + * @param event the <code>WindowEvent</code> indicating the activation + */ + void windowActivated(WindowEvent event); + + /** + * This method is called when the window is deactivated. + * + * @param event the <code>WindowEvent</code> indicating the deactivation + */ + void windowDeactivated(WindowEvent event); +} // interface WindowListener diff --git a/libjava/classpath/java/awt/event/WindowStateListener.java b/libjava/classpath/java/awt/event/WindowStateListener.java new file mode 100644 index 00000000000..9bc6174fda1 --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowStateListener.java @@ -0,0 +1,62 @@ +/* WindowStateListener.java -- listens for window state changes + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * state changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see WindowAdapter + * @see WindowEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface WindowStateListener extends EventListener +{ + /** + * This method is called when the window state is changed, because of + * iconification or maximization. + * + * @param event the <code>WindowEvent</code> indicating the change + */ + void windowStateChanged(WindowEvent event); +} // interface WindowStateListener diff --git a/libjava/classpath/java/awt/event/package.html b/libjava/classpath/java/awt/event/package.html new file mode 100644 index 00000000000..77662a3fb81 --- /dev/null +++ b/libjava/classpath/java/awt/event/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.event package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.event</title></head> + +<body> +<p>Listeners and adapters for different kinds of AWT events.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/font/FontRenderContext.java b/libjava/classpath/java/awt/font/FontRenderContext.java new file mode 100644 index 00000000000..78564a647da --- /dev/null +++ b/libjava/classpath/java/awt/font/FontRenderContext.java @@ -0,0 +1,126 @@ +/* FontRenderContext.java + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.AffineTransform; + +/** + * @author Michael Koch + */ +public class FontRenderContext +{ + private AffineTransform affineTransform; + private boolean isAntiAliased; + private boolean usesFractionalMetrics; + + /** + * Construct a new <code>FontRenderContext</code>. + */ + protected FontRenderContext() + { + // Do nothing here. + } + + /** + * Construct a new <code>FontRenderContext</code>. + */ + public FontRenderContext (AffineTransform tx, boolean isAntiAliased, + boolean usesFractionalMetrics) + { + if (tx != null + && !tx.isIdentity ()) + { + this.affineTransform = new AffineTransform (tx); + } + + this.isAntiAliased = isAntiAliased; + this.usesFractionalMetrics = usesFractionalMetrics; + } + + public boolean equals (Object obj) + { + if (! (obj instanceof FontRenderContext)) + return false; + + return equals ((FontRenderContext) obj); + } + + public boolean equals (FontRenderContext rhs) + { + return (affineTransform.equals (rhs.getTransform ()) + && isAntiAliased == rhs.isAntiAliased () + && usesFractionalMetrics == rhs.usesFractionalMetrics ()); + } + + + /** + * Retrieves the affine transform for scaling typographical points + * to raster pixels. + * + * @return a clone of the transform object. + */ + public AffineTransform getTransform () + { + if (affineTransform == null) + return new AffineTransform (); + else + return new AffineTransform (affineTransform); + } + + + /** + * Returns the hash code of the font render context. + */ + public int hashCode () + { + // FIXME: check what SUN does here. + return affineTransform == null ? 0 : affineTransform.hashCode (); + } + + public boolean isAntiAliased () + { + return isAntiAliased; + } + + public boolean usesFractionalMetrics () + { + return usesFractionalMetrics; + } +} + diff --git a/libjava/classpath/java/awt/font/GlyphJustificationInfo.java b/libjava/classpath/java/awt/font/GlyphJustificationInfo.java new file mode 100644 index 00000000000..5f45fd58498 --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphJustificationInfo.java @@ -0,0 +1,77 @@ +/* GlyphJustificationInfo.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public final class GlyphJustificationInfo +{ + public static final int PRIORITY_KASHIDA = 0; + public static final int PRIORITY_WHITESPACE = 1; + public static final int PRIORITY_INTERCHAR = 2; + public static final int PRIORITY_NONE = 3; + + public final float weight; + public final int growPriority; + public final boolean growAbsorb; + public final float growLeftLimit; + public final float growRightLimit; + public final int shrinkPriority; + public final boolean shrinkAbsorb; + public final float shrinkLeftLimit; + public final float shrinkRightLimit; + + public GlyphJustificationInfo (float weight, boolean growAbsorb, + int growPriority, float growLeftLimit, + float growRightLimit, boolean shrinkAbsorb, + int shrinkPriority, float shrinkLeftLimit, + float shrinkRightLimit) + { + this.weight = weight; + this.growAbsorb = growAbsorb; + this.growPriority = growPriority; + this.growLeftLimit = growLeftLimit; + this.growRightLimit = growRightLimit; + this.shrinkAbsorb = shrinkAbsorb; + this.shrinkPriority = shrinkPriority; + this.shrinkLeftLimit = shrinkLeftLimit; + this.shrinkRightLimit = shrinkRightLimit; + } +} diff --git a/libjava/classpath/java/awt/font/GlyphMetrics.java b/libjava/classpath/java/awt/font/GlyphMetrics.java new file mode 100644 index 00000000000..28b2088cf8e --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphMetrics.java @@ -0,0 +1,134 @@ +/* GlyphMetrics.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.Rectangle2D; + +/** + * @author Michael Koch + */ +public final class GlyphMetrics +{ + public static final byte COMBINING = 2; + public static final byte COMPONENT = 3; + public static final byte LIGATURE = 1; + public static final byte STANDARD = 0; + public static final byte WHITESPACE = 4; + + private boolean horizontal; + private float advanceX; + private float advanceY; + private Rectangle2D bounds; + private byte glyphType; + + public GlyphMetrics (boolean horizontal, float advanceX, float advanceY, + Rectangle2D bounds, byte glyphType) + { + this.horizontal = horizontal; + this.advanceX = advanceX; + this.advanceY = advanceY; + this.bounds = bounds; + this.glyphType = glyphType; + } + + public GlyphMetrics (float advance, Rectangle2D bounds, byte glyphType) + { + this (true, advance, advance, bounds, glyphType); + } + + public float getAdvance () + { + return horizontal ? advanceX : advanceY; + } + + public float getAdvanceX () + { + return advanceX; + } + + public float getAdvanceY () + { + return advanceY; + } + + public Rectangle2D getBounds2D () + { + return bounds; + } + + public float getLSB () + { + throw new Error ("not implemented"); + } + + public float getRSB () + { + throw new Error ("not implemented"); + } + + public int getType () + { + return glyphType; + } + + public boolean isCombining () + { + return (glyphType == COMBINING); + } + + public boolean isComponent () + { + return (glyphType == COMPONENT); + } + + public boolean isLigature() + { + return (glyphType == LIGATURE); + } + + public boolean isStandard() + { + return (glyphType == STANDARD); + } + + public boolean isWhitespace() + { + return (glyphType == WHITESPACE); + } +} diff --git a/libjava/classpath/java/awt/font/GlyphVector.java b/libjava/classpath/java/awt/font/GlyphVector.java new file mode 100644 index 00000000000..57e2581edb4 --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphVector.java @@ -0,0 +1,145 @@ +/* GlyphVector.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * @author Michael Koch + */ +public abstract class GlyphVector implements Cloneable +{ + public static final int FLAG_COMPLEX_GLYPHS = 8; + public static final int FLAG_HAS_POSITION_ADJUSTMENTS = 2; + public static final int FLAG_HAS_TRANSFORMS = 1; + public static final int FLAG_MASK = 15; + public static final int FLAG_RUN_RTL = 4; + + /** + * Constructs a <code>GlyphVector</code> object. + */ + public GlyphVector () + { + } + + public abstract boolean equals (GlyphVector set); + + public abstract Font getFont (); + + public abstract FontRenderContext getFontRenderContext (); + + public int getGlyphCharIndex (int glyphIndex) + { + throw new Error ("not implemented"); + } + + public int[] getGlyphCharIndices (int beginGlyphIndex, int numEntries, + int[] codeReturn) + { + throw new Error ("not implemented"); + } + + public abstract int getGlyphCode (int glyphIndex); + + public abstract int[] getGlyphCodes (int beginGlyphIndex, int numEntries, + int[] codeReturn); + + public abstract GlyphJustificationInfo getGlyphJustificationInfo + (int glyphIndex); + + public abstract Shape getGlyphLogicalBounds (int glyphIndex); + + public abstract GlyphMetrics getGlyphMetrics (int glyphIndex); + + public abstract Shape getGlyphOutline (int glyphIndex); + + public Shape getGlyphOutline (int glyphIndex, float x, float y) + { + throw new Error ("not implemented"); + } + + public Rectangle getGlyphPixelBounds (int index, FontRenderContext renderFRC, + float x, float y) + { + throw new Error ("not implemented"); + } + + public abstract Point2D getGlyphPosition (int glyphIndex); + + public abstract float[] getGlyphPositions (int beginGlyphIndex, + int numEntries, + float[] positionReturn); + + public abstract AffineTransform getGlyphTransform (int glyphIndex); + + public abstract Shape getGlyphVisualBounds (int glyphIndex); + + public int getLayoutFlags () + { + throw new Error ("not implemented"); + } + + public abstract Rectangle2D getLogicalBounds (); + + public abstract int getNumGlyphs (); + + public abstract Shape getOutline (); + + public abstract Shape getOutline (float x, float y); + + public Rectangle getPixelBounds (FontRenderContext renderFRC, + float x, float y) + { + throw new Error ("not implemented"); + } + + public abstract Rectangle2D getVisualBounds (); + + public abstract void performDefaultLayout (); + + public abstract void setGlyphPosition (int glyphIndex, Point2D newPos); + + public abstract void setGlyphTransform (int glyphIndex, + AffineTransform newTX); +} diff --git a/libjava/classpath/java/awt/font/GraphicAttribute.java b/libjava/classpath/java/awt/font/GraphicAttribute.java new file mode 100644 index 00000000000..79eae9955f5 --- /dev/null +++ b/libjava/classpath/java/awt/font/GraphicAttribute.java @@ -0,0 +1,84 @@ +/* GraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +/** + * @author Michael Koch + */ +public abstract class GraphicAttribute +{ + public static final int BOTTOM_ALIGNMENT = -2; + public static final int CENTER_BASELINE = 1; + public static final int HANGING_BASELINE = 2; + public static final int ROMAN_BASELINE = 0; + public static final int TOP_ALIGNMENT = -1; + + private int alignment; + + protected GraphicAttribute (int alignment) + { + this.alignment = alignment; + } + + public abstract void draw (Graphics2D graphics, float x, float y); + + public abstract float getAdvance (); + + public final int getAlignment () + { + return alignment; + } + + public abstract float getAscent (); + + public Rectangle2D getBounds () + { + throw new Error ("not implemented"); + } + + public abstract float getDescent (); + + public GlyphJustificationInfo getJustificationInfo () + { + throw new Error ("not implemented"); + } +} diff --git a/libjava/classpath/java/awt/font/ImageGraphicAttribute.java b/libjava/classpath/java/awt/font/ImageGraphicAttribute.java new file mode 100644 index 00000000000..77413f95dfc --- /dev/null +++ b/libjava/classpath/java/awt/font/ImageGraphicAttribute.java @@ -0,0 +1,109 @@ +/* ImageGraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.Rectangle2D; + +/** + * @author Michael Koch + */ +public final class ImageGraphicAttribute extends GraphicAttribute +{ + private Image image; + + public ImageGraphicAttribute (Image image, int alignment) + { + super (alignment); + this.image = image; + } + + public ImageGraphicAttribute (Image image, int alignment, float originX, + float originY) + { + super (alignment); + this.image = image; + + throw new Error ("not implemented"); + } + + public void draw (Graphics2D graphics, float x, float y) + { + throw new Error ("not implemented"); + } + + public boolean equals (Object obj) + { + if (! (obj instanceof ImageGraphicAttribute)) + return false; + + return equals ((ImageGraphicAttribute) obj); + } + + public boolean equals (ImageGraphicAttribute rhs) + { + throw new Error ("not implemented"); + } + + public float getAdvance () + { + throw new Error ("not implemented"); + } + + public float getAscent () + { + throw new Error ("not implemented"); + } + + public Rectangle2D getBounds () + { + throw new Error ("not implemented"); + } + + public float getDescent () + { + throw new Error ("not implemented"); + } + + public int hashCode () + { + throw new Error ("not implemented"); + } +} diff --git a/libjava/classpath/java/awt/font/LineBreakMeasurer.java b/libjava/classpath/java/awt/font/LineBreakMeasurer.java new file mode 100644 index 00000000000..0a6a96922bd --- /dev/null +++ b/libjava/classpath/java/awt/font/LineBreakMeasurer.java @@ -0,0 +1,113 @@ +/* LineBreakMeasurer.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.text.AttributedCharacterIterator; +import java.text.BreakIterator; + +public final class LineBreakMeasurer +{ + private AttributedCharacterIterator ci; + private FontRenderContext frc; + private BreakIterator bi; + + /** + * Constructs a <code>LineBreakMeasurer</code> object. + */ + public LineBreakMeasurer (AttributedCharacterIterator text, + FontRenderContext frc) + { + this (text, null, frc); + } + + /** + * Constructs a <code>LineBreakMeasurer</code> object. + */ + public LineBreakMeasurer (AttributedCharacterIterator text, + BreakIterator breakIter, FontRenderContext frc) + { + this.ci = text; + this.bi = breakIter; + this.frc = frc; + } + + public void deleteChar (AttributedCharacterIterator newParagraph, + int deletePos) + { + throw new Error ("not implemented"); + } + + public int getPosition () + { + return ci.getIndex (); + } + + public void insertChar (AttributedCharacterIterator newParagraph, + int insertPos) + { + throw new Error ("not implemented"); + } + + public TextLayout nextLayout (float wrappingWidth) + { + throw new Error ("not implemented"); + } + + public TextLayout nextLayout (float wrappingWidth, int offsetLimit, + boolean requireNextWord) + { + throw new Error ("not implemented"); + } + + public int nextOffset (float wrappingWidth) + { + throw new Error ("not implemented"); + } + + public int nextOffset (float wrappingWidth, int offsetLimit, + boolean requireNextWord) + { + throw new Error ("not implemented"); + } + + public void setPosition (int newPosition) + { + ci.setIndex (newPosition); + } +} diff --git a/libjava/classpath/java/awt/font/LineMetrics.java b/libjava/classpath/java/awt/font/LineMetrics.java new file mode 100644 index 00000000000..3c45ad19a6e --- /dev/null +++ b/libjava/classpath/java/awt/font/LineMetrics.java @@ -0,0 +1,67 @@ +/* LineMetrics.java -- Information about about a line display characteristics + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public abstract class LineMetrics +{ + public abstract float getAscent(); + + public abstract int getBaselineIndex(); + + public abstract float[] getBaselineOffsets(); + + public abstract float getDescent(); + + public abstract float getHeight(); + + public abstract float getLeading(); + + public abstract int getNumChars(); + + public abstract float getStrikethroughOffset(); + + public abstract float getStrikethroughThickness(); + + public abstract float getUnderlineOffset(); + + public abstract float getUnderlineThickness(); +} diff --git a/libjava/classpath/java/awt/font/MultipleMaster.java b/libjava/classpath/java/awt/font/MultipleMaster.java new file mode 100644 index 00000000000..57417ea6010 --- /dev/null +++ b/libjava/classpath/java/awt/font/MultipleMaster.java @@ -0,0 +1,61 @@ +/* MultipleMaster.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Font; + +/** + * @author Michael Koch + */ +public interface MultipleMaster +{ + Font deriveMMFont (float[] axes); + + Font deriveMMFont (float[] glyphWidths, float avgStemWidth, + float typicalCapHeight, float typicalXHeight, + float italicAngle); + + float[] getDesignAxisDefaults(); + + String[] getDesignAxisNames(); + + float[] getDesignAxisRanges(); + + int getNumDesignAxes(); +} diff --git a/libjava/classpath/java/awt/font/NumericShaper.java b/libjava/classpath/java/awt/font/NumericShaper.java new file mode 100644 index 00000000000..efbdcd49dc4 --- /dev/null +++ b/libjava/classpath/java/awt/font/NumericShaper.java @@ -0,0 +1,137 @@ +/* NumericShaper.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.io.Serializable; + +/** + * @author Michael Koch + * @since 1.4 + */ +public final class NumericShaper implements Serializable +{ + private static final long serialVersionUID = -8022764705923730308L; + + public static final int ALL_RANGES = 524287; + public static final int ARABIC = 2; + public static final int BENGALI = 16; + public static final int DEVANAGARI = 8; + public static final int EASTERN_ARABIC = 4; + public static final int ETHIOPIC = 65536; + public static final int EUROPEAN = 1; + public static final int GUJARATI = 64; + public static final int GURMUKHI = 32; + public static final int KANNADA = 1024; + public static final int KHMER = 131072; + public static final int LAO = 8192; + public static final int MALAYALAM = 2048; + public static final int MONGOLIAN = 262144; + public static final int MYANMAR = 32768; + public static final int ORIYA = 128; + public static final int TAMIL = 256; + public static final int TELUGU = 512; + public static final int THAI = 4096; + public static final int TIBETAN = 16384; + + private int ranges; + private int context; + + private NumericShaper (int ranges, int context) + { + this.ranges = ranges; + this.context = context; + } + + public boolean equals (Object obj) + { + if (! (obj instanceof NumericShaper)) + return false; + + NumericShaper tmp = (NumericShaper) obj; + + return (ranges == tmp.ranges + && context == tmp.context); + } + + public static NumericShaper getContextualShaper (int ranges) + { + throw new Error ("not implemented"); + } + + public static NumericShaper getContextualShaper (int ranges, + int defaultContext) + { + throw new Error ("not implemented"); + } + + public int getRanges () + { + return ranges; + } + + public static NumericShaper getShaper (int singleRange) + { + throw new Error ("not implemented"); + } + + public int hashCode () + { + throw new Error ("not implemented"); + } + + public boolean isContextual () + { + throw new Error ("not implemented"); + } + + public void shape (char[] text, int start, int count) + { + shape (text, start, count, context); + } + + public void shape (char[] text, int start, int count, int context) + { + throw new Error ("not implemented"); + } + + public String toString () + { + throw new Error ("not implemented"); + } +} diff --git a/libjava/classpath/java/awt/font/OpenType.java b/libjava/classpath/java/awt/font/OpenType.java new file mode 100644 index 00000000000..ece3279cc7f --- /dev/null +++ b/libjava/classpath/java/awt/font/OpenType.java @@ -0,0 +1,111 @@ +/* OpenType.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public interface OpenType +{ + int TAG_ACNT = 1633906292; + int TAG_AVAR = 1635148146; + int TAG_BASE = 1111577413; + int TAG_BDAT = 1650745716; + int TAG_BLOC = 1651273571; + int TAG_BSLN = 1651731566; + int TAG_CFF = 1128678944; + int TAG_CMAP = 1668112752; + int TAG_CVAR = 1668702578; + int TAG_CVT = 1668707360; + int TAG_DSIG = 1146308935; + int TAG_EBDT = 1161970772; + int TAG_EBLC = 1161972803; + int TAG_EBSC = 1161974595; + int TAG_FDSC = 1717859171; + int TAG_FEAT = 1717920116; + int TAG_FMTX = 1718449272; + int TAG_FPGM = 1718642541; + int TAG_FVAR = 1719034226; + int TAG_GASP = 1734439792; + int TAG_GDEF = 1195656518; + int TAG_GLYF = 1735162214; + int TAG_GPOS = 1196445523; + int TAG_GSUB = 1196643650; + int TAG_GVAR = 1735811442; + int TAG_HDMX = 1751412088; + int TAG_HEAD = 1751474532; + int TAG_HHEA = 1751672161; + int TAG_HMTX = 1752003704; + int TAG_JSTF = 1246975046; + int TAG_JUST = 1786082164; + int TAG_KERN = 1801810542; + int TAG_LCAR = 1818452338; + int TAG_LOCA = 1819239265; + int TAG_LTSH = 1280594760; + int TAG_MAXP = 1835104368; + int TAG_MMFX = 1296909912; + int TAG_MMSD = 1296913220; + int TAG_MORT = 1836020340; + int TAG_NAME = 1851878757; + int TAG_OPBD = 1836020340; + int TAG_OS2 = 1330851634; + int TAG_PCLT = 1346587732; + int TAG_POST = 1886352244; + int TAG_PREP = 1886545264; + int TAG_PROP = 1886547824; + int TAG_TRAK = 1953653099; + int TAG_TYP1 = 1954115633; + int TAG_VDMX = 1447316824; + int TAG_VHEA = 1986553185; + int TAG_VMTX = 1986884728; + + byte[] getFontTable (int sfntTag); + + byte[] getFontTable (int sfntTag, int offset, int count); + + byte[] getFontTable (String strSfntTag); + + byte[] getFontTable (String strSfntTag, int offset, int count); + + int getFontTableSize (int sfntTag); + + int getFontTableSize (String strSfntTag); + + int getVersion (); +} diff --git a/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java b/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java new file mode 100644 index 00000000000..6d64dece5d8 --- /dev/null +++ b/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java @@ -0,0 +1,105 @@ +/* ShapeGraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; + +public final class ShapeGraphicAttribute extends GraphicAttribute +{ + public static final boolean FILL = false; + public static final boolean STROKE = true; + + private Shape shape; + private boolean stroke; + + public ShapeGraphicAttribute (Shape shape, int alignment, boolean stroke) + { + super (alignment); + this.shape = shape; + this.stroke = stroke; + } + + public void draw (Graphics2D graphics, float x, float y) + { + throw new Error ("not implemented"); + } + + public boolean equals (Object obj) + { + if (! (obj instanceof ShapeGraphicAttribute)) + return false; + + return equals ((ShapeGraphicAttribute) obj); + } + + public boolean equals (ShapeGraphicAttribute rhs) + { + return (shape.equals (rhs.shape) + && getAlignment () == rhs.getAlignment () + && stroke == rhs.stroke); + } + + public float getAdvance () + { + throw new Error ("not implemented"); + } + + public float getAscent () + { + throw new Error ("not implemented"); + } + + public Rectangle2D getBounds () + { + return shape.getBounds2D (); + } + + public float getDescent () + { + throw new Error ("not implemented"); + } + + public int hashCode () + { + // FIXME: Check what SUN does here + return shape.hashCode (); + } +} diff --git a/libjava/classpath/java/awt/font/TextAttribute.java b/libjava/classpath/java/awt/font/TextAttribute.java new file mode 100644 index 00000000000..6f5ed59f915 --- /dev/null +++ b/libjava/classpath/java/awt/font/TextAttribute.java @@ -0,0 +1,309 @@ +/* TextAttribute.java -- + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.io.InvalidObjectException; +import java.text.AttributedCharacterIterator; + +/** + * Attributes (and associated values) that can be used to define an + * {@link java.text.AttributedString}. + */ +public final class TextAttribute extends AttributedCharacterIterator.Attribute +{ + private static final long serialVersionUID = 7744112784117861702L; + + /** A key for the background paint attribute. */ + public static final TextAttribute BACKGROUND = + new TextAttribute("background"); + + /** A key for the BIDI_EMBEDDING attribute. */ + public static final TextAttribute BIDI_EMBEDDING = + new TextAttribute("bidi_embedding"); + + /** A key for the CHAR_REPLACEMENT attribute. */ + public static final TextAttribute CHAR_REPLACEMENT = + new TextAttribute("char_replacement"); + + /** A key for the FAMILY attribute. */ + public static final TextAttribute FAMILY = new TextAttribute("family"); + + /** A key for the font attribute. */ + public static final TextAttribute FONT = new TextAttribute("font"); + + /** A key for the foreground paint attribute. */ + public static final TextAttribute FOREGROUND = + new TextAttribute("foreground"); + + /** A key for the INPUT_METHOD_HIGHLIGHT attribute. */ + public static final TextAttribute INPUT_METHOD_HIGHLIGHT = + new TextAttribute("input method highlight"); + + /** A key for the INPUT_METHOD_UNDERLINE attribute. */ + public static final TextAttribute INPUT_METHOD_UNDERLINE = + new TextAttribute("input method underline"); + + /** A key for the text justification attribute. */ + public static final TextAttribute JUSTIFICATION = + new TextAttribute("justification"); + + /** + * A value that can be used with the {@link #JUSTIFICATION} attribute to + * indicate full justification of the text. + */ + public static final Float JUSTIFICATION_FULL = new Float(1.0); + + /** + * A value that can be used with the {@link #JUSTIFICATION} attribute to + * indicate no justification of the text. + */ + public static final Float JUSTIFICATION_NONE = new Float(0.0); + + /** A key for the NUMERIC_SHAPING attribute. */ + public static final TextAttribute NUMERIC_SHAPING = + new TextAttribute("numeric_shaping"); + + /** A key for the POSTURE attribute. */ + public static final TextAttribute POSTURE = new TextAttribute("posture"); + + /** A value that can be used with the {@link #POSTURE} attribute. */ + public static final Float POSTURE_OBLIQUE = new Float(0.2); + + /** A value that can be used with the {@link #POSTURE} attribute. */ + public static final Float POSTURE_REGULAR = new Float(0.0); + + /** A key for the RUN_DIRECTION attribute. */ + public static final TextAttribute RUN_DIRECTION = + new TextAttribute("run_direction"); + + /** A value that can be used with the {@link #RUN_DIRECTION} attribute. */ + public static final Boolean RUN_DIRECTION_LTR = Boolean.FALSE; + + /** A value that can be used with the {@link #RUN_DIRECTION} attribute. */ + public static final Boolean RUN_DIRECTION_RTL = Boolean.TRUE; + + /** A key for the text size attribute. */ + public static final TextAttribute SIZE = new TextAttribute("size"); + + /** A key for the STRIKETHROUGH attribute. */ + public static final TextAttribute STRIKETHROUGH = + new TextAttribute("strikethrough"); + + /** A value that can be used with the {@link #STRIKETHROUGH} attribute. */ + public static final Boolean STRIKETHROUGH_ON = Boolean.TRUE; + + /** A key for the SUPERSCRIPT attribute. */ + public static final TextAttribute SUPERSCRIPT = + new TextAttribute("superscript"); + + /** A value that can be used with the {@link #SUPERSCRIPT} attribute. */ + public static final Integer SUPERSCRIPT_SUB = new Integer(-1); + + /** A value that can be used with the {@link #SUPERSCRIPT} attribute. */ + public static final Integer SUPERSCRIPT_SUPER = new Integer(1); + + /** A key for the SWAP_COLORS attribute. */ + public static final TextAttribute SWAP_COLORS = + new TextAttribute("swap_colors"); + + /** A value that can be used with the {@link #SWAP_COLORS} attribute. */ + public static final Boolean SWAP_COLORS_ON = Boolean.TRUE; + + /** A key for the TRANFORM attribute. */ + public static final TextAttribute TRANSFORM = new TextAttribute("transform"); + + /** A key for the UNDERLINE attribute. */ + public static final TextAttribute UNDERLINE = new TextAttribute("underline"); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_DASHED = new Integer(5); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_DOTTED = new Integer(3); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_GRAY = new Integer(4); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_ONE_PIXEL = new Integer(1); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_TWO_PIXEL = new Integer(2); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_ON = new Integer(0); + + /** A key for the WEIGHT attribute. */ + public static final TextAttribute WEIGHT = new TextAttribute("weight"); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_BOLD = new Float(2.0); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_DEMIBOLD = new Float(1.75); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_DEMILIGHT = new Float(0.875); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_EXTRA_LIGHT = new Float(0.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_EXTRABOLD = new Float(2.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_HEAVY = new Float(2.25); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_LIGHT = new Float(0.75); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_MEDIUM = new Float(1.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_REGULAR = new Float(1.0); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_SEMIBOLD = new Float(1.25); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_ULTRABOLD = new Float(2.75); + + /** A key for the WIDTH attribute. */ + public static final TextAttribute WIDTH = new TextAttribute("width"); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_CONDENSED = new Float(0.75); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_EXTENDED = new Float(1.5); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_REGULAR = new Float(1.0); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_SEMI_CONDENSED = new Float(0.875); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_SEMI_EXTENDED = new Float(1.25); + + /** + * Creates a new attribute. + * + * @param name the name. + */ + protected TextAttribute(String name) + { + super(name); + } + + /** + * After deserialization, this method ensures that only one instance of + * each attribute is used. + * + * @return The (single) attribute instance. + * + * @throws InvalidObjectException if the attribute is not recognised. + */ + protected Object readResolve() + throws InvalidObjectException + { + if (this.getName().equals("background")) + return BACKGROUND; + + if (this.getName().equals("bidi_embedding")) + return BIDI_EMBEDDING; + + if (this.getName().equals("char_replacement")) + return CHAR_REPLACEMENT; + + if (this.getName().equals("family")) + return FAMILY; + + if (this.getName().equals("font")) + return FONT; + + if (this.getName().equals("foreground")) + return FOREGROUND; + + if (this.getName().equals("input method highlight")) + return INPUT_METHOD_HIGHLIGHT; + + if (this.getName().equals("input method underline")) + return INPUT_METHOD_UNDERLINE; + + if (this.getName().equals("justification")) + return JUSTIFICATION; + + if (this.getName().equals("numeric_shaping")) + return NUMERIC_SHAPING; + + if (this.getName().equals("posture")) + return POSTURE; + + if (this.getName().equals("run_direction")) + return RUN_DIRECTION; + + if (this.getName().equals("size")) + return SIZE; + + if (this.getName().equals("strikethrough")) + return STRIKETHROUGH; + + if (this.getName().equals("superscript")) + return SUPERSCRIPT; + + if (this.getName().equals("swap_colors")) + return SWAP_COLORS; + + if (this.getName().equals("transform")) + return TRANSFORM; + + if (this.getName().equals("underline")) + return UNDERLINE; + + if (this.getName().equals("weight")) + return WEIGHT; + + if (this.getName().equals("width")) + return WIDTH; + + throw new InvalidObjectException("Can't resolve Attribute: " + getName()); + } +} diff --git a/libjava/classpath/java/awt/font/TextHitInfo.java b/libjava/classpath/java/awt/font/TextHitInfo.java new file mode 100644 index 00000000000..2b23e1963cd --- /dev/null +++ b/libjava/classpath/java/awt/font/TextHitInfo.java @@ -0,0 +1,125 @@ +/* TextHitInfo.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.font; + +/** + * @author John Leuner (jewel@debian.org) + */ +public final class TextHitInfo +{ + private int charIndex; + private boolean leadingEdge; + + TextHitInfo (int charIndex, boolean leadingEdge) + { + this.charIndex = charIndex; + this.leadingEdge = leadingEdge; + } + + public int getCharIndex() + { + return charIndex; + } + + public boolean isLeadingEdge() + { + return leadingEdge; + } + + public int getInsertionIndex() + { + return (leadingEdge ? charIndex : charIndex + 1); + } + + public int hashCode() + { + return charIndex; + } + + public boolean equals(Object obj) + { + if(obj instanceof TextHitInfo) + return this.equals((TextHitInfo) obj); + + return false; + } + + public boolean equals(TextHitInfo hitInfo) + { + return (charIndex == hitInfo.getCharIndex ()) + && (leadingEdge == hitInfo.isLeadingEdge ()); + } + + public static TextHitInfo leading(int charIndex) + { + return new TextHitInfo (charIndex, true); + } + + public static TextHitInfo trailing(int charIndex) + { + return new TextHitInfo (charIndex, false); + } + + public static TextHitInfo beforeOffset(int offset) + { + return new TextHitInfo (offset, false); + } + + public static TextHitInfo afterOffset(int offset) + { + return new TextHitInfo (offset, true); + } + + public TextHitInfo getOtherHit() + { + return (leadingEdge ? trailing (charIndex - 1) : leading (charIndex + 1)); + } + + public TextHitInfo getOffsetHit(int offset) + { + return new TextHitInfo (charIndex + offset, leadingEdge); + } + + public String toString() + { + return "TextHitInfo[" + + charIndex + + (leadingEdge ? "L" : "T" ) + + "]"; + } +} diff --git a/libjava/classpath/java/awt/font/TextLayout.java b/libjava/classpath/java/awt/font/TextLayout.java new file mode 100644 index 00000000000..bb641614a92 --- /dev/null +++ b/libjava/classpath/java/awt/font/TextLayout.java @@ -0,0 +1,332 @@ +/* TextLayout.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.peer.ClasspathTextLayoutPeer; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.Map; + +/** + * @author Michael Koch + */ +public final class TextLayout implements Cloneable +{ + public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy (); + ClasspathTextLayoutPeer peer; + + public static class CaretPolicy + { + public CaretPolicy () + { + // Do nothing here. + } + + public TextHitInfo getStrongCaret (TextHitInfo hit1, TextHitInfo hit2, + TextLayout layout) + { + return layout.peer.getStrongCaret(hit1, hit2); + } + } + + public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) + { + AttributedString as = new AttributedString (text); + ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); + peer = tk.getClasspathTextLayoutPeer(as, frc); + } + + public TextLayout (String string, Font font, FontRenderContext frc) + { + AttributedString as = new AttributedString (string); + as.addAttribute (TextAttribute.FONT, font); + ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); + peer = tk.getClasspathTextLayoutPeer(as, frc); + } + + public TextLayout (String string, Map attributes, FontRenderContext frc) + { + AttributedString as = new AttributedString (string, attributes); + ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); + peer = tk.getClasspathTextLayoutPeer(as, frc); + } + + protected Object clone () + { + try + { + TextLayout tl = (TextLayout) super.clone (); + tl.peer = (ClasspathTextLayoutPeer) this.peer.clone(); + return tl; + } + catch (CloneNotSupportedException e) + { + // This should never occur + throw new InternalError (); + } + } + + + public void draw (Graphics2D g2, float x, float y) + { + peer.draw(g2, x, y); + } + + public boolean equals (Object obj) + { + if (! (obj instanceof TextLayout)) + return false; + + return equals ((TextLayout) obj); + } + + public boolean equals (TextLayout tl) + { + return this.peer.equals(tl.peer); + } + + public float getAdvance () + { + return peer.getAdvance(); + } + + public float getAscent () + { + return peer.getAscent(); + } + + public byte getBaseline () + { + return peer.getBaseline(); + } + + public float[] getBaselineOffsets () + { + return peer.getBaselineOffsets(); + } + + public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint) + { + return peer.getBlackBoxBounds(firstEndpoint, secondEndpoint); + } + + public Rectangle2D getBounds() + { + return peer.getBounds(); + } + + public float[] getCaretInfo (TextHitInfo hit) + { + return getCaretInfo(hit, getBounds()); + } + + public float[] getCaretInfo (TextHitInfo hit, Rectangle2D bounds) + { + return peer.getCaretInfo(hit, bounds); + } + + public Shape getCaretShape (TextHitInfo hit) + { + return getCaretShape(hit, getBounds()); + } + + public Shape getCaretShape (TextHitInfo hit, Rectangle2D bounds) + { + return peer.getCaretShape(hit, bounds); + } + + public Shape[] getCaretShapes (int offset) + { + return getCaretShapes(offset, getBounds()); + } + + public Shape[] getCaretShapes (int offset, Rectangle2D bounds) + { + return getCaretShapes(offset, getBounds(), DEFAULT_CARET_POLICY); + } + + public Shape[] getCaretShapes (int offset, Rectangle2D bounds, + TextLayout.CaretPolicy policy) + { + return peer.getCaretShapes(offset, bounds, policy); + } + + public int getCharacterCount () + { + return peer.getCharacterCount(); + } + + public byte getCharacterLevel (int index) + { + return peer.getCharacterLevel(index); + } + + public float getDescent () + { + return peer.getDescent(); + } + + public TextLayout getJustifiedLayout (float justificationWidth) + { + return peer.getJustifiedLayout(justificationWidth); + } + + public float getLeading () + { + return peer.getLeading(); + } + + public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint) + { + return getLogicalHighlightShape (firstEndpoint, secondEndpoint, getBounds()); + } + + public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint, + Rectangle2D bounds) + { + return peer.getLogicalHighlightShape(firstEndpoint, secondEndpoint, bounds); + } + + public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint) + { + return peer.getLogicalRangesForVisualSelection(firstEndpoint, secondEndpoint); + } + + public TextHitInfo getNextLeftHit (int offset) + { + return getNextLeftHit(offset, DEFAULT_CARET_POLICY); + } + + public TextHitInfo getNextLeftHit (int offset, TextLayout.CaretPolicy policy) + { + return peer.getNextLeftHit(offset, policy); + } + + public TextHitInfo getNextLeftHit (TextHitInfo hit) + { + return getNextLeftHit(hit.getCharIndex()); + } + + public TextHitInfo getNextRightHit (int offset) + { + return getNextRightHit(offset, DEFAULT_CARET_POLICY); + } + + public TextHitInfo getNextRightHit (int offset, TextLayout.CaretPolicy policy) + { + return peer.getNextRightHit(offset, policy); + } + + public TextHitInfo getNextRightHit (TextHitInfo hit) + { + return getNextRightHit(hit.getCharIndex()); + } + + public Shape getOutline (AffineTransform tx) + { + return peer.getOutline(tx); + } + + public float getVisibleAdvance () + { + return peer.getVisibleAdvance(); + } + + public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint) + { + return getVisualHighlightShape(firstEndpoint, secondEndpoint, getBounds()); + } + + public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint, + Rectangle2D bounds) + { + return peer.getVisualHighlightShape(firstEndpoint, secondEndpoint, bounds); + } + + public TextHitInfo getVisualOtherHit (TextHitInfo hit) + { + return peer.getVisualOtherHit(hit); + } + + protected void handleJustify (float justificationWidth) + { + peer.handleJustify(justificationWidth); + } + + public int hashCode () + { + return peer.hashCode(); + } + + public TextHitInfo hitTestChar (float x, float y) + { + return hitTestChar(x, y, getBounds()); + } + + public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds) + { + return peer.hitTestChar(x, y, bounds); + } + + public boolean isLeftToRight () + { + return peer.isLeftToRight(); + } + + public boolean isVertical () + { + return peer.isVertical(); + } + + public String toString () + { + return peer.toString(); + } +} diff --git a/libjava/classpath/java/awt/font/TextMeasurer.java b/libjava/classpath/java/awt/font/TextMeasurer.java new file mode 100644 index 00000000000..7fcdc87a83c --- /dev/null +++ b/libjava/classpath/java/awt/font/TextMeasurer.java @@ -0,0 +1,97 @@ +/* TextMeasurer.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.text.AttributedCharacterIterator; + +/** + * @author Michael Koch + * @since 1.3 + */ +public final class TextMeasurer implements Cloneable +{ + private AttributedCharacterIterator ci; + private FontRenderContext frc; + + public TextMeasurer (AttributedCharacterIterator text, FontRenderContext frc) + { + this.ci = text; + this.frc = frc; + } + + protected Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException e) + { + // This may never occur + throw new InternalError (); + } + } + + public void deleteChar (AttributedCharacterIterator newParagraph, + int deletePos) + { + throw new Error ("not implemented"); + } + + public float getAdvanceBetween (int start, int limit) + { + throw new Error ("not implemented"); + } + + public TextLayout getLayout (int start, int limit) + { + throw new Error ("not implemented"); + } + + public int getLineBreakIndex (int start, float maxAdvance) + { + throw new Error ("not implemented"); + } + + public void insertChar (AttributedCharacterIterator newParagraph, + int insertPos) + { + throw new Error ("not implemented"); + } +} diff --git a/libjava/classpath/java/awt/font/TransformAttribute.java b/libjava/classpath/java/awt/font/TransformAttribute.java new file mode 100644 index 00000000000..977cafa032e --- /dev/null +++ b/libjava/classpath/java/awt/font/TransformAttribute.java @@ -0,0 +1,100 @@ +/* TransformAttribute.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.AffineTransform; +import java.io.Serializable; + +/** + * This class provides a mechanism for using an {@link AffineTransform} as + * an <i>immutable</i> attribute (for example, in the + * {@link java.text.AttributedString} class). Any transform passed to + * this class is copied before being stored, and any transform handed out + * by this class is a copy of the stored transform. In this way, it is + * not possible to modify the stored transform. + * + * @author Michael Koch + */ +public final class TransformAttribute implements Serializable +{ + private static final long serialVersionUID = 3356247357827709530L; + + private AffineTransform affineTransform; + + /** + * Creates a new attribute that contains a copy of the given transform. + * + * @param transform the transform (<code>null</code> not permitted). + * + * @throws IllegalArgumentException if <code>transform</code> is + * <code>null</code>. + */ + public TransformAttribute (AffineTransform transform) + { + if (transform == null) + { + throw new IllegalArgumentException("Null 'transform' not permitted."); + } + this.affineTransform = new AffineTransform (transform); + } + + /** + * Returns a copy of the transform contained by this attribute. + * + * @return A copy of the transform. + */ + public AffineTransform getTransform () + { + return (AffineTransform) affineTransform.clone(); + } + + /** + * Returns <code>true</code> if the transform contained by this attribute is + * an identity transform, and <code>false</code> otherwise. + * + * @return <code>true</code> if the transform contained by this attribute is + * an identity transform, and <code>false</code> otherwise. + * + * @since 1.4 + */ + public boolean isIdentity () + { + return (affineTransform == null ? false : affineTransform.isIdentity ()); + } +} diff --git a/libjava/classpath/java/awt/font/package.html b/libjava/classpath/java/awt/font/package.html new file mode 100644 index 00000000000..8c3c61a40bd --- /dev/null +++ b/libjava/classpath/java/awt/font/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.font package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.font</title></head> + +<body> +<p>Representations of different kind of characters and fonts.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/geom/AffineTransform.java b/libjava/classpath/java/awt/geom/AffineTransform.java new file mode 100644 index 00000000000..4d1a4d6d5d4 --- /dev/null +++ b/libjava/classpath/java/awt/geom/AffineTransform.java @@ -0,0 +1,1487 @@ +/* AffineTransform.java -- transform coordinates between two 2-D spaces + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Shape; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * This class represents an affine transformation between two coordinate + * spaces in 2 dimensions. Such a transform preserves the "straightness" + * and "parallelness" of lines. The transform is built from a sequence of + * translations, scales, flips, rotations, and shears. + * + * <p>The transformation can be represented using matrix math on a 3x3 array. + * Given (x,y), the transformation (x',y') can be found by: + * <pre> + * [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ] + * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ] + * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] + * </pre> + * The bottom row of the matrix is constant, so a transform can be uniquely + * represented (as in {@link #toString()}) by + * "[[m00, m01, m02], [m10, m11, m12]]". + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status partially updated to 1.4, still has some problems + */ +public class AffineTransform implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 1330973210523860834L; + + /** + * The transformation is the identity (x' = x, y' = y). All other transforms + * have either a combination of the appropriate transform flag bits for + * their type, or the type GENERAL_TRANSFORM. + * + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_IDENTITY = 0; + + /** + * The transformation includes a translation - shifting in the x or y + * direction without changing length or angles. + * + * @see #TYPE_IDENTITY + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_TRANSLATION = 1; + + /** + * The transformation includes a uniform scale - length is scaled in both + * the x and y directions by the same amount, without affecting angles. + * This is mutually exclusive with TYPE_GENERAL_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ + public static final int TYPE_UNIFORM_SCALE = 2; + + /** + * The transformation includes a general scale - length is scaled in either + * or both the x and y directions, but by different amounts; without + * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ + public static final int TYPE_GENERAL_SCALE = 4; + + /** + * This constant checks if either variety of scale transform is performed. + * + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + */ + public static final int TYPE_MASK_SCALE = 6; + + /** + * The transformation includes a flip about an axis, swapping between + * right-handed and left-handed coordinate systems. In a right-handed + * system, the positive x-axis rotates counter-clockwise to the positive + * y-axis; in a left-handed system it rotates clockwise. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_FLIP = 64; + + /** + * The transformation includes a rotation of a multiple of 90 degrees (PI/2 + * radians). Angles are rotated, but length is preserved. This is mutually + * exclusive with TYPE_GENERAL_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ + public static final int TYPE_QUADRANT_ROTATION = 8; + + /** + * The transformation includes a rotation by an arbitrary angle. Angles are + * rotated, but length is preserved. This is mutually exclusive with + * TYPE_QUADRANT_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_ROTATION = 16; + + /** + * This constant checks if either variety of rotation is performed. + * + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + */ + public static final int TYPE_MASK_ROTATION = 24; + + /** + * The transformation is an arbitrary conversion of coordinates which + * could not be decomposed into the other TYPEs. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_TRANSFORM = 32; + + /** + * The X coordinate scaling element of the transform matrix. + * + * @serial matrix[0,0] + */ + private double m00; + + /** + * The Y coordinate shearing element of the transform matrix. + * + * @serial matrix[1,0] + */ + private double m10; + + /** + * The X coordinate shearing element of the transform matrix. + * + * @serial matrix[0,1] + */ + private double m01; + + /** + * The Y coordinate scaling element of the transform matrix. + * + * @serial matrix[1,1] + */ + private double m11; + + /** + * The X coordinate translation element of the transform matrix. + * + * @serial matrix[0,2] + */ + private double m02; + + /** + * The Y coordinate translation element of the transform matrix. + * + * @serial matrix[1,2] + */ + private double m12; + + /** The type of this transform. */ + private transient int type; + + /** + * Construct a new identity transform: + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + */ + public AffineTransform() + { + m00 = m11 = 1; + } + + /** + * Create a new transform which copies the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public AffineTransform(AffineTransform tx) + { + setTransform(tx); + } + + /** + * Construct a transform with the given matrix entries: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(float m00, float m10, + float m01, float m11, + float m02, float m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Construct a transform from a sequence of float entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + * <pre> + * [ f[0] f[2] (f[4]) ] + * [ f[1] f[3] (f[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param f the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if f is null + * @throws ArrayIndexOutOfBoundsException if f is too small + */ + public AffineTransform(float[] f) + { + m00 = f[0]; + m10 = f[1]; + m01 = f[2]; + m11 = f[3]; + if (f.length >= 6) + { + m02 = f[4]; + m12 = f[5]; + } + updateType(); + } + + /** + * Construct a transform with the given matrix entries: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(double m00, double m10, double m01, + double m11, double m02, double m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Construct a transform from a sequence of double entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + * <pre> + * [ d[0] d[2] (d[4]) ] + * [ d[1] d[3] (d[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param d the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public AffineTransform(double[] d) + { + m00 = d[0]; + m10 = d[1]; + m01 = d[2]; + m11 = d[3]; + if (d.length >= 6) + { + m02 = d[4]; + m12 = d[5]; + } + updateType(); + } + + /** + * Returns a translation transform: + * <pre> + * [ 1 0 tx ] + * [ 0 1 ty ] + * [ 0 0 1 ] + * </pre> + * + * @param tx the x translation distance + * @param ty the y translation distance + * @return the translating transform + */ + public static AffineTransform getTranslateInstance(double tx, double ty) + { + AffineTransform t = new AffineTransform(); + t.setToTranslation(tx, ty); + return t; + } + + /** + * Returns a rotation transform. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + * <pre> + * [ cos(theta) -sin(theta) 0 ] + * [ sin(theta) cos(theta) 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta) + { + AffineTransform t = new AffineTransform(); + t.setToRotation(theta); + return t; + } + + /** + * Returns a rotation transform about a point. A positive angle (in radians) + * rotates the positive x-axis to the positive y-axis. This is the same + * as calling: + * <pre> + * AffineTransform tx = new AffineTransform(); + * tx.setToTranslation(x, y); + * tx.rotate(theta); + * tx.translate(-x, -y); + * </pre> + * + * <p>The resulting matrix is: + * <pre> + * [ cos(theta) -sin(theta) x-x*cos+y*sin ] + * [ sin(theta) cos(theta) y-x*sin-y*cos ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta, + double x, double y) + { + AffineTransform t = new AffineTransform(); + t.setToTranslation(x, y); + t.rotate(theta); + t.translate(-x, -y); + return t; + } + + /** + * Returns a scaling transform: + * <pre> + * [ sx 0 0 ] + * [ 0 sy 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @return the scaling transform + */ + public static AffineTransform getScaleInstance(double sx, double sy) + { + AffineTransform t = new AffineTransform(); + t.setToScale(sx, sy); + return t; + } + + /** + * Returns a shearing transform (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + * <pre> + * [ 1 shx 0 ] + * [ shy 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @return the shearing transform + */ + public static AffineTransform getShearInstance(double shx, double shy) + { + AffineTransform t = new AffineTransform(); + t.setToShear(shx, shy); + return t; + } + + /** + * Returns the type of this transform. The result is always valid, although + * it may not be the simplest interpretation (in other words, there are + * sequences of transforms which reduce to something simpler, which this + * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, + * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive + * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. + * + * @return The type. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + */ + public int getType() + { + return type; + } + + /** + * Return the determinant of this transform matrix. If the determinant is + * non-zero, the transform is invertible; otherwise operations which require + * an inverse throw a NoninvertibleTransformException. A result very near + * zero, due to rounding errors, may indicate that inversion results do not + * carry enough precision to be meaningful. + * + * <p>If this is a uniform scale transformation, the determinant also + * represents the squared value of the scale. Otherwise, it carries little + * additional meaning. The determinant is calculated as: + * <pre> + * | m00 m01 m02 | + * | m10 m11 m12 | = m00 * m11 - m01 * m10 + * | 0 0 1 | + * </pre> + * + * @return the determinant + * @see #createInverse() + */ + public double getDeterminant() + { + return m00 * m11 - m01 * m10; + } + + /** + * Return the matrix of values used in this transform. If the matrix has + * fewer than 6 entries, only the scale and shear factors are returned; + * otherwise the translation factors are copied as well. The resulting + * values are: + * <pre> + * [ d[0] d[2] (d[4]) ] + * [ d[1] d[3] (d[5]) ] + * [ 0 0 1 ] + * </pre> + * + * @param d the matrix to store the results into; with 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public void getMatrix(double[] d) + { + d[0] = m00; + d[1] = m10; + d[2] = m01; + d[3] = m11; + if (d.length >= 6) + { + d[4] = m02; + d[5] = m12; + } + } + + /** + * Returns the X coordinate scaling factor of the matrix. + * + * @return m00 + * @see #getMatrix(double[]) + */ + public double getScaleX() + { + return m00; + } + + /** + * Returns the Y coordinate scaling factor of the matrix. + * + * @return m11 + * @see #getMatrix(double[]) + */ + public double getScaleY() + { + return m11; + } + + /** + * Returns the X coordinate shearing factor of the matrix. + * + * @return m01 + * @see #getMatrix(double[]) + */ + public double getShearX() + { + return m01; + } + + /** + * Returns the Y coordinate shearing factor of the matrix. + * + * @return m10 + * @see #getMatrix(double[]) + */ + public double getShearY() + { + return m10; + } + + /** + * Returns the X coordinate translation factor of the matrix. + * + * @return m02 + * @see #getMatrix(double[]) + */ + public double getTranslateX() + { + return m02; + } + + /** + * Returns the Y coordinate translation factor of the matrix. + * + * @return m12 + * @see #getMatrix(double[]) + */ + public double getTranslateY() + { + return m12; + } + + /** + * Concatenate a translation onto this transform. This is equivalent, but + * more efficient than + * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. + * + * @param tx the x translation distance + * @param ty the y translation distance + * @see #getTranslateInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void translate(double tx, double ty) + { + m02 += tx * m00 + ty * m01; + m12 += tx * m10 + ty * m11; + updateType(); + } + + /** + * Concatenate a rotation onto this transform. This is equivalent, but + * more efficient than + * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. + * + * @param theta the rotation angle + * @see #getRotateInstance(double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + double n00 = m00 * c + m01 * s; + double n01 = m00 * -s + m01 * c; + double n10 = m10 * c + m11 * s; + double n11 = m10 * -s + m11 * c; + m00 = n00; + m01 = n01; + m10 = n10; + m11 = n11; + updateType(); + } + + /** + * Concatenate a rotation about a point onto this transform. This is + * equivalent, but more efficient than + * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @see #getRotateInstance(double, double, double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta, double x, double y) + { + translate(x, y); + rotate(theta); + translate(-x, -y); + } + + /** + * Concatenate a scale onto this transform. This is equivalent, but more + * efficient than + * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. + * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @see #getScaleInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void scale(double sx, double sy) + { + m00 *= sx; + m01 *= sy; + m10 *= sx; + m11 *= sy; + updateType(); + } + + /** + * Concatenate a shearing onto this transform. This is equivalent, but more + * efficient than + * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. + * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @see #getShearInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void shear(double shx, double shy) + { + double n00 = m00 + (shy * m01); + double n01 = m01 + (shx * m00); + double n10 = m10 + (shy * m11); + double n11 = m11 + (shx * m10); + m00 = n00; + m01 = n01; + m10 = n10; + m11 = n11; + updateType(); + } + + /** + * Reset this transform to the identity (no transformation): + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + */ + public void setToIdentity() + { + m00 = m11 = 1; + m01 = m02 = m10 = m12 = 0; + type = TYPE_IDENTITY; + } + + /** + * Set this transform to a translation: + * <pre> + * [ 1 0 tx ] + * [ 0 1 ty ] + * [ 0 0 1 ] + * </pre> + * + * @param tx the x translation distance + * @param ty the y translation distance + */ + public void setToTranslation(double tx, double ty) + { + m00 = m11 = 1; + m01 = m10 = 0; + m02 = tx; + m12 = ty; + type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; + } + + /** + * Set this transform to a rotation. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + * <pre> + * [ cos(theta) -sin(theta) 0 ] + * [ sin(theta) cos(theta) 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + */ + public void setToRotation(double theta) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + m00 = c; + m01 = -s; + m02 = 0; + m10 = s; + m11 = c; + m12 = 0; + type = (c == 1 ? TYPE_IDENTITY + : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION + : TYPE_GENERAL_ROTATION); + } + + /** + * Set this transform to a rotation about a point. A positive angle (in + * radians) rotates the positive x-axis to the positive y-axis. This is the + * same as calling: + * <pre> + * tx.setToTranslation(x, y); + * tx.rotate(theta); + * tx.translate(-x, -y); + * </pre> + * + * <p>The resulting matrix is: + * <pre> + * [ cos(theta) -sin(theta) x-x*cos+y*sin ] + * [ sin(theta) cos(theta) y-x*sin-y*cos ] + * [ 0 0 1 ] + * </pre> + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + */ + public void setToRotation(double theta, double x, double y) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + m00 = c; + m01 = -s; + m02 = x - x * c + y * s; + m10 = s; + m11 = c; + m12 = y - x * s - y * c; + updateType(); + } + + /** + * Set this transform to a scale: + * <pre> + * [ sx 0 0 ] + * [ 0 sy 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param sx the x scaling factor + * @param sy the y scaling factor + */ + public void setToScale(double sx, double sy) + { + m00 = sx; + m01 = m02 = m10 = m12 = 0; + m11 = sy; + type = (sx != sy ? TYPE_GENERAL_SCALE + : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); + } + + /** + * Set this transform to a shear (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + * <pre> + * [ 1 shx 0 ] + * [ shy 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @param shx the x shearing factor + * @param shy the y shearing factor + */ + public void setToShear(double shx, double shy) + { + m00 = m11 = 1; + m01 = shx; + m10 = shy; + m02 = m12 = 0; + updateType(); + } + + /** + * Set this transform to a copy of the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public void setTransform(AffineTransform tx) + { + m00 = tx.m00; + m01 = tx.m01; + m02 = tx.m02; + m10 = tx.m10; + m11 = tx.m11; + m12 = tx.m12; + type = tx.type; + } + + /** + * Set this transform to the given values: + * <pre> + * [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * </pre> + * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public void setTransform(double m00, double m10, double m01, + double m11, double m02, double m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Set this transform to the result of performing the original version of + * this followed by tx. This is commonly used when chaining transformations + * from one space to another. In matrix form: + * <pre> + * [ this ] = [ this ] x [ tx ] + * </pre> + * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #preConcatenate(AffineTransform) + */ + public void concatenate(AffineTransform tx) + { + double n00 = m00 * tx.m00 + m01 * tx.m10; + double n01 = m00 * tx.m01 + m01 * tx.m11; + double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; + double n10 = m10 * tx.m00 + m11 * tx.m10; + double n11 = m10 * tx.m01 + m11 * tx.m11; + double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; + m00 = n00; + m01 = n01; + m02 = n02; + m10 = n10; + m11 = n11; + m12 = n12; + updateType(); + } + + /** + * Set this transform to the result of performing tx followed by the + * original version of this. This is less common than normal concatenation, + * but can still be used to chain transformations from one space to another. + * In matrix form: + * <pre> + * [ this ] = [ tx ] x [ this ] + * </pre> + * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #concatenate(AffineTransform) + */ + public void preConcatenate(AffineTransform tx) + { + double n00 = tx.m00 * m00 + tx.m01 * m10; + double n01 = tx.m00 * m01 + tx.m01 * m11; + double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; + double n10 = tx.m10 * m00 + tx.m11 * m10; + double n11 = tx.m10 * m01 + tx.m11 * m11; + double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; + m00 = n00; + m01 = n01; + m02 = n02; + m10 = n10; + m11 = n11; + m12 = n12; + updateType(); + } + + /** + * Returns a transform, which if concatenated to this one, will result in + * the identity transform. This is useful for undoing transformations, but + * is only possible if the original transform has an inverse (ie. does not + * map multiple points to the same line or point). A transform exists only + * if getDeterminant() has a non-zero value. + * + * The inverse is calculated as: + * + * <pre> + * + * Let A be the matrix for which we want to find the inverse: + * + * A = [ m00 m01 m02 ] + * [ m10 m11 m12 ] + * [ 0 0 1 ] + * + * + * 1 + * inverse (A) = --- x adjoint(A) + * det + * + * + * + * = 1 [ m11 -m01 m01*m12-m02*m11 ] + * --- x [ -m10 m00 -m00*m12+m10*m02 ] + * det [ 0 0 m00*m11-m10*m01 ] + * + * + * + * = [ m11/det -m01/det m01*m12-m02*m11/det ] + * [ -m10/det m00/det -m00*m12+m10*m02/det ] + * [ 0 0 1 ] + * + * + * </pre> + * + * + * + * @return a new inverse transform + * @throws NoninvertibleTransformException if inversion is not possible + * @see #getDeterminant() + */ + public AffineTransform createInverse() + throws NoninvertibleTransformException + { + double det = getDeterminant(); + if (det == 0) + throw new NoninvertibleTransformException("can't invert transform"); + + double im00 = m11 / det; + double im10 = -m10 / det; + double im01 = -m01 / det; + double im11 = m00 / det; + double im02 = (m01 * m12 - m02 * m11) / det; + double im12 = (-m00 * m12 + m10 * m02) / det; + + return new AffineTransform (im00, im10, im01, im11, im02, im12); + } + + /** + * Perform this transformation on the given source point, and store the + * result in the destination (creating it if necessary). It is safe for + * src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + public Point2D transform(Point2D src, Point2D dst) + { + if (dst == null) + dst = new Point2D.Double(); + double x = src.getX(); + double y = src.getY(); + double nx = m00 * x + m01 * y + m02; + double ny = m10 * x + m11 * y + m12; + dst.setLocation(nx, ny); + return dst; + } + + /** + * Perform this transformation on an array of points, storing the results + * in another (possibly same) array. This will not create a destination + * array, but will create points for the null entries of the destination. + * The transformation is done sequentially. While having a single source + * and destination point be the same is safe, you should be aware that + * duplicate references to the same point in the source, and having the + * source overlap the destination, may result in your source points changing + * from a previous transform before it is their turn to be evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points (may have null entries) + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null, or src has null + * entries + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws ArrayStoreException if new points are incompatible with dst + */ + public void transform(Point2D[] src, int srcOff, + Point2D[] dst, int dstOff, int num) + { + while (--num >= 0) + dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + float[] f = new float[2 * num]; + System.arraycopy(srcPts, srcOff, f, 0, 2 * num); + srcPts = f; + } + while (--num >= 0) + { + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m11 * y + m12; + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + { + while (--num >= 0) + { + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m11 * y + m12; + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) + { + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); + } + } + + /** + * Perform the inverse of this transformation on the given source point, + * and store the result in the destination (creating it if necessary). It + * is safe for src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the inverse transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public Point2D inverseTransform(Point2D src, Point2D dst) + throws NoninvertibleTransformException + { + return createInverse().transform(src, dst); + } + + /** + * Perform the inverse of this transformation on an array of points, in + * (x,y) pairs, storing the results in another (possibly same) array. This + * will not create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public void inverseTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + throws NoninvertibleTransformException + { + createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); + } + + /** + * Perform this transformation, less any translation, on the given source + * point, and store the result in the destination (creating it if + * necessary). It is safe for src and dst to be the same. The reduced + * transform is equivalent to: + * <pre> + * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] + * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] + * </pre> + * + * @param src the source point + * @param dst the destination, or null + * @return the delta transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + public Point2D deltaTransform(Point2D src, Point2D dst) + { + if (dst == null) + dst = new Point2D.Double(); + double x = src.getX(); + double y = src.getY(); + double nx = m00 * x + m01 * y; + double ny = m10 * x + m11 * y; + dst.setLocation(nx, ny); + return dst; + } + + /** + * Perform this transformation, less any translation, on an array of points, + * in (x,y) pairs, storing the results in another (possibly same) array. + * This will not create a destination array. All sources are copied before + * the transformation, so that no result will overwrite a point that has + * not yet been evaluated. The reduced transform is equivalent to: + * <pre> + * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] + * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] + * </pre> + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void deltaTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, + int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y; + dstPts[dstOff++] = m10 * x + m11 * y; + } + } + + /** + * Return a new Shape, based on the given one, where the path of the shape + * has been transformed by this transform. Notice that this uses GeneralPath, + * which only stores points in float precision. + * + * @param src the shape source to transform + * @return the shape, transformed by this, <code>null</code> if src is + * <code>null</code>. + * @see GeneralPath#transform(AffineTransform) + */ + public Shape createTransformedShape(Shape src) + { + if(src == null) + return null; + GeneralPath p = new GeneralPath(src); + p.transform(this); + return p; + } + + /** + * Returns a string representation of the transform, in the format: + * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + * + m10 + ", " + m11 + ", " + m12 + "]]"</code>. + * + * @return the string representation + */ + public String toString() + { + return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + + m10 + ", " + m11 + ", " + m12 + "]]"; + } + + /** + * Tests if this transformation is the identity: + * <pre> + * [ 1 0 0 ] + * [ 0 1 0 ] + * [ 0 0 1 ] + * </pre> + * + * @return true if this is the identity transform + */ + public boolean isIdentity() + { + // Rather than rely on type, check explicitly. + return (m00 == 1 && m01 == 0 && m02 == 0 + && m10 == 0 && m11 == 1 && m12 == 0); + } + + /** + * Create a new transform of the same run-time type, with the same + * transforming properties as this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Return the hashcode for this transformation. The formula is not + * documented, but appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getScaleX()); + * l = l * 31 + Double.doubleToLongBits(getShearY()); + * l = l * 31 + Double.doubleToLongBits(getShearX()); + * l = l * 31 + Double.doubleToLongBits(getScaleY()); + * l = l * 31 + Double.doubleToLongBits(getTranslateX()); + * l = l * 31 + Double.doubleToLongBits(getTranslateY()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() + { + long l = Double.doubleToLongBits(m00); + l = l * 31 + Double.doubleToLongBits(m10); + l = l * 31 + Double.doubleToLongBits(m01); + l = l * 31 + Double.doubleToLongBits(m11); + l = l * 31 + Double.doubleToLongBits(m02); + l = l * 31 + Double.doubleToLongBits(m12); + return (int) ((l >> 32) ^ l); + } + + /** + * Compares two transforms for equality. This returns true if they have the + * same matrix values. + * + * @param obj the transform to compare + * @return true if it is equal + */ + public boolean equals(Object obj) + { + if (! (obj instanceof AffineTransform)) + return false; + AffineTransform t = (AffineTransform) obj; + return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 + && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); + } + + /** + * Helper to decode the type from the matrix. This is not guaranteed + * to find the optimal type, but at least it will be valid. + */ + private void updateType() + { + double det = getDeterminant(); + if (det == 0) + { + type = TYPE_GENERAL_TRANSFORM; + return; + } + // Scale (includes rotation by PI) or translation. + if (m01 == 0 && m10 == 0) + { + if (m00 == m11) + type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; + else + type = TYPE_GENERAL_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + // Rotation. + else if (m00 == m11 && m01 == -m10) + { + type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; + if (det != 1) + type |= TYPE_UNIFORM_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + else + type = TYPE_GENERAL_TRANSFORM; + } + + /** + * Reads a transform from an object stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if there is a problem deserializing + * @throws IOException if there is a problem deserializing + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + updateType(); + } +} // class AffineTransform diff --git a/libjava/classpath/java/awt/geom/Arc2D.java b/libjava/classpath/java/awt/geom/Arc2D.java new file mode 100644 index 00000000000..eff34a08144 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Arc2D.java @@ -0,0 +1,1399 @@ +/* Arc2D.java -- represents an arc in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** + * This class represents all arcs (segments of an ellipse in 2-D space). The + * arcs are defined by starting angle and extent (arc length) in degrees, as + * opposed to radians (like the rest of Java), and can be open, chorded, or + * wedge shaped. The angles are skewed according to the ellipse, so that 45 + * degrees always points to the upper right corner (positive x, negative y) + * of the bounding rectangle. A positive extent draws a counterclockwise arc, + * and while the angle can be any value, the path iterator only traverses the + * first 360 degrees. Storage is up to the subclasses. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sven de Marothy (sven@physto.se) + * @since 1.2 + */ +public abstract class Arc2D extends RectangularShape +{ + /** + * An open arc, with no segment connecting the endpoints. This type of + * arc still contains the same points as a chorded version. + */ + public static final int OPEN = 0; + + /** + * A closed arc with a single segment connecting the endpoints (a chord). + */ + public static final int CHORD = 1; + + /** + * A closed arc with two segments, one from each endpoint, meeting at the + * center of the ellipse. + */ + public static final int PIE = 2; + + /** The closure type of this arc. This is package-private to avoid an + * accessor method. */ + int type; + + /** + * Create a new arc, with the specified closure type. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. + * @throws IllegalArgumentException if type is invalid + */ + protected Arc2D(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Get the starting angle of the arc in degrees. + * + * @return the starting angle + * @see #setAngleStart(double) + */ + public abstract double getAngleStart(); + + /** + * Get the extent angle of the arc in degrees. + * + * @return the extent angle + * @see #setAngleExtent(double) + */ + public abstract double getAngleExtent(); + + /** + * Return the closure type of the arc. + * + * @return the closure type + * @see #OPEN + * @see #CHORD + * @see #PIE + * @see #setArcType(int) + */ + public int getArcType() + { + return type; + } + + /** + * Returns the starting point of the arc. + * + * @return the start point + */ + public Point2D getStartPoint() + { + double angle = Math.toRadians(getAngleStart()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Returns the ending point of the arc. + * + * @return the end point + */ + public Point2D getEndPoint() + { + double angle = Math.toRadians(getAngleStart() + getAngleExtent()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param x the new x coordinate of the upper left of the bounding box + * @param y the new y coordinate of the upper left of the bounding box + * @param w the new width of the bounding box + * @param h the new height of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public abstract void setArc(double x, double y, double w, double h, + double start, double extent, int type); + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param p the upper left point of the bounding box + * @param d the dimensions of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if p or d is null + */ + public void setArc(Point2D p, Dimension2D d, double start, double extent, + int type) + { + setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param r the new bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public void setArc(Rectangle2D r, double start, double extent, int type) + { + setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc from the given one. + * + * @param a the arc to copy + * @throws NullPointerException if a is null + */ + public void setArc(Arc2D a) + { + setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(), + a.getAngleExtent(), a.getArcType()); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). This controls the center point and radius, so the arc will be + * circular. + * + * @param x the x coordinate of the center of the circle + * @param y the y coordinate of the center of the circle + * @param r the radius of the circle + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArcByCenter(double x, double y, double r, double start, + double extent, int type) + { + setArc(x - r, y - r, r + r, r + r, start, extent, type); + } + + /** + * Sets the parameters of the arc by finding the tangents of two lines, and + * using the specified radius. The arc will be circular, will begin on the + * tangent point of the line extending from p1 to p2, and will end on the + * tangent point of the line extending from p2 to p3. + * + * XXX What happens if the points are colinear, or the radius negative? + * + * @param p1 the first point + * @param p2 the tangent line intersection point + * @param p3 the third point + * @param r the radius of the arc + * @throws NullPointerException if any point is null + */ + public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r) + { + if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY()) + - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0) + { + Point2D p = p3; + p3 = p1; + p1 = p; + } + + // normalized tangent vectors + double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2); + double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2); + double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2); + double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2); + double theta1 = Math.atan2(dx1, dy1); + double theta2 = Math.atan2(dx2, dy2); + + double dx = r * Math.cos(theta2) - r * Math.cos(theta1); + double dy = -r * Math.sin(theta2) + r * Math.sin(theta1); + + if (theta1 < 0) + theta1 += 2 * Math.PI; + if (theta2 < 0) + theta2 += 2 * Math.PI; + if (theta2 < theta1) + theta2 += 2 * Math.PI; + + // Vectors of the lines, not normalized, note we change + // the direction of line 2. + dx1 = p1.getX() - p2.getX(); + dy1 = p1.getY() - p2.getY(); + dx2 = p3.getX() - p2.getX(); + dy2 = p3.getY() - p2.getY(); + + // Calculate the tangent point to the second line + double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2); + double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX(); + double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY(); + + // calculate the center point + double x = x2 - r * Math.cos(theta2); + double y = y2 + r * Math.sin(theta2); + + setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1), + Math.toDegrees(theta2 - theta1), getArcType()); + } + + /** + * Set the start, in degrees. + * + * @param start the new start angle + * @see #getAngleStart() + */ + public abstract void setAngleStart(double start); + + /** + * Set the extent, in degrees. + * + * @param extent the new extent angle + * @see #getAngleExtent() + */ + public abstract void setAngleExtent(double extent); + + /** + * Sets the starting angle to the angle of the given point relative to + * the center of the arc. The extent remains constant; in other words, + * this rotates the arc. + * + * @param p the new start point + * @throws NullPointerException if p is null + * @see #getStartPoint() + * @see #getAngleStart() + */ + public void setAngleStart(Point2D p) + { + // Normalize. + double x = p.getX() - (getX() + getWidth() / 2); + double y = p.getY() - (getY() + getHeight() / 2); + setAngleStart(Math.toDegrees(Math.atan2(-y, x))); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @see #setAngleStart(Point2D) + */ + public void setAngles(double x1, double y1, double x2, double y2) + { + // Normalize the points. + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + x1 = x1 - (mx + mw / 2); + y1 = y1 - (my + mh / 2); + x2 = x2 - (mx + mw / 2); + y2 = y2 - (my + mh / 2); + double start = Math.toDegrees(Math.atan2(-y1, x1)); + double extent = Math.toDegrees(Math.atan2(-y2, x2)) - start; + if (extent < 0) + extent += 360; + setAngleStart(start); + setAngleExtent(extent); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + * @see #setAngleStart(Point2D) + */ + public void setAngles(Point2D p1, Point2D p2) + { + setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the closure type of this arc. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @see #getArcType() + */ + public void setArcType(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Sets the location and bounds of the ellipse of which this arc is a part. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public void setFrame(double x, double y, double w, double h) + { + setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); + } + + /** + * Gets the bounds of the arc. This is much tighter than + * <code>getBounds</code>, as it takes into consideration the start and + * end angles, and the center point of a pie wedge, rather than just the + * overall ellipse. + * + * @return the bounds of the arc + * @see #getBounds() + */ + public Rectangle2D getBounds2D() + { + double extent = getAngleExtent(); + if (Math.abs(extent) >= 360) + return makeBounds(getX(), getY(), getWidth(), getHeight()); + + // Find the minimal bounding box. This determined by its extrema, + // which are the center, the endpoints of the arc, and any local + // maximum contained by the arc. + double rX = getWidth() / 2; + double rY = getHeight() / 2; + double centerX = getX() + rX; + double centerY = getY() + rY; + + Point2D p1 = getStartPoint(); + Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0); + result.add(getEndPoint()); + + if (type == PIE) + result.add(centerX, centerY); + if (containsAngle(0)) + result.add(centerX + rX, centerY); + if (containsAngle(90)) + result.add(centerX, centerY - rY); + if (containsAngle(180)) + result.add(centerX - rX, centerY); + if (containsAngle(270)) + result.add(centerX, centerY + rY); + + return result; + } + + /** + * Construct a bounding box in a precision appropriate for the subclass. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @return the rectangle for use in getBounds2D + */ + protected abstract Rectangle2D makeBounds(double x, double y, double w, + double h); + + /** + * Tests if the given angle, in degrees, is included in the arc. + * All angles are normalized to be between 0 and 360 degrees. + * + * @param a the angle to test + * @return true if it is contained + */ + public boolean containsAngle(double a) + { + double start = getAngleStart(); + double extent = getAngleExtent(); + double end = start + extent; + + if (extent == 0) + return false; + + if (extent >= 360 || extent <= -360) + return true; + + if (extent < 0) + { + end = start; + start += extent; + } + + start %= 360; + while (start < 0) + start += 360; + + end %= 360; + while (end < start) + end += 360; + + a %= 360; + while (a < start) + a += 360; + + return a >= start && a < end; // starting angle included, ending angle not + } + + /** + * Determines if the arc contains the given point. If the bounding box + * is empty, then this will return false. + * + * The area considered 'inside' an arc of type OPEN is the same as the + * area inside an equivalent filled CHORD-type arc. The area considered + * 'inside' a CHORD-type arc is the same as the filled area. + * + * @param x the x coordinate to test + * @param y the y coordinate to test + * @return true if the point is inside the arc + */ + public boolean contains(double x, double y) + { + double w = getWidth(); + double h = getHeight(); + double extent = getAngleExtent(); + if (w <= 0 || h <= 0 || extent == 0) + return false; + + double mx = getX() + w / 2; + double my = getY() + h / 2; + double dx = (x - mx) * 2 / w; + double dy = (y - my) * 2 / h; + if ((dx * dx + dy * dy) >= 1.0) + return false; + + double angle = Math.toDegrees(Math.atan2(-dy, dx)); + if (getArcType() == PIE) + return containsAngle(angle); + + double a1 = Math.toRadians(getAngleStart()); + double a2 = Math.toRadians(getAngleStart() + extent); + double x1 = mx + getWidth() * Math.cos(a1) / 2; + double y1 = my - getHeight() * Math.sin(a1) / 2; + double x2 = mx + getWidth() * Math.cos(a2) / 2; + double y2 = my - getHeight() * Math.sin(a2) / 2; + double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y + - y1) - (x - x1) * (y2 - y1)); + + if (Math.abs(extent) > 180) + { + if (containsAngle(angle)) + return true; + return sgn > 0; + } + else + { + if (! containsAngle(angle)) + return false; + return sgn < 0; + } + } + + /** + * Tests if a given rectangle intersects the area of the arc. + * + * For a definition of the 'inside' area, see the contains() method. + * @see #contains(double, double) + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the two shapes share common points + */ + public boolean intersects(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return true; + if (rect.intersectsLine(mx, my, x2, y2)) + return true; + } + else// check the chord + if (rect.intersectsLine(x1, y1, x2, y2)) + return true; + + // Check the Arc segment against the four edges + double dx; + + // Check the Arc segment against the four edges + double dy; + dy = y - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dy = (y + h) - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dx = x - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + dx = (x + w) - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + // Check whether the arc is contained within the box + if (rect.contains(mx, my)) + return true; + + return false; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y) + && contains(x + w, y + h))) + return false; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return false; + + if (rect.intersectsLine(mx, my, x2, y2)) + return false; + } + else if (rect.intersectsLine(x1, y1, x2, y2)) + return false; + return true; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param r the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns an iterator over this arc, with an optional transformation. + * This iterator is threadsafe, so future modifications to the arc do not + * affect the iteration. + * + * @param at the transformation, or null + * @return a path iterator + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new ArcIterator(this, at); + } + + /** + * This class is used to iterate over an arc. Since ellipses are a subclass + * of arcs, this is used by Ellipse2D as well. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static final class ArcIterator implements PathIterator + { + /** The current iteration. */ + private int current; + + /** The last iteration. */ + private final int limit; + + /** The optional transformation. */ + private final AffineTransform xform; + + /** The x coordinate of the bounding box. */ + private final double x; + + /** The y coordinate of the bounding box. */ + private final double y; + + /** The width of the bounding box. */ + private final double w; + + /** The height of the bounding box. */ + private final double h; + + /** The start angle, in radians (not degrees). */ + private final double start; + + /** The extent angle, in radians (not degrees). */ + private final double extent; + + /** The arc closure type. */ + private final int type; + + /** + * Construct a new iterator over an arc. + * + * @param a the arc + * @param xform the transform + */ + public ArcIterator(Arc2D a, AffineTransform xform) + { + this.xform = xform; + x = a.getX(); + y = a.getY(); + w = a.getWidth(); + h = a.getHeight(); + double start = a.getAngleStart() * (Math.PI / 180); + double extent = a.getAngleExtent() * (Math.PI / 180); + + if (extent < 0) + { + extent = -extent; + start = 2 * Math.PI - extent + start; + } + this.start = start; + this.extent = extent; + + type = a.type; + if (w < 0 || h < 0) + limit = -1; + else if (extent == 0) + limit = type; + else if (extent <= Math.PI / 2.0) + limit = type + 1; + else if (extent <= Math.PI) + limit = type + 2; + else if (extent <= 3.0 * (Math.PI / 2.0)) + limit = type + 3; + else + limit = type + 4; + } + + /** + * Construct a new iterator over an ellipse. + * + * @param e the ellipse + * @param xform the transform + */ + public ArcIterator(Ellipse2D e, AffineTransform xform) + { + this.xform = xform; + x = e.getX(); + y = e.getY(); + w = e.getWidth(); + h = e.getHeight(); + start = 0; + extent = 2 * Math.PI; + type = CHORD; + limit = (w < 0 || h < 0) ? -1 : 5; + } + + /** + * Return the winding rule. + * + * @return {@link PathIterator#WIND_NON_ZERO} + */ + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + /** + * Test if the iteration is complete. + * + * @return true if more segments exist + */ + public boolean isDone() + { + return current > limit; + } + + /** + * Advance the iterator. + */ + public void next() + { + current++; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(float[] coords) + { + double[] double_coords = new double[6]; + int code = currentSegment(double_coords); + for (int i = 0; i < 6; ++i) + coords[i] = (float) double_coords[i]; + return code; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(double[] coords) + { + double rx = w / 2; + double ry = h / 2; + double xmid = x + rx; + double ymid = y + ry; + + if (current > limit) + throw new NoSuchElementException("arc iterator out of bounds"); + + if (current == 0) + { + coords[0] = xmid + rx * Math.cos(start); + coords[1] = ymid - ry * Math.sin(start); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + + if (type != OPEN && current == limit) + return SEG_CLOSE; + + if ((current == limit - 1) && (type == PIE)) + { + coords[0] = xmid; + coords[1] = ymid; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; + } + + // note that this produces a cubic approximation of the arc segment, + // not a true ellipsoid. there's no ellipsoid path segment code, + // unfortunately. the cubic approximation looks about right, though. + double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0); + double quad = (Math.PI / 2.0); + + double curr_begin = start + (current - 1) * quad; + double curr_extent = Math.min((start + extent) - curr_begin, quad); + double portion_of_a_quadrant = curr_extent / quad; + + double x0 = xmid + rx * Math.cos(curr_begin); + double y0 = ymid - ry * Math.sin(curr_begin); + + double x1 = xmid + rx * Math.cos(curr_begin + curr_extent); + double y1 = ymid - ry * Math.sin(curr_begin + curr_extent); + + AffineTransform trans = new AffineTransform(); + double[] cvec = new double[2]; + double len = kappa * portion_of_a_quadrant; + double angle = curr_begin; + + // in a hypothetical "first quadrant" setting, our first control + // vector would be sticking up, from [1,0] to [1,kappa]. + // + // let us recall however that in java2d, y coords are upside down + // from what one would consider "normal" first quadrant rules, so we + // will *subtract* the y value of this control vector from our first + // point. + cvec[0] = 0; + cvec[1] = len; + trans.scale(rx, ry); + trans.rotate(angle); + trans.transform(cvec, 0, cvec, 0, 1); + coords[0] = x0 + cvec[0]; + coords[1] = y0 - cvec[1]; + + // control vector #2 would, ideally, be sticking out and to the + // right, in a first quadrant arc segment. again, subtraction of y. + cvec[0] = 0; + cvec[1] = -len; + trans.rotate(curr_extent); + trans.transform(cvec, 0, cvec, 0, 1); + coords[2] = x1 + cvec[0]; + coords[3] = y1 - cvec[1]; + + // end point + coords[4] = x1; + coords[5] = y1; + + if (xform != null) + xform.transform(coords, 0, coords, 0, 3); + + return SEG_CUBICTO; + } + } // class ArcIterator + + /** + * This class implements an arc in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Double extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public double x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public double y; + + /** The width of the box bounding the ellipse of this arc. */ + public double width; + + /** The height of the box bounding the ellipse of this arc. */ + public double height; + + /** The start angle of this arc, in degrees. */ + public double start; + + /** The extent angle of this arc, in degrees. */ + public double extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Double() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(double x, double y, double w, double h, double start, + double extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Double(Rectangle2D r, double start, double extent, int type) + { + super(type); + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class implements an arc in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Float extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public float x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public float y; + + /** The width of the box bounding the ellipse of this arc. */ + public float width; + + /** The height of the box bounding the ellipse of this arc. */ + public float height; + + /** The start angle of this arc, in degrees. */ + public float start; + + /** The extent angle of this arc, in degrees. */ + public float extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Float() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(float x, float y, float w, float h, float start, + float extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Float(Rectangle2D r, float start, float extent, int type) + { + super(type); + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + this.start = start; + this.extent = (float) extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + this.start = (float) start; + this.extent = (float) extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = (float) start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = (float) extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h); + } + } // class Float +} // class Arc2D diff --git a/libjava/classpath/java/awt/geom/Area.java b/libjava/classpath/java/awt/geom/Area.java new file mode 100644 index 00000000000..7a0fac43a34 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Area.java @@ -0,0 +1,3312 @@ +/* Area.java -- represents a shape built by constructive area geometry + Copyright (C) 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.Vector; + + +/** + * The Area class represents any area for the purpose of + * Constructive Area Geometry (CAG) manipulations. CAG manipulations + * work as an area-wise form of boolean logic, where the basic operations are: + * <P><li>Add (in boolean algebra: A <B>or</B> B)<BR> + * <li>Subtract (in boolean algebra: A <B>and</B> (<B>not</B> B) )<BR> + * <li>Intersect (in boolean algebra: A <B>and</B> B)<BR> + * <li>Exclusive Or <BR> + * <img src="doc-files/Area-1.png" width="342" height="302" + * alt="Illustration of CAG operations" /><BR> + * Above is an illustration of the CAG operations on two ring shapes.<P> + * + * The contains and intersects() methods are also more accurate than the + * specification of #Shape requires.<P> + * + * Please note that constructing an Area can be slow + * (Self-intersection resolving is proportional to the square of + * the number of segments).<P> + * @see #add(Area) + * @see #subtract(Area) + * @see #intersect(Area) + * @see #exclusiveOr(Area) + * + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + * @status Works, but could be faster and more reliable. + */ +public class Area implements Shape, Cloneable +{ + /** + * General numerical precision + */ + private static final double EPSILON = 1E-11; + + /** + * recursive subdivision epsilon - (see getRecursionDepth) + */ + private static final double RS_EPSILON = 1E-13; + + /** + * Snap distance - points within this distance are considered equal + */ + private static final double PE_EPSILON = 1E-11; + + /** + * Segment vectors containing solid areas and holes + * This is package-private to avoid an accessor method. + */ + Vector solids; + + /** + * Segment vectors containing solid areas and holes + * This is package-private to avoid an accessor method. + */ + Vector holes; + + /** + * Vector (temporary) storing curve-curve intersections + */ + private Vector cc_intersections; + + /** + * Winding rule WIND_NON_ZERO used, after construction, + * this is irrelevant. + */ + private int windingRule; + + /** + * Constructs an empty Area + */ + public Area() + { + solids = new Vector(); + holes = new Vector(); + } + + /** + * Constructs an Area from any given Shape. <P> + * + * If the Shape is self-intersecting, the created Area will consist + * of non-self-intersecting subpaths, and any inner paths which + * are found redundant in accordance with the Shape's winding rule + * will not be included. + * + * @param s the shape (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>s</code> is <code>null</code>. + */ + public Area(Shape s) + { + this(); + + Vector p = makeSegment(s); + + // empty path + if (p == null) + return; + + // delete empty paths + for (int i = 0; i < p.size(); i++) + if (((Segment) p.elementAt(i)).getSignedArea() == 0.0) + p.remove(i--); + + /* + * Resolve self intersecting paths into non-intersecting + * solids and holes. + * Algorithm is as follows: + * 1: Create nodes at all self intersections + * 2: Put all segments into a list + * 3: Grab a segment, follow it, change direction at each node, + * removing segments from the list in the process + * 4: Repeat (3) until no segments remain in the list + * 5: Remove redundant paths and sort into solids and holes + */ + Vector paths = new Vector(); + Segment v; + + for (int i = 0; i < p.size(); i++) + { + Segment path = (Segment) p.elementAt(i); + createNodesSelf(path); + } + + if (p.size() > 1) + { + for (int i = 0; i < p.size() - 1; i++) + for (int j = i + 1; j < p.size(); j++) + { + Segment path1 = (Segment) p.elementAt(i); + Segment path2 = (Segment) p.elementAt(j); + createNodes(path1, path2); + } + } + + // we have intersecting points. + Vector segments = new Vector(); + + for (int i = 0; i < p.size(); i++) + { + Segment path = v = (Segment) p.elementAt(i); + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an add (union) operation on this area with another Area.<BR> + * @param area - the area to be unioned with this one + */ + public void add(Area area) + { + if (equals(area)) + return; + if (area.isEmpty()) + return; + + Area B = (Area) area.clone(); + + Vector pathA = new Vector(); + Vector pathB = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + Segment v; + + // we have intersecting points. + Vector segments = new Vector(); + + // In a union operation, we keep all + // segments of A oustide B and all B outside A + for (int i = 0; i < pathA.size(); i++) + { + v = (Segment) pathA.elementAt(i); + Segment path = v; + do + { + if (v.isSegmentOutside(area)) + segments.add(v); + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + v = (Segment) pathB.elementAt(i); + Segment path = v; + do + { + if (v.isSegmentOutside(this)) + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs a subtraction operation on this Area.<BR> + * @param area the area to be subtracted from this area. + * @throws NullPointerException if <code>area</code> is <code>null</code>. + */ + public void subtract(Area area) + { + if (isEmpty() || area.isEmpty()) + return; + + if (equals(area)) + { + reset(); + return; + } + + Vector pathA = new Vector(); + Area B = (Area) area.clone(); + pathA.addAll(solids); + pathA.addAll(holes); + + // reverse the directions of B paths. + setDirection(B.holes, true); + setDirection(B.solids, false); + + Vector pathB = new Vector(); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + // create nodes + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + + // we have intersecting points. + Vector segments = new Vector(); + + // In a subtraction operation, we keep all + // segments of A oustide B and all B within A + // We outsideness-test only one segment in each path + // and the segments before and after any node + for (int i = 0; i < pathA.size(); i++) + { + Segment v = (Segment) pathA.elementAt(i); + Segment path = v; + if (v.isSegmentOutside(area) && v.node == null) + segments.add(v); + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (v.isSegmentOutside(area)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + Segment v = (Segment) pathB.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(this) && v.node == null) + segments.add(v); + v = v.next; + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(this)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an intersection operation on this Area.<BR> + * @param area - the area to be intersected with this area. + * @throws NullPointerException if <code>area</code> is <code>null</code>. + */ + public void intersect(Area area) + { + if (isEmpty() || area.isEmpty()) + { + reset(); + return; + } + if (equals(area)) + return; + + Vector pathA = new Vector(); + Area B = (Area) area.clone(); + pathA.addAll(solids); + pathA.addAll(holes); + + Vector pathB = new Vector(); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + // create nodes + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + + // we have intersecting points. + Vector segments = new Vector(); + + // In an intersection operation, we keep all + // segments of A within B and all B within A + // (The rest must be redundant) + // We outsideness-test only one segment in each path + // and the segments before and after any node + for (int i = 0; i < pathA.size(); i++) + { + Segment v = (Segment) pathA.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(area) && v.node == null) + segments.add(v); + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(area)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + Segment v = (Segment) pathB.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(this) && v.node == null) + segments.add(v); + v = v.next; + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(this)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an exclusive-or operation on this Area.<BR> + * @param area - the area to be XORed with this area. + * @throws NullPointerException if <code>area</code> is <code>null</code>. + */ + public void exclusiveOr(Area area) + { + if (area.isEmpty()) + return; + + if (isEmpty()) + { + Area B = (Area) area.clone(); + solids = B.solids; + holes = B.holes; + return; + } + if (equals(area)) + { + reset(); + return; + } + + Vector pathA = new Vector(); + + Area B = (Area) area.clone(); + Vector pathB = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + + // reverse the directions of B paths. + setDirection(B.holes, true); + setDirection(B.solids, false); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + Segment v; + + // we have intersecting points. + Vector segments = new Vector(); + + // In an XOR operation, we operate on all segments + for (int i = 0; i < pathA.size(); i++) + { + v = (Segment) pathA.elementAt(i); + Segment path = v; + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + v = (Segment) pathB.elementAt(i); + Segment path = v; + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Clears the Area object, creating an empty area. + */ + public void reset() + { + solids = new Vector(); + holes = new Vector(); + } + + /** + * Returns whether this area encloses any area. + * @return true if the object encloses any area. + */ + public boolean isEmpty() + { + if (solids.size() == 0) + return true; + + double totalArea = 0; + for (int i = 0; i < solids.size(); i++) + totalArea += Math.abs(((Segment) solids.elementAt(i)).getSignedArea()); + for (int i = 0; i < holes.size(); i++) + totalArea -= Math.abs(((Segment) holes.elementAt(i)).getSignedArea()); + if (totalArea <= EPSILON) + return true; + + return false; + } + + /** + * Determines whether the Area consists entirely of line segments + * @return true if the Area lines-only, false otherwise + */ + public boolean isPolygonal() + { + for (int i = 0; i < holes.size(); i++) + if (! ((Segment) holes.elementAt(i)).isPolygonal()) + return false; + for (int i = 0; i < solids.size(); i++) + if (! ((Segment) solids.elementAt(i)).isPolygonal()) + return false; + return true; + } + + /** + * Determines if the Area is rectangular.<P> + * + * This is strictly qualified. An area is considered rectangular if:<BR> + * <li>It consists of a single polygonal path.<BR> + * <li>It is oriented parallel/perpendicular to the xy axis<BR> + * <li>It must be exactly rectangular, i.e. small errors induced by + * transformations may cause a false result, although the area is + * visibly rectangular.<P> + * @return true if the above criteria are met, false otherwise + */ + public boolean isRectangular() + { + if (isEmpty()) + return true; + + if (holes.size() != 0 || solids.size() != 1) + return false; + + Segment path = (Segment) solids.elementAt(0); + if (! path.isPolygonal()) + return false; + + int nCorners = 0; + Segment s = path; + do + { + Segment s2 = s.next; + double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/ + ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); + double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/ + ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); + double dotproduct = d1 + d2; + + // For some reason, only rectangles on the XY axis count. + if (d1 != 0 && d2 != 0) + return false; + + if (Math.abs(dotproduct) == 0) // 90 degree angle + nCorners++; + else if ((Math.abs(1.0 - dotproduct) > 0)) // 0 degree angle? + return false; // if not, return false + + s = s.next; + } + while (s != path); + + return nCorners == 4; + } + + /** + * Returns whether the Area consists of more than one simple + * (non self-intersecting) subpath. + * + * @return true if the Area consists of none or one simple subpath, + * false otherwise. + */ + public boolean isSingular() + { + return (holes.size() == 0 && solids.size() <= 1); + } + + /** + * Returns the bounding box of the Area.<P> Unlike the CubicCurve2D and + * QuadraticCurve2D classes, this method will return the tightest possible + * bounding box, evaluating the extreme points of each curved segment.<P> + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + if (solids.size() == 0) + return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0); + + double xmin; + double xmax; + double ymin; + double ymax; + xmin = xmax = ((Segment) solids.elementAt(0)).P1.getX(); + ymin = ymax = ((Segment) solids.elementAt(0)).P1.getY(); + + for (int path = 0; path < solids.size(); path++) + { + Rectangle2D r = ((Segment) solids.elementAt(path)).getPathBounds(); + xmin = Math.min(r.getMinX(), xmin); + ymin = Math.min(r.getMinY(), ymin); + xmax = Math.max(r.getMaxX(), xmax); + ymax = Math.max(r.getMaxY(), ymax); + } + + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Returns the bounds of this object in Rectangle format. + * Please note that this may lead to loss of precision. + * + * @return The bounds. + * @see #getBounds2D() + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Create a new area of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + Area clone = new Area(); + for (int i = 0; i < solids.size(); i++) + clone.solids.add(((Segment) solids.elementAt(i)).cloneSegmentList()); + for (int i = 0; i < holes.size(); i++) + clone.holes.add(((Segment) holes.elementAt(i)).cloneSegmentList()); + return clone; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Compares two Areas. + * + * @param area the area to compare against this area (<code>null</code> + * permitted). + * @return <code>true</code> if the areas are equal, and <code>false</code> + * otherwise. + */ + public boolean equals(Area area) + { + if (area == null) + return false; + + if (! getBounds2D().equals(area.getBounds2D())) + return false; + + if (solids.size() != area.solids.size() + || holes.size() != area.holes.size()) + return false; + + Vector pathA = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + Vector pathB = new Vector(); + pathB.addAll(area.solids); + pathB.addAll(area.holes); + + int nPaths = pathA.size(); + boolean[][] match = new boolean[2][nPaths]; + + for (int i = 0; i < nPaths; i++) + { + for (int j = 0; j < nPaths; j++) + { + Segment p1 = (Segment) pathA.elementAt(i); + Segment p2 = (Segment) pathB.elementAt(j); + if (! match[0][i] && ! match[1][j]) + if (p1.pathEquals(p2)) + match[0][i] = match[1][j] = true; + } + } + + boolean result = true; + for (int i = 0; i < nPaths; i++) + result = result && match[0][i] && match[1][i]; + return result; + } + + /** + * Transforms this area by the AffineTransform at. + * + * @param at the transform. + */ + public void transform(AffineTransform at) + { + for (int i = 0; i < solids.size(); i++) + ((Segment) solids.elementAt(i)).transformSegmentList(at); + for (int i = 0; i < holes.size(); i++) + ((Segment) holes.elementAt(i)).transformSegmentList(at); + + // Note that the orientation is not invariant under inversion + if ((at.getType() & AffineTransform.TYPE_FLIP) != 0) + { + setDirection(holes, false); + setDirection(solids, true); + } + } + + /** + * Returns a new Area equal to this one, transformed + * by the AffineTransform at. + * @param at the transform. + * @return the transformed area + * @throws NullPointerException if <code>at</code> is <code>null</code>. + */ + public Area createTransformedArea(AffineTransform at) + { + Area a = (Area) clone(); + a.transform(at); + return a; + } + + /** + * Determines if the point (x,y) is contained within this Area. + * + * @param x the x-coordinate of the point. + * @param y the y-coordinate of the point. + * @return true if the point is contained, false otherwise. + */ + public boolean contains(double x, double y) + { + int n = 0; + for (int i = 0; i < solids.size(); i++) + if (((Segment) solids.elementAt(i)).contains(x, y)) + n++; + + for (int i = 0; i < holes.size(); i++) + if (((Segment) holes.elementAt(i)).contains(x, y)) + n--; + + return (n != 0); + } + + /** + * Determines if the Point2D p is contained within this Area. + * + * @param p the point. + * @return <code>true</code> if the point is contained, <code>false</code> + * otherwise. + * @throws NullPointerException if <code>p</code> is <code>null</code>. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines if the rectangle specified by (x,y) as the upper-left + * and with width w and height h is completely contained within this Area, + * returns false otherwise.<P> + * + * This method should always produce the correct results, unlike for other + * classes in geom. + * + * @param x the x-coordinate of the rectangle. + * @param y the y-coordinate of the rectangle. + * @param w the width of the the rectangle. + * @param h the height of the rectangle. + * @return <code>true</code> if the rectangle is considered contained + */ + public boolean contains(double x, double y, double w, double h) + { + LineSegment[] l = new LineSegment[4]; + l[0] = new LineSegment(x, y, x + w, y); + l[1] = new LineSegment(x, y + h, x + w, y + h); + l[2] = new LineSegment(x, y, x, y + h); + l[3] = new LineSegment(x + w, y, x + w, y + h); + + // Since every segment in the area must a contour + // between inside/outside segments, ANY intersection + // will mean the rectangle is not entirely contained. + for (int i = 0; i < 4; i++) + { + for (int path = 0; path < solids.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) solids.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return false; + v = v.next; + } + while (v != start); + } + for (int path = 0; path < holes.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) holes.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return false; + v = v.next; + } + while (v != start); + } + } + + // Is any point inside? + if (! contains(x, y)) + return false; + + // Final hoop: Is the rectangle non-intersecting and inside, + // but encloses a hole? + Rectangle2D r = new Rectangle2D.Double(x, y, w, h); + for (int path = 0; path < holes.size(); path++) + if (! ((Segment) holes.elementAt(path)).isSegmentOutside(r)) + return false; + + return true; + } + + /** + * Determines if the Rectangle2D specified by r is completely contained + * within this Area, returns false otherwise.<P> + * + * This method should always produce the correct results, unlike for other + * classes in geom. + * + * @param r the rectangle. + * @return <code>true</code> if the rectangle is considered contained + * + * @throws NullPointerException if <code>r</code> is <code>null</code>. + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines if the rectangle specified by (x,y) as the upper-left + * and with width w and height h intersects any part of this Area. + * + * @param x the x-coordinate for the rectangle. + * @param y the y-coordinate for the rectangle. + * @param w the width of the rectangle. + * @param h the height of the rectangle. + * @return <code>true</code> if the rectangle intersects the area, + * <code>false</code> otherwise. + */ + public boolean intersects(double x, double y, double w, double h) + { + if (solids.size() == 0) + return false; + + LineSegment[] l = new LineSegment[4]; + l[0] = new LineSegment(x, y, x + w, y); + l[1] = new LineSegment(x, y + h, x + w, y + h); + l[2] = new LineSegment(x, y, x, y + h); + l[3] = new LineSegment(x + w, y, x + w, y + h); + + // Return true on any intersection + for (int i = 0; i < 4; i++) + { + for (int path = 0; path < solids.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) solids.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return true; + v = v.next; + } + while (v != start); + } + for (int path = 0; path < holes.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) holes.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return true; + v = v.next; + } + while (v != start); + } + } + + // Non-intersecting, Is any point inside? + if (contains(x + w * 0.5, y + h * 0.5)) + return true; + + // What if the rectangle encloses the whole shape? + Point2D p = ((Segment) solids.elementAt(0)).getMidPoint(); + if ((new Rectangle2D.Double(x, y, w, h)).contains(p)) + return true; + return false; + } + + /** + * Determines if the Rectangle2D specified by r intersects any + * part of this Area. + * @param r the rectangle to test intersection with (<code>null</code> + * not permitted). + * @return <code>true</code> if the rectangle intersects the area, + * <code>false</code> otherwise. + * @throws NullPointerException if <code>r</code> is <code>null</code>. + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns a PathIterator object defining the contour of this Area, + * transformed by at. + * + * @param at the transform. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at) + { + return (new AreaIterator(at)); + } + + /** + * Returns a flattened PathIterator object defining the contour of this + * Area, transformed by at and with a defined flatness. + * + * @param at the transform. + * @param flatness the flatness. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + //--------------------------------------------------------------------- + // Non-public methods and classes + + /** + * Private pathiterator object. + */ + private class AreaIterator implements PathIterator + { + private Vector segments; + private int index; + private AffineTransform at; + + // Simple compound type for segments + class IteratorSegment + { + int type; + double[] coords; + + IteratorSegment() + { + coords = new double[6]; + } + } + + /** + * The contructor here does most of the work, + * creates a vector of IteratorSegments, which can + * readily be returned + */ + public AreaIterator(AffineTransform at) + { + this.at = at; + index = 0; + segments = new Vector(); + Vector allpaths = new Vector(); + allpaths.addAll(solids); + allpaths.addAll(holes); + + for (int i = 0; i < allpaths.size(); i++) + { + Segment v = (Segment) allpaths.elementAt(i); + Segment start = v; + + IteratorSegment is = new IteratorSegment(); + is.type = SEG_MOVETO; + is.coords[0] = start.P1.getX(); + is.coords[1] = start.P1.getY(); + segments.add(is); + + do + { + is = new IteratorSegment(); + is.type = v.pathIteratorFormat(is.coords); + segments.add(is); + v = v.next; + } + while (v != start); + + is = new IteratorSegment(); + is.type = SEG_CLOSE; + segments.add(is); + } + } + + public int currentSegment(double[] coords) + { + IteratorSegment s = (IteratorSegment) segments.elementAt(index); + if (at != null) + at.transform(s.coords, 0, coords, 0, 3); + else + for (int i = 0; i < 6; i++) + coords[i] = s.coords[i]; + return (s.type); + } + + public int currentSegment(float[] coords) + { + IteratorSegment s = (IteratorSegment) segments.elementAt(index); + double[] d = new double[6]; + if (at != null) + { + at.transform(s.coords, 0, d, 0, 3); + for (int i = 0; i < 6; i++) + coords[i] = (float) d[i]; + } + else + for (int i = 0; i < 6; i++) + coords[i] = (float) s.coords[i]; + return (s.type); + } + + // Note that the winding rule should not matter here, + // EVEN_ODD is chosen because it renders faster. + public int getWindingRule() + { + return (PathIterator.WIND_EVEN_ODD); + } + + public boolean isDone() + { + return (index >= segments.size()); + } + + public void next() + { + index++; + } + } + + /** + * Performs the fundamental task of the Weiler-Atherton algorithm, + * traverse a list of segments, for each segment: + * Follow it, removing segments from the list and switching paths + * at each node. Do so until the starting segment is reached. + * + * Returns a Vector of the resulting paths. + */ + private Vector weilerAtherton(Vector segments) + { + Vector paths = new Vector(); + while (segments.size() > 0) + { + // Iterate over the path + Segment start = (Segment) segments.elementAt(0); + Segment s = start; + do + { + segments.remove(s); + if (s.node != null) + { // switch over + s.next = s.node; + s.node = null; + } + s = s.next; // continue + } + while (s != start); + + paths.add(start); + } + return paths; + } + + /** + * A small wrapper class to store intersection points + */ + private class Intersection + { + Point2D p; // the 2D point of intersection + double ta; // the parametric value on a + double tb; // the parametric value on b + Segment seg; // segment placeholder for node setting + + public Intersection(Point2D p, double ta, double tb) + { + this.p = p; + this.ta = ta; + this.tb = tb; + } + } + + /** + * Returns the recursion depth necessary to approximate the + * curve by line segments within the error RS_EPSILON. + * + * This is done with Wang's formula: + * L0 = max{0<=i<=N-2}(|xi - 2xi+1 + xi+2|,|yi - 2yi+1 + yi+2|) + * r0 = log4(sqrt(2)*N*(N-1)*L0/8e) + * Where e is the maximum distance error (RS_EPSILON) + */ + private int getRecursionDepth(CubicSegment curve) + { + double x0 = curve.P1.getX(); + double y0 = curve.P1.getY(); + + double x1 = curve.cp1.getX(); + double y1 = curve.cp1.getY(); + + double x2 = curve.cp2.getX(); + double y2 = curve.cp2.getY(); + + double x3 = curve.P2.getX(); + double y3 = curve.P2.getY(); + + double L0 = Math.max(Math.max(Math.abs(x0 - 2 * x1 + x2), + Math.abs(x1 - 2 * x2 + x3)), + Math.max(Math.abs(y0 - 2 * y1 + y2), + Math.abs(y1 - 2 * y2 + y3))); + + double f = Math.sqrt(2) * 6.0 * L0 / (8.0 * RS_EPSILON); + + int r0 = (int) Math.ceil(Math.log(f) / Math.log(4.0)); + return (r0); + } + + /** + * Performs recursive subdivision: + * @param c1 - curve 1 + * @param c2 - curve 2 + * @param depth1 - recursion depth of curve 1 + * @param depth2 - recursion depth of curve 2 + * @param t1 - global parametric value of the first curve's starting point + * @param t2 - global parametric value of the second curve's starting point + * @param w1 - global parametric length of curve 1 + * @param c1 - global parametric length of curve 2 + * + * The final four parameters are for keeping track of the parametric + * value of the curve. For a full curve t = 0, w = 1, w is halved with + * each subdivision. + */ + private void recursiveSubdivide(CubicCurve2D c1, CubicCurve2D c2, + int depth1, int depth2, double t1, + double t2, double w1, double w2) + { + boolean flat1 = depth1 <= 0; + boolean flat2 = depth2 <= 0; + + if (flat1 && flat2) + { + double xlk = c1.getP2().getX() - c1.getP1().getX(); + double ylk = c1.getP2().getY() - c1.getP1().getY(); + + double xnm = c2.getP2().getX() - c2.getP1().getX(); + double ynm = c2.getP2().getY() - c2.getP1().getY(); + + double xmk = c2.getP1().getX() - c1.getP1().getX(); + double ymk = c2.getP1().getY() - c1.getP1().getY(); + double det = xnm * ylk - ynm * xlk; + + if (det + 1.0 == 1.0) + return; + + double detinv = 1.0 / det; + double s = (xnm * ymk - ynm * xmk) * detinv; + double t = (xlk * ymk - ylk * xmk) * detinv; + if ((s < 0.0) || (s > 1.0) || (t < 0.0) || (t > 1.0)) + return; + + double[] temp = new double[2]; + temp[0] = t1 + s * w1; + temp[1] = t2 + t * w1; + cc_intersections.add(temp); + return; + } + + CubicCurve2D.Double c11 = new CubicCurve2D.Double(); + CubicCurve2D.Double c12 = new CubicCurve2D.Double(); + CubicCurve2D.Double c21 = new CubicCurve2D.Double(); + CubicCurve2D.Double c22 = new CubicCurve2D.Double(); + + if (! flat1 && ! flat2) + { + depth1--; + depth2--; + w1 = w1 * 0.5; + w2 = w2 * 0.5; + c1.subdivide(c11, c12); + c2.subdivide(c21, c22); + if (c11.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c11, c21, depth1, depth2, t1, t2, w1, w2); + if (c11.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c11, c22, depth1, depth2, t1, t2 + w2, w1, w2); + if (c12.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c12, c21, depth1, depth2, t1 + w1, t2, w1, w2); + if (c12.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c12, c22, depth1, depth2, t1 + w1, t2 + w2, w1, w2); + return; + } + + if (! flat1) + { + depth1--; + c1.subdivide(c11, c12); + w1 = w1 * 0.5; + if (c11.getBounds2D().intersects(c2.getBounds2D())) + recursiveSubdivide(c11, c2, depth1, depth2, t1, t2, w1, w2); + if (c12.getBounds2D().intersects(c2.getBounds2D())) + recursiveSubdivide(c12, c2, depth1, depth2, t1 + w1, t2, w1, w2); + return; + } + + depth2--; + c2.subdivide(c21, c22); + w2 = w2 * 0.5; + if (c1.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c1, c21, depth1, depth2, t1, t2, w1, w2); + if (c1.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c1, c22, depth1, depth2, t1, t2 + w2, w1, w2); + } + + /** + * Returns a set of interesections between two Cubic segments + * Or null if no intersections were found. + * + * The method used to find the intersection is recursive midpoint + * subdivision. Outline description: + * + * 1) Check if the bounding boxes of the curves intersect, + * 2) If so, divide the curves in the middle and test the bounding + * boxes again, + * 3) Repeat until a maximum recursion depth has been reached, where + * the intersecting curves can be approximated by line segments. + * + * This is a reasonably accurate method, although the recursion depth + * is typically around 20, the bounding-box tests allow for significant + * pruning of the subdivision tree. + * + * This is package-private to avoid an accessor method. + */ + Intersection[] cubicCubicIntersect(CubicSegment curve1, CubicSegment curve2) + { + Rectangle2D r1 = curve1.getBounds(); + Rectangle2D r2 = curve2.getBounds(); + + if (! r1.intersects(r2)) + return null; + + cc_intersections = new Vector(); + recursiveSubdivide(curve1.getCubicCurve2D(), curve2.getCubicCurve2D(), + getRecursionDepth(curve1), getRecursionDepth(curve2), + 0.0, 0.0, 1.0, 1.0); + + if (cc_intersections.size() == 0) + return null; + + Intersection[] results = new Intersection[cc_intersections.size()]; + for (int i = 0; i < cc_intersections.size(); i++) + { + double[] temp = (double[]) cc_intersections.elementAt(i); + results[i] = new Intersection(curve1.evaluatePoint(temp[0]), temp[0], + temp[1]); + } + cc_intersections = null; + return (results); + } + + /** + * Returns the intersections between a line and a quadratic bezier + * Or null if no intersections are found1 + * This is done through combining the line's equation with the + * parametric form of the Bezier and solving the resulting quadratic. + * This is package-private to avoid an accessor method. + */ + Intersection[] lineQuadIntersect(LineSegment l, QuadSegment c) + { + double[] y = new double[3]; + double[] x = new double[3]; + double[] r = new double[3]; + int nRoots; + double x0 = c.P1.getX(); + double y0 = c.P1.getY(); + double x1 = c.cp.getX(); + double y1 = c.cp.getY(); + double x2 = c.P2.getX(); + double y2 = c.P2.getY(); + + double lx0 = l.P1.getX(); + double ly0 = l.P1.getY(); + double lx1 = l.P2.getX(); + double ly1 = l.P2.getY(); + double dx = lx1 - lx0; + double dy = ly1 - ly0; + + // form r(t) = y(t) - x(t) for the bezier + y[0] = y0; + y[1] = 2 * (y1 - y0); + y[2] = (y2 - 2 * y1 + y0); + + x[0] = x0; + x[1] = 2 * (x1 - x0); + x[2] = (x2 - 2 * x1 + x0); + + // a point, not a line + if (dy == 0 && dx == 0) + return null; + + // line on y axis + if (dx == 0 || (dy / dx) > 1.0) + { + double k = dx / dy; + x[0] -= lx0; + y[0] -= ly0; + y[0] *= k; + y[1] *= k; + y[2] *= k; + } + else + { + double k = dy / dx; + x[0] -= lx0; + y[0] -= ly0; + x[0] *= k; + x[1] *= k; + x[2] *= k; + } + + for (int i = 0; i < 3; i++) + r[i] = y[i] - x[i]; + + if ((nRoots = QuadCurve2D.solveQuadratic(r)) > 0) + { + Intersection[] temp = new Intersection[nRoots]; + int intersections = 0; + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + Point2D p = c.evaluatePoint(t); + + // if the line is on an axis, snap the point to that axis. + if (dx == 0) + p.setLocation(lx0, p.getY()); + if (dy == 0) + p.setLocation(p.getX(), ly0); + + if (p.getX() <= Math.max(lx0, lx1) + && p.getX() >= Math.min(lx0, lx1) + && p.getY() <= Math.max(ly0, ly1) + && p.getY() >= Math.min(ly0, ly1)) + { + double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1); + temp[i] = new Intersection(p, lineparameter, t); + intersections++; + } + } + else + temp[i] = null; + } + if (intersections == 0) + return null; + + Intersection[] rValues = new Intersection[intersections]; + + for (int i = 0; i < nRoots; i++) + if (temp[i] != null) + rValues[--intersections] = temp[i]; + return (rValues); + } + return null; + } + + /** + * Returns the intersections between a line and a cubic segment + * This is done through combining the line's equation with the + * parametric form of the Bezier and solving the resulting quadratic. + * This is package-private to avoid an accessor method. + */ + Intersection[] lineCubicIntersect(LineSegment l, CubicSegment c) + { + double[] y = new double[4]; + double[] x = new double[4]; + double[] r = new double[4]; + int nRoots; + double x0 = c.P1.getX(); + double y0 = c.P1.getY(); + double x1 = c.cp1.getX(); + double y1 = c.cp1.getY(); + double x2 = c.cp2.getX(); + double y2 = c.cp2.getY(); + double x3 = c.P2.getX(); + double y3 = c.P2.getY(); + + double lx0 = l.P1.getX(); + double ly0 = l.P1.getY(); + double lx1 = l.P2.getX(); + double ly1 = l.P2.getY(); + double dx = lx1 - lx0; + double dy = ly1 - ly0; + + // form r(t) = y(t) - x(t) for the bezier + y[0] = y0; + y[1] = 3 * (y1 - y0); + y[2] = 3 * (y2 + y0 - 2 * y1); + y[3] = y3 - 3 * y2 + 3 * y1 - y0; + + x[0] = x0; + x[1] = 3 * (x1 - x0); + x[2] = 3 * (x2 + x0 - 2 * x1); + x[3] = x3 - 3 * x2 + 3 * x1 - x0; + + // a point, not a line + if (dy == 0 && dx == 0) + return null; + + // line on y axis + if (dx == 0 || (dy / dx) > 1.0) + { + double k = dx / dy; + x[0] -= lx0; + y[0] -= ly0; + y[0] *= k; + y[1] *= k; + y[2] *= k; + y[3] *= k; + } + else + { + double k = dy / dx; + x[0] -= lx0; + y[0] -= ly0; + x[0] *= k; + x[1] *= k; + x[2] *= k; + x[3] *= k; + } + for (int i = 0; i < 4; i++) + r[i] = y[i] - x[i]; + + if ((nRoots = CubicCurve2D.solveCubic(r)) > 0) + { + Intersection[] temp = new Intersection[nRoots]; + int intersections = 0; + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + // if the line is on an axis, snap the point to that axis. + Point2D p = c.evaluatePoint(t); + if (dx == 0) + p.setLocation(lx0, p.getY()); + if (dy == 0) + p.setLocation(p.getX(), ly0); + + if (p.getX() <= Math.max(lx0, lx1) + && p.getX() >= Math.min(lx0, lx1) + && p.getY() <= Math.max(ly0, ly1) + && p.getY() >= Math.min(ly0, ly1)) + { + double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1); + temp[i] = new Intersection(p, lineparameter, t); + intersections++; + } + } + else + temp[i] = null; + } + + if (intersections == 0) + return null; + + Intersection[] rValues = new Intersection[intersections]; + for (int i = 0; i < nRoots; i++) + if (temp[i] != null) + rValues[--intersections] = temp[i]; + return (rValues); + } + return null; + } + + /** + * Returns the intersection between two lines, or null if there is no + * intersection. + * This is package-private to avoid an accessor method. + */ + Intersection linesIntersect(LineSegment a, LineSegment b) + { + Point2D P1 = a.P1; + Point2D P2 = a.P2; + Point2D P3 = b.P1; + Point2D P4 = b.P2; + + if (! Line2D.linesIntersect(P1.getX(), P1.getY(), P2.getX(), P2.getY(), + P3.getX(), P3.getY(), P4.getX(), P4.getY())) + return null; + + double x1 = P1.getX(); + double y1 = P1.getY(); + double rx = P2.getX() - x1; + double ry = P2.getY() - y1; + + double x2 = P3.getX(); + double y2 = P3.getY(); + double sx = P4.getX() - x2; + double sy = P4.getY() - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // Parallel lines don't intersect. At least we pretend they don't. + if (Math.abs(determinant) < EPSILON) + return null; + + nom = nom / determinant; + + if (nom == 0.0) + return null; + if (nom == 1.0) + return null; + + Point2D p = new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + + return new Intersection(p, p.distance(P1) / P1.distance(P2), + p.distance(P3) / P3.distance(P4)); + } + + /** + * Determines if two points are equal, within an error margin + * 'snap distance' + * This is package-private to avoid an accessor method. + */ + boolean pointEquals(Point2D a, Point2D b) + { + return (a.equals(b) || a.distance(b) < PE_EPSILON); + } + + /** + * Helper method + * Turns a shape into a Vector of Segments + */ + private Vector makeSegment(Shape s) + { + Vector paths = new Vector(); + PathIterator pi = s.getPathIterator(null); + double[] coords = new double[6]; + Segment subpath = null; + Segment current = null; + double cx; + double cy; + double subpathx; + double subpathy; + cx = cy = subpathx = subpathy = 0.0; + + this.windingRule = pi.getWindingRule(); + + while (! pi.isDone()) + { + Segment v; + switch (pi.currentSegment(coords)) + { + case PathIterator.SEG_MOVETO: + if (subpath != null) + { // close existing open path + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + } + subpath = null; + subpathx = cx = coords[0]; + subpathy = cy = coords[1]; + break; + + // replace 'close' with a line-to. + case PathIterator.SEG_CLOSE: + if (subpath != null && (subpathx != cx || subpathy != cy)) + { + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + cx = subpathx; + cy = subpathy; + subpath = null; + } + else if (subpath != null) + { + current.next = subpath; + subpath = null; + } + break; + case PathIterator.SEG_LINETO: + if (cx != coords[0] || cy != coords[1]) + { + v = new LineSegment(cx, cy, coords[0], coords[1]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + cx = coords[0]; + cy = coords[1]; + } + break; + case PathIterator.SEG_QUADTO: + v = new QuadSegment(cx, cy, coords[0], coords[1], coords[2], + coords[3]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + cx = coords[2]; + cy = coords[3]; + break; + case PathIterator.SEG_CUBICTO: + v = new CubicSegment(cx, cy, coords[0], coords[1], coords[2], + coords[3], coords[4], coords[5]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + + // check if the cubic is self-intersecting + double[] lpts = ((CubicSegment) v).getLoop(); + if (lpts != null) + { + // if it is, break off the loop into its own path. + v.subdivideInsert(lpts[0]); + v.next.subdivideInsert((lpts[1] - lpts[0]) / (1.0 - lpts[0])); + + CubicSegment loop = (CubicSegment) v.next; + v.next = loop.next; + loop.next = loop; + + v.P2 = v.next.P1 = loop.P2 = loop.P1; // snap points + paths.add(loop); + current = v.next; + } + + cx = coords[4]; + cy = coords[5]; + break; + } + pi.next(); + } + + if (subpath != null) + { // close any open path + if (subpathx != cx || subpathy != cy) + { + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + } + else + current.next = subpath; + } + + if (paths.size() == 0) + return (null); + + return (paths); + } + + /** + * Find the intersections of two separate closed paths, + * A and B, split the segments at the intersection points, + * and create nodes pointing from one to the other + */ + private int createNodes(Segment A, Segment B) + { + int nNodes = 0; + + Segment a = A; + Segment b = B; + + do + { + do + { + nNodes += a.splitIntersections(b); + b = b.next; + } + while (b != B); + + a = a.next; // move to the next segment + } + while (a != A); // until one wrap. + + return (nNodes); + } + + /** + * Find the intersections of a path with itself. + * Splits the segments at the intersection points, + * and create nodes pointing from one to the other. + */ + private int createNodesSelf(Segment A) + { + int nNodes = 0; + Segment a = A; + + if (A.next == A) + return 0; + + do + { + Segment b = a.next; + do + { + if (b != a) // necessary + nNodes += a.splitIntersections(b); + b = b.next; + } + while (b != A); + a = a.next; // move to the next segment + } + while (a != A); // until one wrap. + + return (nNodes); + } + + /** + * Deletes paths which are redundant from a list, (i.e. solid areas within + * solid areas) Clears any nodes. Sorts the remaining paths into solids + * and holes, sets their orientation and sets the solids and holes lists. + */ + private void deleteRedundantPaths(Vector paths) + { + int npaths = paths.size(); + + int[][] contains = new int[npaths][npaths]; + int[][] windingNumbers = new int[npaths][2]; + int neg; + Rectangle2D[] bb = new Rectangle2D[npaths]; // path bounding boxes + + neg = ((windingRule == PathIterator.WIND_NON_ZERO) ? -1 : 1); + + for (int i = 0; i < npaths; i++) + bb[i] = ((Segment) paths.elementAt(i)).getPathBounds(); + + // Find which path contains which, assign winding numbers + for (int i = 0; i < npaths; i++) + { + Segment pathA = (Segment) paths.elementAt(i); + pathA.nullNodes(); // remove any now-redundant nodes, in case. + int windingA = pathA.hasClockwiseOrientation() ? 1 : neg; + + for (int j = 0; j < npaths; j++) + if (i != j) + { + Segment pathB = (Segment) paths.elementAt(j); + + // A contains B + if (bb[i].intersects(bb[j])) + { + Segment s = pathB.next; + while (s.P1.getY() == s.P2.getY() && s != pathB) + s = s.next; + Point2D p = s.getMidPoint(); + if (pathA.contains(p.getX(), p.getY())) + contains[i][j] = windingA; + } + else + // A does not contain B + contains[i][j] = 0; + } + else + contains[i][j] = windingA; // i == j + } + + for (int i = 0; i < npaths; i++) + { + windingNumbers[i][0] = 0; + for (int j = 0; j < npaths; j++) + windingNumbers[i][0] += contains[j][i]; + windingNumbers[i][1] = contains[i][i]; + } + + Vector solids = new Vector(); + Vector holes = new Vector(); + + if (windingRule == PathIterator.WIND_NON_ZERO) + { + for (int i = 0; i < npaths; i++) + { + if (windingNumbers[i][0] == 0) + holes.add(paths.elementAt(i)); + else if (windingNumbers[i][0] - windingNumbers[i][1] == 0 + && Math.abs(windingNumbers[i][0]) == 1) + solids.add(paths.elementAt(i)); + } + } + else + { + windingRule = PathIterator.WIND_NON_ZERO; + for (int i = 0; i < npaths; i++) + { + if ((windingNumbers[i][0] & 1) == 0) + holes.add(paths.elementAt(i)); + else if ((windingNumbers[i][0] & 1) == 1) + solids.add(paths.elementAt(i)); + } + } + + setDirection(holes, false); + setDirection(solids, true); + this.holes = holes; + this.solids = solids; + } + + /** + * Sets the winding direction of a Vector of paths + * @param clockwise gives the direction, + * true = clockwise, false = counter-clockwise + */ + private void setDirection(Vector paths, boolean clockwise) + { + Segment v; + for (int i = 0; i < paths.size(); i++) + { + v = (Segment) paths.elementAt(i); + if (clockwise != v.hasClockwiseOrientation()) + v.reverseAll(); + } + } + + /** + * Class representing a linked-list of vertices forming a closed polygon, + * convex or concave, without holes. + */ + private abstract class Segment implements Cloneable + { + // segment type, PathIterator segment types are used. + Point2D P1; + Point2D P2; + Segment next; + Segment node; + + Segment() + { + P1 = P2 = null; + node = next = null; + } + + /** + * Reverses the direction of a single segment + */ + abstract void reverseCoords(); + + /** + * Returns the segment's midpoint + */ + abstract Point2D getMidPoint(); + + /** + * Returns the bounding box of this segment + */ + abstract Rectangle2D getBounds(); + + /** + * Transforms a single segment + */ + abstract void transform(AffineTransform at); + + /** + * Returns the PathIterator type of a segment + */ + abstract int getType(); + + /** + */ + abstract int splitIntersections(Segment b); + + /** + * Returns the PathIterator coords of a segment + */ + abstract int pathIteratorFormat(double[] coords); + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + * + * (Although that could be done by the line-intersect methods, + * a dedicated method is better to guarantee consitent handling + * of endpoint-special-cases) + */ + abstract int rayCrossing(double x, double y); + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + abstract void subdivideInsert(double t); + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * Used for area calculations. + */ + abstract double curveArea(); + + /** + * Compare two segments. + */ + abstract boolean equals(Segment b); + + /** + * Determines if this path of segments contains the point (x,y) + */ + boolean contains(double x, double y) + { + Segment v = this; + int crossings = 0; + do + { + int n = v.rayCrossing(x, y); + crossings += n; + v = v.next; + } + while (v != this); + return ((crossings & 1) == 1); + } + + /** + * Nulls all nodes of the path. Clean up any 'hairs'. + */ + void nullNodes() + { + Segment v = this; + do + { + v.node = null; + v = v.next; + } + while (v != this); + } + + /** + * Transforms each segment in the closed path + */ + void transformSegmentList(AffineTransform at) + { + Segment v = this; + do + { + v.transform(at); + v = v.next; + } + while (v != this); + } + + /** + * Determines the winding direction of the path + * By the sign of the area. + */ + boolean hasClockwiseOrientation() + { + return (getSignedArea() > 0.0); + } + + /** + * Returns the bounds of this path + */ + public Rectangle2D getPathBounds() + { + double xmin; + double xmax; + double ymin; + double ymax; + xmin = xmax = P1.getX(); + ymin = ymax = P1.getY(); + + Segment v = this; + do + { + Rectangle2D r = v.getBounds(); + xmin = Math.min(r.getMinX(), xmin); + ymin = Math.min(r.getMinY(), ymin); + xmax = Math.max(r.getMaxX(), xmax); + ymax = Math.max(r.getMaxY(), ymax); + v = v.next; + } + while (v != this); + + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Calculates twice the signed area of the path; + */ + double getSignedArea() + { + Segment s; + double area = 0.0; + + s = this; + do + { + area += s.curveArea(); + + area += s.P1.getX() * s.next.P1.getY() + - s.P1.getY() * s.next.P1.getX(); + s = s.next; + } + while (s != this); + + return area; + } + + /** + * Reverses the orientation of the whole polygon + */ + void reverseAll() + { + reverseCoords(); + Segment v = next; + Segment former = this; + while (v != this) + { + v.reverseCoords(); + Segment vnext = v.next; + v.next = former; + former = v; + v = vnext; + } + next = former; + } + + /** + * Inserts a Segment after this one + */ + void insert(Segment v) + { + Segment n = next; + next = v; + v.next = n; + } + + /** + * Returns if this segment path is polygonal + */ + boolean isPolygonal() + { + Segment v = this; + do + { + if (! (v instanceof LineSegment)) + return false; + v = v.next; + } + while (v != this); + return true; + } + + /** + * Clones this path + */ + Segment cloneSegmentList() throws CloneNotSupportedException + { + Vector list = new Vector(); + Segment v = next; + + while (v != this) + { + list.add(v); + v = v.next; + } + + Segment clone = (Segment) this.clone(); + v = clone; + for (int i = 0; i < list.size(); i++) + { + clone.next = (Segment) ((Segment) list.elementAt(i)).clone(); + clone = clone.next; + } + clone.next = v; + return v; + } + + /** + * Creates a node between this segment and segment b + * at the given intersection + * @return the number of nodes created (0 or 1) + */ + int createNode(Segment b, Intersection i) + { + Point2D p = i.p; + if ((pointEquals(P1, p) || pointEquals(P2, p)) + && (pointEquals(b.P1, p) || pointEquals(b.P2, p))) + return 0; + + subdivideInsert(i.ta); + b.subdivideInsert(i.tb); + + // snap points + b.P2 = b.next.P1 = P2 = next.P1 = i.p; + + node = b.next; + b.node = next; + return 1; + } + + /** + * Creates multiple nodes from a list of intersections, + * This must be done in the order of ascending parameters, + * and the parameters must be recalculated in accordance + * with each split. + * @return the number of nodes created + */ + protected int createNodes(Segment b, Intersection[] x) + { + Vector v = new Vector(); + for (int i = 0; i < x.length; i++) + { + Point2D p = x[i].p; + if (! ((pointEquals(P1, p) || pointEquals(P2, p)) + && (pointEquals(b.P1, p) || pointEquals(b.P2, p)))) + v.add(x[i]); + } + + int nNodes = v.size(); + Intersection[] A = new Intersection[nNodes]; + Intersection[] B = new Intersection[nNodes]; + for (int i = 0; i < nNodes; i++) + A[i] = B[i] = (Intersection) v.elementAt(i); + + // Create two lists sorted by the parameter + // Bubble sort, OK I suppose, since the number of intersections + // cannot be larger than 9 (cubic-cubic worst case) anyway + for (int i = 0; i < nNodes - 1; i++) + { + for (int j = i + 1; j < nNodes; j++) + { + if (A[i].ta > A[j].ta) + { + Intersection swap = A[i]; + A[i] = A[j]; + A[j] = swap; + } + if (B[i].tb > B[j].tb) + { + Intersection swap = B[i]; + B[i] = B[j]; + B[j] = swap; + } + } + } + // subdivide a + Segment s = this; + for (int i = 0; i < nNodes; i++) + { + s.subdivideInsert(A[i].ta); + + // renormalize the parameters + for (int j = i + 1; j < nNodes; j++) + A[j].ta = (A[j].ta - A[i].ta) / (1.0 - A[i].ta); + + A[i].seg = s; + s = s.next; + } + + // subdivide b, set nodes + s = b; + for (int i = 0; i < nNodes; i++) + { + s.subdivideInsert(B[i].tb); + + for (int j = i + 1; j < nNodes; j++) + B[j].tb = (B[j].tb - B[i].tb) / (1.0 - B[i].tb); + + // set nodes + B[i].seg.node = s.next; // node a -> b + s.node = B[i].seg.next; // node b -> a + + // snap points + B[i].seg.P2 = B[i].seg.next.P1 = s.P2 = s.next.P1 = B[i].p; + s = s.next; + } + return nNodes; + } + + /** + * Determines if two paths are equal. + * Colinear line segments are ignored in the comparison. + */ + boolean pathEquals(Segment B) + { + if (! getPathBounds().equals(B.getPathBounds())) + return false; + + Segment startA = getTopLeft(); + Segment startB = B.getTopLeft(); + Segment a = startA; + Segment b = startB; + do + { + if (! a.equals(b)) + return false; + + if (a instanceof LineSegment) + a = ((LineSegment) a).lastCoLinear(); + if (b instanceof LineSegment) + b = ((LineSegment) b).lastCoLinear(); + + a = a.next; + b = b.next; + } + while (a != startA && b != startB); + return true; + } + + /** + * Return the segment with the top-leftmost first point + */ + Segment getTopLeft() + { + Segment v = this; + Segment tl = this; + do + { + if (v.P1.getY() < tl.P1.getY()) + tl = v; + else if (v.P1.getY() == tl.P1.getY()) + { + if (v.P1.getX() < tl.P1.getX()) + tl = v; + } + v = v.next; + } + while (v != this); + return tl; + } + + /** + * Returns if the path has a segment outside a shape + */ + boolean isSegmentOutside(Shape shape) + { + return ! shape.contains(getMidPoint()); + } + } // class Segment + + private class LineSegment extends Segment + { + public LineSegment(double x1, double y1, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + } + + public LineSegment(Point2D p1, Point2D p2) + { + super(); + P1 = (Point2D) p1.clone(); + P2 = (Point2D) p2.clone(); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new LineSegment(P1, P2); + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return (new Point2D.Double(0.5 * (P1.getX() + P2.getX()), + 0.5 * (P1.getY() + P2.getY()))); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * Obviously, a line does not enclose any area besides the line + */ + double curveArea() + { + return 0; + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_LINETO); + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + Point2D p = new Point2D.Double((P2.getX() - P1.getX()) * t + P1.getX(), + (P2.getY() - P1.getY()) * t + P1.getY()); + insert(new LineSegment(p, P2)); + P2 = p; + next.node = node; + node = null; + } + + /** + * Determines if two line segments are strictly colinear + */ + boolean isCoLinear(LineSegment b) + { + double x1 = P1.getX(); + double y1 = P1.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + double x3 = b.P1.getX(); + double y3 = b.P1.getY(); + double x4 = b.P2.getX(); + double y4 = b.P2.getY(); + + if ((y1 - y3) * (x4 - x3) - (x1 - x3) * (y4 - y3) != 0.0) + return false; + + return ((x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3) == 0.0); + } + + /** + * Return the last segment colinear with this one. + * Used in comparing paths. + */ + Segment lastCoLinear() + { + Segment prev = this; + Segment v = next; + + while (v instanceof LineSegment) + { + if (isCoLinear((LineSegment) v)) + { + prev = v; + v = v.next; + } + else + return prev; + } + return prev; + } + + /** + * Compare two segments. + * We must take into account that the lines may be broken into colinear + * subsegments and ignore them. + */ + boolean equals(Segment b) + { + if (! (b instanceof LineSegment)) + return false; + Point2D p1 = P1; + Point2D p3 = b.P1; + + if (! p1.equals(p3)) + return false; + + Point2D p2 = lastCoLinear().P2; + Point2D p4 = ((LineSegment) b).lastCoLinear().P2; + return (p2.equals(p4)); + } + + /** + * Returns a line segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = P2.getX(); + coords[1] = P2.getY(); + return (PathIterator.SEG_LINETO); + } + + /** + * Returns if the line has intersections. + */ + boolean hasIntersections(Segment b) + { + if (b instanceof LineSegment) + return (linesIntersect(this, (LineSegment) b) != null); + + if (b instanceof QuadSegment) + return (lineQuadIntersect(this, (QuadSegment) b) != null); + + if (b instanceof CubicSegment) + return (lineCubicIntersect(this, (CubicSegment) b) != null); + + return false; + } + + /** + * Splits intersections into nodes, + * This one handles line-line, line-quadratic, line-cubic + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + { + Intersection i = linesIntersect(this, (LineSegment) b); + + if (i == null) + return 0; + + return createNode(b, i); + } + + Intersection[] x = null; + + if (b instanceof QuadSegment) + x = lineQuadIntersect(this, (QuadSegment) b); + + if (b instanceof CubicSegment) + x = lineCubicIntersect(this, (CubicSegment) b); + + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, (Intersection) x[0]); + + return createNodes(b, x); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + return (new Rectangle2D.Double(Math.min(P1.getX(), P2.getX()), + Math.min(P1.getY(), P2.getY()), + Math.abs(P1.getX() - P2.getX()), + Math.abs(P1.getY() - P2.getY()))); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = P2.getX() - x; + double y1 = P2.getY() - y; + + if (y0 * y1 > 0) + return 0; + + if (x0 < 0 && x1 < 0) + return 0; + + if (y0 == 0.0) + y0 -= EPSILON; + + if (y1 == 0.0) + y1 -= EPSILON; + + if (Line2D.linesIntersect(x0, y0, x1, y1, + EPSILON, 0.0, Double.MAX_VALUE, 0.0)) + return 1; + return 0; + } + } // class LineSegment + + /** + * Quadratic Bezier curve segment + * + * Note: Most peers don't support quadratics directly, so it might make + * sense to represent them as cubics internally and just be done with it. + * I think we should be peer-agnostic, however, and stay faithful to the + * input geometry types as far as possible. + */ + private class QuadSegment extends Segment + { + Point2D cp; // control point + + /** + * Constructor, takes the coordinates of the start, control, + * and end point, respectively. + */ + QuadSegment(double x1, double y1, double cx, double cy, double x2, + double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp = new Point2D.Double(cx, cy); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new QuadSegment(P1.getX(), P1.getY(), cp.getX(), cp.getY(), + P2.getX(), P2.getY()); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * + * The area formula can be derived by using Green's formula in the + * plane on the parametric form of the bezier. + */ + double curveArea() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + double P = (y2 - 2 * y1 + y0); + double Q = 2 * (y1 - y0); + + double A = (x2 - 2 * x1 + x0); + double B = 2 * (x1 - x0); + + double area = (B * P - A * Q) / 3.0; + return (area); + } + + /** + * Compare two segments. + */ + boolean equals(Segment b) + { + if (! (b instanceof QuadSegment)) + return false; + + return (P1.equals(b.P1) && cp.equals(((QuadSegment) b).cp) + && P2.equals(b.P2)); + } + + /** + * Returns a Point2D corresponding to the parametric value t + * of the curve + */ + Point2D evaluatePoint(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + return new Point2D.Double(t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + + x0, + t * t * (y2 - 2 * y1 + y0) + 2 * t * (y1 - y0) + + y0); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + double r0; + double r1; + + double xmax = Math.max(x0, x2); + double ymax = Math.max(y0, y2); + double xmin = Math.min(x0, x2); + double ymin = Math.min(y0, y2); + + r0 = 2 * (y1 - y0); + r1 = 2 * (y2 - 2 * y1 + y0); + if (r1 != 0.0) + { + double t = -r0 / r1; + if (t > 0.0 && t < 1.0) + { + double y = evaluatePoint(t).getY(); + ymax = Math.max(y, ymax); + ymin = Math.min(y, ymin); + } + } + r0 = 2 * (x1 - x0); + r1 = 2 * (x2 - 2 * x1 + x0); + if (r1 != 0.0) + { + double t = -r0 / r1; + if (t > 0.0 && t < 1.0) + { + double x = evaluatePoint(t).getY(); + xmax = Math.max(x, xmax); + xmin = Math.min(x, xmin); + } + } + + return (new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin)); + } + + /** + * Returns a cubic segment corresponding to this curve + */ + CubicSegment getCubicSegment() + { + double x1 = P1.getX() + 2.0 * (cp.getX() - P1.getX()) / 3.0; + double y1 = P1.getY() + 2.0 * (cp.getY() - P1.getY()) / 3.0; + double x2 = cp.getX() + (P2.getX() - cp.getX()) / 3.0; + double y2 = cp.getY() + (P2.getY() - cp.getY()) / 3.0; + + return new CubicSegment(P1.getX(), P1.getY(), x1, y1, x2, y2, P2.getX(), + P2.getY()); + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return evaluatePoint(0.5); + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_QUADTO); + } + + /** + * Returns the PathIterator coords of a segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = cp.getX(); + coords[1] = cp.getY(); + coords[2] = P2.getX(); + coords[3] = P2.getY(); + return (PathIterator.SEG_QUADTO); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = cp.getX() - x; + double y1 = cp.getY() - y; + double x2 = P2.getX() - x; + double y2 = P2.getY() - y; + double[] r = new double[3]; + int nRoots; + int nCrossings = 0; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) && (y0 * y1 <= 0 || y1 * y2 <= 0)) + { + if (y0 == 0.0) + y0 -= EPSILON; + if (y2 == 0.0) + y2 -= EPSILON; + + r[0] = y0; + r[1] = 2 * (y1 - y0); + r[2] = (y2 - 2 * y1 + y0); + + nRoots = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < nRoots; i++) + if (r[i] > 0.0f && r[i] < 1.0f) + { + double t = r[i]; + if (t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + x0 > 0.0) + nCrossings++; + } + } + return nCrossings; + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D temp = P1; + P1 = P2; + P2 = temp; + } + + /** + * Splits intersections into nodes, + * This one handles quadratic-quadratic only, + * Quadratic-line is passed on to the LineSegment class, + * Quadratic-cubic is passed on to the CubicSegment class + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + return (b.splitIntersections(this)); + + if (b instanceof CubicSegment) + return (b.splitIntersections(this)); + + if (b instanceof QuadSegment) + { + // Use the cubic-cubic intersection routine for quads as well, + // Since a quadratic can be exactly described as a cubic, this + // should not be a problem; + // The recursion depth will be the same in any case. + Intersection[] x = cubicCubicIntersect(getCubicSegment(), + ((QuadSegment) b) + .getCubicSegment()); + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, (Intersection) x[0]); + + return createNodes(b, x); + } + return 0; + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + double p10x = x0 + t * (x1 - x0); + double p10y = y0 + t * (y1 - y0); + double p11x = x1 + t * (x2 - x1); + double p11y = y1 + t * (y2 - y1); + double p20x = p10x + t * (p11x - p10x); + double p20y = p10y + t * (p11y - p10y); + + insert(new QuadSegment(p20x, p20y, p11x, p11y, x2, y2)); + P2 = next.P1; + cp.setLocation(p10x, p10y); + + next.node = node; + node = null; + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + cp = at.transform(cp, null); + } + } // class QuadSegment + + /** + * Cubic Bezier curve segment + */ + private class CubicSegment extends Segment + { + Point2D cp1; // control points + Point2D cp2; // control points + + /** + * Constructor - takes coordinates of the starting point, + * first control point, second control point and end point, + * respecively. + */ + public CubicSegment(double x1, double y1, double c1x, double c1y, + double c2x, double c2y, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp1 = new Point2D.Double(c1x, c1y); + cp2 = new Point2D.Double(c2x, c2y); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new CubicSegment(P1.getX(), P1.getY(), cp1.getX(), cp1.getY(), + cp2.getX(), cp2.getY(), P2.getX(), P2.getY()); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * + * The area formula can be derived by using Green's formula in the + * plane on the parametric form of the bezier. + */ + double curveArea() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + + double P = y3 - 3 * y2 + 3 * y1 - y0; + double Q = 3 * (y2 + y0 - 2 * y1); + double R = 3 * (y1 - y0); + + double A = x3 - 3 * x2 + 3 * x1 - x0; + double B = 3 * (x2 + x0 - 2 * x1); + double C = 3 * (x1 - x0); + + double area = (B * P - A * Q) / 5.0 + (C * P - A * R) / 2.0 + + (C * Q - B * R) / 3.0; + + return (area); + } + + /** + * Compare two segments. + */ + boolean equals(Segment b) + { + if (! (b instanceof CubicSegment)) + return false; + + return (P1.equals(b.P1) && cp1.equals(((CubicSegment) b).cp1) + && cp2.equals(((CubicSegment) b).cp2) && P2.equals(b.P2)); + } + + /** + * Returns a Point2D corresponding to the parametric value t + * of the curve + */ + Point2D evaluatePoint(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + + return new Point2D.Double(-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + + 3 * t * (x1 - x0) + x0, + -(t * t * t) * (y0 - 3 * y1 + 3 * y2 - y3) + + 3 * t * t * (y0 - 2 * y1 + y2) + + 3 * t * (y1 - y0) + y0); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + double[] r = new double[3]; + + double xmax = Math.max(x0, x3); + double ymax = Math.max(y0, y3); + double xmin = Math.min(x0, x3); + double ymin = Math.min(y0, y3); + + r[0] = 3 * (y1 - y0); + r[1] = 6.0 * (y2 + y0 - 2 * y1); + r[2] = 3.0 * (y3 - 3 * y2 + 3 * y1 - y0); + + int n = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < n; i++) + { + double t = r[i]; + if (t > 0 && t < 1.0) + { + double y = evaluatePoint(t).getY(); + ymax = Math.max(y, ymax); + ymin = Math.min(y, ymin); + } + } + + r[0] = 3 * (x1 - x0); + r[1] = 6.0 * (x2 + x0 - 2 * x1); + r[2] = 3.0 * (x3 - 3 * x2 + 3 * x1 - x0); + n = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < n; i++) + { + double t = r[i]; + if (t > 0 && t < 1.0) + { + double x = evaluatePoint(t).getX(); + xmax = Math.max(x, xmax); + xmin = Math.min(x, xmin); + } + } + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Returns a CubicCurve2D object corresponding to this segment. + */ + CubicCurve2D getCubicCurve2D() + { + return new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(), + cp1.getY(), cp2.getX(), cp2.getY(), + P2.getX(), P2.getY()); + } + + /** + * Returns the parametric points of self-intersection if the cubic + * is self-intersecting, null otherwise. + */ + double[] getLoop() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + double[] r = new double[4]; + double k; + double R; + double T; + double A; + double B; + double[] results = new double[2]; + + R = x3 - 3 * x2 + 3 * x1 - x0; + T = y3 - 3 * y2 + 3 * y1 - y0; + + // A qudratic + if (R == 0.0 && T == 0.0) + return null; + + // true cubic + if (R != 0.0 && T != 0.0) + { + A = 3 * (x2 + x0 - 2 * x1) / R; + B = 3 * (x1 - x0) / R; + + double P = 3 * (y2 + y0 - 2 * y1) / T; + double Q = 3 * (y1 - y0) / T; + + if (A == P || Q == B) + return null; + + k = (Q - B) / (A - P); + } + else + { + if (R == 0.0) + { + // quadratic in x + k = -(3 * (x1 - x0)) / (3 * (x2 + x0 - 2 * x1)); + A = 3 * (y2 + y0 - 2 * y1) / T; + B = 3 * (y1 - y0) / T; + } + else + { + // quadratic in y + k = -(3 * (y1 - y0)) / (3 * (y2 + y0 - 2 * y1)); + A = 3 * (x2 + x0 - 2 * x1) / R; + B = 3 * (x1 - x0) / R; + } + } + + r[0] = -k * k * k - A * k * k - B * k; + r[1] = 3 * k * k + 2 * k * A + 2 * B; + r[2] = -3 * k; + r[3] = 2; + + int n = CubicCurve2D.solveCubic(r); + if (n != 3) + return null; + + // sort r + double t; + for (int i = 0; i < 2; i++) + for (int j = i + 1; j < 3; j++) + if (r[j] < r[i]) + { + t = r[i]; + r[i] = r[j]; + r[j] = t; + } + + if (Math.abs(r[0] + r[2] - k) < 1E-13) + if (r[0] >= 0.0 && r[0] <= 1.0 && r[2] >= 0.0 && r[2] <= 1.0) + if (evaluatePoint(r[0]).distance(evaluatePoint(r[2])) < PE_EPSILON * 10) + { // we snap the points anyway + results[0] = r[0]; + results[1] = r[2]; + return (results); + } + return null; + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return evaluatePoint(0.5); + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_CUBICTO); + } + + /** + * Returns the PathIterator coords of a segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = cp1.getX(); + coords[1] = cp1.getY(); + coords[2] = cp2.getX(); + coords[3] = cp2.getY(); + coords[4] = P2.getX(); + coords[5] = P2.getY(); + return (PathIterator.SEG_CUBICTO); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = cp1.getX() - x; + double y1 = cp1.getY() - y; + double x2 = cp2.getX() - x; + double y2 = cp2.getY() - y; + double x3 = P2.getX() - x; + double y3 = P2.getY() - y; + double[] r = new double[4]; + int nRoots; + int nCrossings = 0; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0)) + { + if (y0 == 0.0) + y0 -= EPSILON; + if (y3 == 0.0) + y3 -= EPSILON; + + r[0] = y0; + r[1] = 3 * (y1 - y0); + r[2] = 3 * (y2 + y0 - 2 * y1); + r[3] = y3 - 3 * y2 + 3 * y1 - y0; + + if ((nRoots = CubicCurve2D.solveCubic(r)) > 0) + for (int i = 0; i < nRoots; i++) + { + if (r[i] > 0.0 && r[i] < 1.0) + { + double t = r[i]; + if (-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + 3 * t * (x1 - x0) + + x0 > 0.0) + nCrossings++; + } + } + } + return nCrossings; + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D p = P1; + P1 = P2; + P2 = p; + p = cp1; // swap control points + cp1 = cp2; + cp2 = p; + } + + /** + * Splits intersections into nodes, + * This one handles cubic-cubic and cubic-quadratic intersections + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + return (b.splitIntersections(this)); + + Intersection[] x = null; + + if (b instanceof QuadSegment) + x = cubicCubicIntersect(this, ((QuadSegment) b).getCubicSegment()); + + if (b instanceof CubicSegment) + x = cubicCubicIntersect(this, (CubicSegment) b); + + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, x[0]); + + return createNodes(b, x); + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + CubicSegment s = (CubicSegment) clone(); + double p1x = (s.cp1.getX() - s.P1.getX()) * t + s.P1.getX(); + double p1y = (s.cp1.getY() - s.P1.getY()) * t + s.P1.getY(); + + double px = (s.cp2.getX() - s.cp1.getX()) * t + s.cp1.getX(); + double py = (s.cp2.getY() - s.cp1.getY()) * t + s.cp1.getY(); + + s.cp2.setLocation((s.P2.getX() - s.cp2.getX()) * t + s.cp2.getX(), + (s.P2.getY() - s.cp2.getY()) * t + s.cp2.getY()); + + s.cp1.setLocation((s.cp2.getX() - px) * t + px, + (s.cp2.getY() - py) * t + py); + + double p2x = (px - p1x) * t + p1x; + double p2y = (py - p1y) * t + p1y; + + double p3x = (s.cp1.getX() - p2x) * t + p2x; + double p3y = (s.cp1.getY() - p2y) * t + p2y; + s.P1.setLocation(p3x, p3y); + + // insert new curve + insert(s); + + // set this curve + cp1.setLocation(p1x, p1y); + cp2.setLocation(p2x, p2y); + P2 = s.P1; + next.node = node; + node = null; + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + cp1 = at.transform(cp1, null); + cp2 = at.transform(cp2, null); + } + } // class CubicSegment +} // class Area diff --git a/libjava/classpath/java/awt/geom/CubicCurve2D.java b/libjava/classpath/java/awt/geom/CubicCurve2D.java new file mode 100644 index 00000000000..50c381194bb --- /dev/null +++ b/libjava/classpath/java/awt/geom/CubicCurve2D.java @@ -0,0 +1,1724 @@ +/* CubicCurve2D.java -- represents a parameterized cubic curve in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + + +/** + * A two-dimensional curve that is parameterized with a cubic + * function. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Graydon Hoare (graydon@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public abstract class CubicCurve2D implements Shape, Cloneable +{ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + private static final double EPSILON = 1E-10; + + /** + * Constructs a new CubicCurve2D. Typical users will want to + * construct instances of a subclass, such as {@link + * CubicCurve2D.Float} or {@link CubicCurve2D.Double}. + */ + protected CubicCurve2D() + { + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public abstract double getX1(); + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public abstract double getY1(); + + /** + * Returns the curve’s start point. + */ + public abstract Point2D getP1(); + + /** + * Returns the <i>x</i> coordinate of the curve’s first + * control point. + */ + public abstract double getCtrlX1(); + + /** + * Returns the <i>y</i> coordinate of the curve’s first + * control point. + */ + public abstract double getCtrlY1(); + + /** + * Returns the curve’s first control point. + */ + public abstract Point2D getCtrlP1(); + + /** + * Returns the <i>x</i> coordinate of the curve’s second + * control point. + */ + public abstract double getCtrlX2(); + + /** + * Returns the <i>y</i> coordinate of the curve’s second + * control point. + */ + public abstract double getCtrlY2(); + + /** + * Returns the curve’s second control point. + */ + public abstract Point2D getCtrlP2(); + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public abstract double getX2(); + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public abstract double getY2(); + + /** + * Returns the curve’s end point. + */ + public abstract Point2D getP2(); + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s new start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s new + * first control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s new + * first control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s new + * second control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s new + * second control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new end + * point. + */ + public abstract void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2); + + /** + * Changes the curve geometry, specifying coordinate values in an + * array. + * + * @param coords an array containing the new coordinate values. The + * <i>x</i> coordinate of the new start point is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * new first control point is located at <code>coords[offset + + * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 3]</code>. The <i>x</i> coordinate of the new second control + * point is located at <code>coords[offset + 4]</code>, its <i>y</i> + * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> + * coordinate of the new end point is located at <code>coords[offset + * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 7]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + + /** + * Changes the curve geometry, specifying coordinate values in + * separate Point objects. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * <p>The curve does not keep any reference to the passed point + * objects. Therefore, a later change to <code>p1</code>, + * <code>c1</code>, <code>c2</code> or <code>p2</code> will not + * affect the curve geometry. + * + * @param p1 the new start point. + * @param c1 the new first control point. + * @param c2 the new second control point. + * @param p2 the new end point. + */ + public void setCurve(Point2D p1, Point2D c1, Point2D c2, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c1.getX(), c1.getY(), c2.getX(), c2.getY(), + p2.getX(), p2.getY()); + } + + /** + * Changes the curve geometry, specifying coordinate values in an + * array of Point objects. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * <p>The curve does not keep references to the passed point + * objects. Therefore, a later change to the <code>pts</code> array + * or any of its elements will not affect the curve geometry. + * + * @param pts an array containing the points. The new start point + * is located at <code>pts[offset]</code>, the new first control + * point at <code>pts[offset + 1]</code>, the new second control + * point at <code>pts[offset + 2]</code>, and the new end point + * at <code>pts[offset + 3]</code>. + * + * @param offset the offset of the start point in <code>pts</code>. + */ + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset++].getY(), pts[offset].getX(), + pts[offset++].getY(), pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY()); + } + + /** + * Changes the curve geometry to that of another curve. + * + * @param c the curve whose coordinates will be copied. + */ + public void setCurve(CubicCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), + c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); + } + + /** + * Calculates the squared flatness of a cubic curve, directly + * specifying each coordinate value. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + * + * @param x1 the <i>x</i> coordinate of the start point P1. + * @param y1 the <i>y</i> coordinate of the start point P1. + * @param cx1 the <i>x</i> coordinate of the first control point C1. + * @param cy1 the <i>y</i> coordinate of the first control point C1. + * @param cx2 the <i>x</i> coordinate of the second control point C2. + * @param cy2 the <i>y</i> coordinate of the second control point C2. + * @param x2 the <i>x</i> coordinate of the end point P2. + * @param y2 the <i>y</i> coordinate of the end point P2. + */ + public static double getFlatnessSq(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, cx1, cy1), + Line2D.ptSegDistSq(x1, y1, x2, y2, cx2, cy2)); + } + + /** + * Calculates the flatness of a cubic curve, directly specifying + * each coordinate value. The flatness is the maximal distance of a + * control point to the line between start and end point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + * + * @param x1 the <i>x</i> coordinate of the start point P1. + * @param y1 the <i>y</i> coordinate of the start point P1. + * @param cx1 the <i>x</i> coordinate of the first control point C1. + * @param cy1 the <i>y</i> coordinate of the first control point C1. + * @param cx2 the <i>x</i> coordinate of the second control point C2. + * @param cy2 the <i>y</i> coordinate of the second control point C2. + * @param x2 the <i>x</i> coordinate of the end point P2. + * @param y2 the <i>y</i> coordinate of the end point P2. + */ + public static double getFlatness(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + return Math.sqrt(getFlatnessSq(x1, y1, cx1, cy1, cx2, cy2, x2, y2)); + } + + /** + * Calculates the squared flatness of a cubic curve, specifying the + * coordinate values in an array. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + * + * @param coords an array containing the coordinate values. The + * <i>x</i> coordinate of the start point P1 is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * first control point C1 is located at <code>coords[offset + + * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 3]</code>. The <i>x</i> coordinate of the second control point C2 + * is located at <code>coords[offset + 4]</code>, its <i>y</i> + * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> + * coordinate of the end point P2 is located at <code>coords[offset + * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 7]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public static double getFlatnessSq(double[] coords, int offset) + { + return getFlatnessSq(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + + /** + * Calculates the flatness of a cubic curve, specifying the + * coordinate values in an array. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + * + * @param coords an array containing the coordinate values. The + * <i>x</i> coordinate of the start point P1 is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * first control point C1 is located at <code>coords[offset + + * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 3]</code>. The <i>x</i> coordinate of the second control point C2 + * is located at <code>coords[offset + 4]</code>, its <i>y</i> + * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> + * coordinate of the end point P2 is located at <code>coords[offset + * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + + * 7]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public static double getFlatness(double[] coords, int offset) + { + return Math.sqrt(getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++])); + } + + /** + * Calculates the squared flatness of this curve. The flatness is + * the maximal distance of a control point to the line between start + * and end point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + */ + public double getFlatnessSq() + { + return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2()); + } + + /** + * Calculates the flatness of this curve. The flatness is the + * maximal distance of a control point to the line between start and + * end point. + * + * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + */ + public double getFlatness() + { + return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2())); + } + + /** + * Subdivides this curve into two halves. + * + * <p><img src="doc-files/CubicCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a CubicCurve2D" /> + * + * @param left a curve whose geometry will be set to the left half + * of this curve, or <code>null</code> if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of this curve, or <code>null</code> if the caller is not + * interested in the right half. + */ + public void subdivide(CubicCurve2D left, CubicCurve2D right) + { + // Use empty slots at end to share single array. + double[] d = new double[] + { + getX1(), getY1(), getCtrlX1(), getCtrlY1(), getCtrlX2(), + getCtrlY2(), getX2(), getY2(), 0, 0, 0, 0, 0, 0 + }; + subdivide(d, 0, d, 0, d, 6); + if (left != null) + left.setCurve(d, 0); + if (right != null) + right.setCurve(d, 6); + } + + /** + * Subdivides a cubic curve into two halves. + * + * <p><img src="doc-files/CubicCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a CubicCurve2D" /> + * + * @param src the curve to be subdivided. + * + * @param left a curve whose geometry will be set to the left half + * of <code>src</code>, or <code>null</code> if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of <code>src</code>, or <code>null</code> if the caller is not + * interested in the right half. + */ + public static void subdivide(CubicCurve2D src, CubicCurve2D left, + CubicCurve2D right) + { + src.subdivide(left, right); + } + + /** + * Subdivides a cubic curve into two halves, passing all coordinates + * in an array. + * + * <p><img src="doc-files/CubicCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a CubicCurve2D" /> + * + * <p>The left end point and the right start point will always be + * identical. Memory-concious programmers thus may want to pass the + * same array for both <code>left</code> and <code>right</code>, and + * set <code>rightOff</code> to <code>leftOff + 6</code>. + * + * @param src an array containing the coordinates of the curve to be + * subdivided. The <i>x</i> coordinate of the start point P1 is + * located at <code>src[srcOff]</code>, its <i>y</i> at + * <code>src[srcOff + 1]</code>. The <i>x</i> coordinate of the + * first control point C1 is located at <code>src[srcOff + + * 2]</code>, its <i>y</i> at <code>src[srcOff + 3]</code>. The + * <i>x</i> coordinate of the second control point C2 is located at + * <code>src[srcOff + 4]</code>, its <i>y</i> at <code>src[srcOff + + * 5]</code>. The <i>x</i> coordinate of the end point is located at + * <code>src[srcOff + 6]</code>, its <i>y</i> at <code>src[srcOff + + * 7]</code>. + * + * @param srcOff an offset into <code>src</code>, specifying + * the index of the start point’s <i>x</i> coordinate. + * + * @param left an array that will receive the coordinates of the + * left half of <code>src</code>. It is acceptable to pass + * <code>src</code>. A caller who is not interested in the left half + * can pass <code>null</code>. + * + * @param leftOff an offset into <code>left</code>, specifying the + * index where the start point’s <i>x</i> coordinate will be + * stored. + * + * @param right an array that will receive the coordinates of the + * right half of <code>src</code>. It is acceptable to pass + * <code>src</code> or <code>left</code>. A caller who is not + * interested in the right half can pass <code>null</code>. + * + * @param rightOff an offset into <code>right</code>, specifying the + * index where the start point’s <i>x</i> coordinate will be + * stored. + */ + public static void subdivide(double[] src, int srcOff, double[] left, + int leftOff, double[] right, int rightOff) + { + // To understand this code, please have a look at the image + // "CubicCurve2D-3.png" in the sub-directory "doc-files". + double src_C1_x; + double src_C1_y; + double src_C2_x; + double src_C2_y; + double left_P1_x; + double left_P1_y; + double left_C1_x; + double left_C1_y; + double left_C2_x; + double left_C2_y; + double right_C1_x; + double right_C1_y; + double right_C2_x; + double right_C2_y; + double right_P2_x; + double right_P2_y; + double Mid_x; // Mid = left.P2 = right.P1 + double Mid_y; // Mid = left.P2 = right.P1 + + left_P1_x = src[srcOff]; + left_P1_y = src[srcOff + 1]; + src_C1_x = src[srcOff + 2]; + src_C1_y = src[srcOff + 3]; + src_C2_x = src[srcOff + 4]; + src_C2_y = src[srcOff + 5]; + right_P2_x = src[srcOff + 6]; + right_P2_y = src[srcOff + 7]; + + left_C1_x = (left_P1_x + src_C1_x) / 2; + left_C1_y = (left_P1_y + src_C1_y) / 2; + right_C2_x = (right_P2_x + src_C2_x) / 2; + right_C2_y = (right_P2_y + src_C2_y) / 2; + Mid_x = (src_C1_x + src_C2_x) / 2; + Mid_y = (src_C1_y + src_C2_y) / 2; + left_C2_x = (left_C1_x + Mid_x) / 2; + left_C2_y = (left_C1_y + Mid_y) / 2; + right_C1_x = (Mid_x + right_C2_x) / 2; + right_C1_y = (Mid_y + right_C2_y) / 2; + Mid_x = (left_C2_x + right_C1_x) / 2; + Mid_y = (left_C2_y + right_C1_y) / 2; + + if (left != null) + { + left[leftOff] = left_P1_x; + left[leftOff + 1] = left_P1_y; + left[leftOff + 2] = left_C1_x; + left[leftOff + 3] = left_C1_y; + left[leftOff + 4] = left_C2_x; + left[leftOff + 5] = left_C2_y; + left[leftOff + 6] = Mid_x; + left[leftOff + 7] = Mid_y; + } + + if (right != null) + { + right[rightOff] = Mid_x; + right[rightOff + 1] = Mid_y; + right[rightOff + 2] = right_C1_x; + right[rightOff + 3] = right_C1_y; + right[rightOff + 4] = right_C2_x; + right[rightOff + 5] = right_C2_y; + right[rightOff + 6] = right_P2_x; + right[rightOff + 7] = right_P2_y; + } + } + + /** + * Finds the non-complex roots of a cubic equation, placing the + * results into the same array as the equation coefficients. The + * following equation is being solved: + * + * <blockquote><code>eqn[3]</code> · <i>x</i><sup>3</sup> + * + <code>eqn[2]</code> · <i>x</i><sup>2</sup> + * + <code>eqn[1]</code> · <i>x</i> + * + <code>eqn[0]</code> + * = 0 + * </blockquote> + * + * <p>For some background about solving cubic equations, see the + * article <a + * href="http://planetmath.org/encyclopedia/CubicFormula.html" + * >“Cubic Formula”</a> in <a + * href="http://planetmath.org/" >PlanetMath</a>. For an extensive + * library of numerical algorithms written in the C programming + * language, see the <a href= "http://www.gnu.org/software/gsl/">GNU + * Scientific Library</a>, from which this implementation was + * adapted. + * + * @param eqn an array with the coefficients of the equation. When + * this procedure has returned, <code>eqn</code> will contain the + * non-complex solutions of the equation, in no particular order. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @see #solveCubic(double[], double[]) + * @see QuadCurve2D#solveQuadratic(double[],double[]) + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the <a href= + * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveCubic(double[] eqn) + { + return solveCubic(eqn, eqn); + } + + /** + * Finds the non-complex roots of a cubic equation. The following + * equation is being solved: + * + * <blockquote><code>eqn[3]</code> · <i>x</i><sup>3</sup> + * + <code>eqn[2]</code> · <i>x</i><sup>2</sup> + * + <code>eqn[1]</code> · <i>x</i> + * + <code>eqn[0]</code> + * = 0 + * </blockquote> + * + * <p>For some background about solving cubic equations, see the + * article <a + * href="http://planetmath.org/encyclopedia/CubicFormula.html" + * >“Cubic Formula”</a> in <a + * href="http://planetmath.org/" >PlanetMath</a>. For an extensive + * library of numerical algorithms written in the C programming + * language, see the <a href= "http://www.gnu.org/software/gsl/">GNU + * Scientific Library</a>, from which this implementation was + * adapted. + * + * @see QuadCurve2D#solveQuadratic(double[],double[]) + * + * @param eqn an array with the coefficients of the equation. + * + * @param res an array into which the non-complex roots will be + * stored. The results may be in an arbitrary order. It is safe to + * pass the same array object reference for both <code>eqn</code> + * and <code>res</code>. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the <a href= + * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveCubic(double[] eqn, double[] res) + { + // Adapted from poly/solve_cubic.c in the GNU Scientific Library + // (GSL), revision 1.7 of 2003-07-26. For the original source, see + // http://www.gnu.org/software/gsl/ + // + // Brian Gough, the author of that code, has granted the + // permission to use it in GNU Classpath under the GNU Classpath + // license, and has assigned the copyright to the Free Software + // Foundation. + // + // The Java implementation is very similar to the GSL code, but + // not a strict one-to-one copy. For example, GSL would sort the + // result. + + double a; + double b; + double c; + double q; + double r; + double Q; + double R; + double c3; + double Q3; + double R2; + double CR2; + double CQ3; + + // If the cubic coefficient is zero, we have a quadratic equation. + c3 = eqn[3]; + if (c3 == 0) + return QuadCurve2D.solveQuadratic(eqn, res); + + // Divide the equation by the cubic coefficient. + c = eqn[0] / c3; + b = eqn[1] / c3; + a = eqn[2] / c3; + + // We now need to solve x^3 + ax^2 + bx + c = 0. + q = a * a - 3 * b; + r = 2 * a * a * a - 9 * a * b + 27 * c; + + Q = q / 9; + R = r / 54; + + Q3 = Q * Q * Q; + R2 = R * R; + + CR2 = 729 * r * r; + CQ3 = 2916 * q * q * q; + + if (R == 0 && Q == 0) + { + // The GNU Scientific Library would return three identical + // solutions in this case. + res[0] = -a / 3; + return 1; + } + + if (CR2 == CQ3) + { + /* this test is actually R2 == Q3, written in a form suitable + for exact computation with integers */ + /* Due to finite precision some double roots may be missed, and + considered to be a pair of complex roots z = x +/- epsilon i + close to the real axis. */ + double sqrtQ = Math.sqrt(Q); + + if (R > 0) + { + res[0] = -2 * sqrtQ - a / 3; + res[1] = sqrtQ - a / 3; + } + else + { + res[0] = -sqrtQ - a / 3; + res[1] = 2 * sqrtQ - a / 3; + } + return 2; + } + + if (CR2 < CQ3) /* equivalent to R2 < Q3 */ + { + double sqrtQ = Math.sqrt(Q); + double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ; + double theta = Math.acos(R / sqrtQ3); + double norm = -2 * sqrtQ; + res[0] = norm * Math.cos(theta / 3) - a / 3; + res[1] = norm * Math.cos((theta + 2.0 * Math.PI) / 3) - a / 3; + res[2] = norm * Math.cos((theta - 2.0 * Math.PI) / 3) - a / 3; + + // The GNU Scientific Library sorts the results. We don't. + return 3; + } + + double sgnR = (R >= 0 ? 1 : -1); + double A = -sgnR * Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0); + double B = Q / A; + res[0] = A + B - a / 3; + return 1; + } + + /** + * Determines whether a position lies inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + */ + public boolean contains(double x, double y) + { + if (! getBounds2D().contains(x, y)) + return false; + + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } + + /** + * Determines whether a point lies inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines whether any part of a rectangle is inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” in a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean intersects(double x, double y, double w, double h) + { + if (! getBounds2D().contains(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether any part of a Rectangle2D is inside the area bounded + * by the curve and the straight line connecting its end points. + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determine whether a rectangle is entirely inside the area that is bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determine whether a Rectangle2D is entirely inside the area that is + * bounded by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getCtrlX1(); + coords[1] = (float) getCtrlY1(); + coords[2] = (float) getCtrlX2(); + coords[3] = (float) getCtrlY2(); + coords[4] = (float) getX2(); + coords[5] = (float) getY2(); + result = SEG_CUBICTO; + break; + default: + throw new NoSuchElementException("cubic iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getCtrlX1(); + coords[1] = getCtrlY1(); + coords[2] = getCtrlX2(); + coords[3] = getCtrlY2(); + coords[4] = getX2(); + coords[5] = getY2(); + result = SEG_CUBICTO; + break; + default: + throw new NoSuchElementException("cubic iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return result; + } + }; + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new curve with the same contents as this one. + * + * @return the clone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Helper method used by contains() and intersects() methods, that + * returns the number of curve/line intersections on a given axis + * extending from a certain point. + * + * @param x x coordinate of the origin point + * @param y y coordinate of the origin point + * @param useYaxis axis used, if true the positive Y axis is used, + * false uses the positive X axis. + * + * This is an implementation of the line-crossings algorithm, + * Detailed in an article on Eric Haines' page: + * http://www.acm.org/tog/editors/erich/ptinpoly/ + * + * A special-case not adressed in this code is self-intersections + * of the curve, e.g. if the axis intersects the self-itersection, + * the degenerate roots of the polynomial will erroneously count as + * a single intersection of the curve, and not two. + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + int nCrossings = 0; + double a0; + double a1; + double a2; + double a3; + double b0; + double b1; + double b2; + double b3; + double[] r = new double[4]; + int nRoots; + + a0 = a3 = 0.0; + + if (useYaxis) + { + a0 = getY1() - y; + a1 = getCtrlY1() - y; + a2 = getCtrlY2() - y; + a3 = getY2() - y; + b0 = getX1() - x; + b1 = getCtrlX1() - x; + b2 = getCtrlX2() - x; + b3 = getX2() - x; + } + else + { + a0 = getX1() - x; + a1 = getCtrlX1() - x; + a2 = getCtrlX2() - x; + a3 = getX2() - x; + b0 = getY1() - y; + b1 = getCtrlY1() - y; + b2 = getCtrlY2() - y; + b3 = getY2() - y; + } + + /* If the axis intersects a start/endpoint, shift it up by some small + amount to guarantee the line is 'inside' + If this is not done, bad behaviour may result for points on that axis.*/ + if (a0 == 0.0 || a3 == 0.0) + { + double small = getFlatness() * EPSILON; + if (a0 == 0.0) + a0 -= small; + if (a3 == 0.0) + a3 -= small; + } + + if (useYaxis) + { + if (Line2D.linesIntersect(b0, a0, b3, a3, EPSILON, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a3, b3, 0.0, EPSILON, 0.0, distance)) + nCrossings++; + } + + r[0] = a0; + r[1] = 3 * (a1 - a0); + r[2] = 3 * (a2 + a0 - 2 * a1); + r[3] = a3 - 3 * a2 + 3 * a1 - a0; + + if ((nRoots = solveCubic(r)) != 0) + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + double crossing = -(t * t * t) * (b0 - 3 * b1 + 3 * b2 - b3) + + 3 * t * t * (b0 - 2 * b1 + b2) + + 3 * t * (b1 - b0) + b0; + if (crossing > 0.0 && crossing <= distance) + nCrossings++; + } + } + + return (nCrossings); + } + + /** + * A two-dimensional curve that is parameterized with a cubic + * function and stores coordinate values in double-precision + * floating-point format. + * + * @see CubicCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Double extends CubicCurve2D + { + /** + * The <i>x</i> coordinate of the curve’s start point. + */ + public double x1; + + /** + * The <i>y</i> coordinate of the curve’s start point. + */ + public double y1; + + /** + * The <i>x</i> coordinate of the curve’s first control point. + */ + public double ctrlx1; + + /** + * The <i>y</i> coordinate of the curve’s first control point. + */ + public double ctrly1; + + /** + * The <i>x</i> coordinate of the curve’s second control point. + */ + public double ctrlx2; + + /** + * The <i>y</i> coordinate of the curve’s second control point. + */ + public double ctrly2; + + /** + * The <i>x</i> coordinate of the curve’s end point. + */ + public double x2; + + /** + * The <i>y</i> coordinate of the curve’s end point. + */ + public double y2; + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in double-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Double() + { + } + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in double-precision floating-point format, specifying the + * initial position of each point. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s first + * control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s first + * control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s second + * control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s second + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s end + * point. + */ + public Double(double x1, double y1, double cx1, double cy1, double cx2, + double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s first + * control point. + */ + public double getCtrlX1() + { + return ctrlx1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s first + * control point. + */ + public double getCtrlY1() + { + return ctrly1; + } + + /** + * Returns the curve’s first control point. + */ + public Point2D getCtrlP1() + { + return new Point2D.Double(ctrlx1, ctrly1); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s second + * control point. + */ + public double getCtrlX2() + { + return ctrlx2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s second + * control point. + */ + public double getCtrlY2() + { + return ctrly2; + } + + /** + * Returns the curve’s second control point. + */ + public Point2D getCtrlP2() + { + return new Point2D.Double(ctrlx2, ctrly2); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s new start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s new + * first control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s new + * first control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s new + * second control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s new + * second control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new end + * point. + */ + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. As the + * illustration below shows, the invisible control points may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + * <p><img src="doc-files/CubicCurve2D-2.png" width="350" height="180" + * alt="An illustration of the bounds of a CubicCurve2D" /> + */ + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + double ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + double nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + double ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } + + /** + * A two-dimensional curve that is parameterized with a cubic + * function and stores coordinate values in single-precision + * floating-point format. + * + * @see CubicCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Float extends CubicCurve2D + { + /** + * The <i>x</i> coordinate of the curve’s start point. + */ + public float x1; + + /** + * The <i>y</i> coordinate of the curve’s start point. + */ + public float y1; + + /** + * The <i>x</i> coordinate of the curve’s first control point. + */ + public float ctrlx1; + + /** + * The <i>y</i> coordinate of the curve’s first control point. + */ + public float ctrly1; + + /** + * The <i>x</i> coordinate of the curve’s second control point. + */ + public float ctrlx2; + + /** + * The <i>y</i> coordinate of the curve’s second control point. + */ + public float ctrly2; + + /** + * The <i>x</i> coordinate of the curve’s end point. + */ + public float x2; + + /** + * The <i>y</i> coordinate of the curve’s end point. + */ + public float y2; + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in single-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Float() + { + } + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in single-precision floating-point format, specifying the + * initial position of each point. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s first + * control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s first + * control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s second + * control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s second + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s end + * point. + */ + public Float(float x1, float y1, float cx1, float cy1, float cx2, + float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s first + * control point. + */ + public double getCtrlX1() + { + return ctrlx1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s first + * control point. + */ + public double getCtrlY1() + { + return ctrly1; + } + + /** + * Returns the curve’s first control point. + */ + public Point2D getCtrlP1() + { + return new Point2D.Float(ctrlx1, ctrly1); + } + + /** + * Returns the <i>s</i> coordinate of the curve’s second + * control point. + */ + public double getCtrlX2() + { + return ctrlx2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s second + * control point. + */ + public double getCtrlY2() + { + return ctrly2; + } + + /** + * Returns the curve’s second control point. + */ + public Point2D getCtrlP2() + { + return new Point2D.Float(ctrlx2, ctrly2); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value as a double-precision floating-point number. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s new start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s new + * first control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s new + * first control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s new + * second control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s new + * second control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new end + * point. + */ + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx1 = (float) cx1; + ctrly1 = (float) cy1; + ctrlx2 = (float) cx2; + ctrly2 = (float) cy2; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value as a single-precision floating-point number. + * + * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" + * alt="A drawing of a CubicCurve2D" /> + * + * @param x1 the <i>x</i> coordinate of the curve’s new start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new start + * point. + * + * @param cx1 the <i>x</i> coordinate of the curve’s new + * first control point. + * + * @param cy1 the <i>y</i> coordinate of the curve’s new + * first control point. + * + * @param cx2 the <i>x</i> coordinate of the curve’s new + * second control point. + * + * @param cy2 the <i>y</i> coordinate of the curve’s new + * second control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new end + * point. + */ + public void setCurve(float x1, float y1, float cx1, float cy1, float cx2, + float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. As the + * illustration below shows, the invisible control points may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + * <p><img src="doc-files/CubicCurve2D-2.png" width="350" height="180" + * alt="An illustration of the bounds of a CubicCurve2D" /> + */ + public Rectangle2D getBounds2D() + { + float nx1 = (float) Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + float ny1 = (float) Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + float nx2 = (float) Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + float ny2 = (float) Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } +} diff --git a/libjava/classpath/java/awt/geom/Dimension2D.java b/libjava/classpath/java/awt/geom/Dimension2D.java new file mode 100644 index 00000000000..6b5ce8830a9 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Dimension2D.java @@ -0,0 +1,118 @@ +/* Dimension2D.java -- abstraction of a dimension + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +/** + * This stores a dimension in 2-dimensional space - a width (along the x-axis) + * and height (along the y-axis). The storage is left to subclasses. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Dimension2D implements Cloneable +{ + /** + * The default constructor. + */ + protected Dimension2D() + { + } + + /** + * Get the width of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the width + */ + public abstract double getWidth(); + + /** + * Get the height of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the height + */ + public abstract double getHeight(); + + /** + * Set the size of this dimension to the requested values. Loss of precision + * may occur. + * + * @param w the new width + * @param h the new height + */ + public abstract void setSize(double w, double h); + + /** + * Set the size of this dimension to the requested value. Loss of precision + * may occur. + * + * @param d the dimension containing the new values + * + * @throws NullPointerException if d is null + */ + public void setSize(Dimension2D d) + { + setSize(d.getWidth(), d.getHeight()); + } + + /** + * Create a new dimension of the same run-time type with the same contents + * as this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class Dimension2D diff --git a/libjava/classpath/java/awt/geom/Ellipse2D.java b/libjava/classpath/java/awt/geom/Ellipse2D.java new file mode 100644 index 00000000000..e8830770f60 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Ellipse2D.java @@ -0,0 +1,413 @@ +/* Ellipse2D.java -- represents an ellipse in 2-D space + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + + +/** + * Ellipse2D is the shape of an ellipse. + * <BR> + * <img src="doc-files/Ellipse-1.png" width="347" height="221" + * alt="A drawing of an ellipse" /><BR> + * The ellipse is defined by it's bounding box (shown in red), + * and is defined by the implicit curve:<BR> + * <blockquote>(<i>x</i>/<i>a</i>)<sup>2</sup> + + * (<i>y</i>/<i>b</i>)<sup>2</sup> = 1<BR><BR></blockquote> + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.2 + */ +public abstract class Ellipse2D extends RectangularShape +{ + /** + * Ellipse2D is defined as abstract. + * Implementing classes are Ellipse2D.Float and Ellipse2D.Double. + */ + protected Ellipse2D() + { + } + + /** + * Determines if a point is contained within the ellipse. <P> + * @param x - x coordinate of the point. + * @param y - y coordinate of the point. + * @return true if the point is within the ellipse, false otherwise. + */ + public boolean contains(double x, double y) + { + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double tx = (x - (getX() + rx)) / rx; + double ty = (y - (getY() + ry)) / ry; + return tx * tx + ty * ty < 1.0; + } + + /** + * Determines if a rectangle is completely contained within the + * ellipse. <P> + * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle is completely contained, false otherwise. + */ + public boolean contains(double x, double y, double w, double h) + { + double x2 = x + w; + double y2 = y + h; + return (contains(x, y) && contains(x, y2) && contains(x2, y) + && contains(x2, y2)); + } + + /** + * Returns a PathIterator object corresponding to the ellipse.<P> + * + * Note: An ellipse cannot be represented exactly in PathIterator + * segments, the outline is thefore approximated with cubic + * Bezier segments. + * + * @param at an optional transform. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at) + { + // An ellipse is just a complete arc. + return new Arc2D.ArcIterator(this, at); + } + + /** + * Determines if a rectangle intersects any part of the ellipse.<P> + * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle intersects the ellipse, false otherwise. + */ + public boolean intersects(double x, double y, double w, double h) + { + Rectangle2D r = new Rectangle2D.Double(x, y, w, h); + if (! r.intersects(getX(), getY(), getWidth(), getHeight())) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Line2D l1 = new Line2D.Double(getX(), getY() + (getHeight() / 2), + getX() + getWidth(), + getY() + (getHeight() / 2)); + Line2D l2 = new Line2D.Double(getX() + (getWidth() / 2), getY(), + getX() + (getWidth() / 2), + getY() + getHeight()); + + if (l1.intersects(r) || l2.intersects(r)) + return true; + + return false; + } + + /** + * An {@link Ellipse2D} that stores its coordinates using <code>double</code> + * primitives. + */ + public static class Double extends Ellipse2D + { + /** + * The height of the ellipse. + */ + public double height; + + /** + * The width of the ellipse. + */ + public double width; + + /** + * The upper-left x coordinate of the bounding-box + */ + public double x; + + /** + * The upper-left y coordinate of the bounding-box + */ + public double y; + + /** + * Creates a new Ellipse2D with an upper-left coordinate of (0,0) + * and a zero size. + */ + public Double() + { + } + + /** + * Creates a new Ellipse2D within a given rectangle + * using double-precision coordinates.<P> + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public Double(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + + /** + * Returns the bounding-box of the ellipse. + * @return The bounding box. + */ + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Double(x, y, width, height); + } + + /** + * Returns the height of the ellipse. + * @return The height of the ellipse. + */ + public double getHeight() + { + return height; + } + + /** + * Returns the width of the ellipse. + * @return The width of the ellipse. + */ + public double getWidth() + { + return width; + } + + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The x coordinate. + */ + public double getX() + { + return x; + } + + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The y coordinate. + */ + public double getY() + { + return y; + } + + /** + * Returns <code>true</code> if the ellipse encloses no area, and + * <code>false</code> otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return height <= 0 || width <= 0; + } + + /** + * Sets the geometry of the ellipse's bounding box.<P> + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + } // class Double + + /** + * An {@link Ellipse2D} that stores its coordinates using <code>float</code> + * primitives. + */ + public static class Float extends Ellipse2D + { + /** + * The height of the ellipse. + */ + public float height; + + /** + * The width of the ellipse. + */ + public float width; + + /** + * The upper-left x coordinate of the bounding-box + */ + public float x; + + /** + * The upper-left y coordinate of the bounding-box + */ + public float y; + + /** + * Creates a new Ellipse2D with an upper-left coordinate of (0,0) + * and a zero size. + */ + public Float() + { + } + + /** + * Creates a new Ellipse2D within a given rectangle + * using floating-point precision.<P> + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + * + */ + public Float(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + this.height = h; + this.width = w; + } + + /** + * Returns the bounding-box of the ellipse. + * @return The bounding box. + */ + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Float(x, y, width, height); + } + + /** + * Returns the height of the ellipse. + * @return The height of the ellipse. + */ + public double getHeight() + { + return height; + } + + /** + * Returns the width of the ellipse. + * @return The width of the ellipse. + */ + public double getWidth() + { + return width; + } + + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The x coordinate. + */ + public double getX() + { + return x; + } + + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The y coordinate. + */ + public double getY() + { + return y; + } + + /** + * Returns <code>true</code> if the ellipse encloses no area, and + * <code>false</code> otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return height <= 0 || width <= 0; + } + + /** + * Sets the geometry of the ellipse's bounding box.<P> + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + + /** + * Sets the geometry of the ellipse's bounding box. + * + * Note: This leads to a loss of precision.<P> + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + height = (float) h; + width = (float) w; + } + } // class Float +} // class Ellipse2D diff --git a/libjava/classpath/java/awt/geom/FlatteningPathIterator.java b/libjava/classpath/java/awt/geom/FlatteningPathIterator.java new file mode 100644 index 00000000000..b06e6cc47b8 --- /dev/null +++ b/libjava/classpath/java/awt/geom/FlatteningPathIterator.java @@ -0,0 +1,579 @@ +/* FlatteningPathIterator.java -- Approximates curves by straight lines + Copyright (C) 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** + * A PathIterator for approximating curved path segments by sequences + * of straight lines. Instances of this class will only return + * segments of type {@link PathIterator#SEG_MOVETO}, {@link + * PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}. + * + * <p>The accuracy of the approximation is determined by two + * parameters: + * + * <ul><li>The <i>flatness</i> is a threshold value for deciding when + * a curved segment is consided flat enough for being approximated by + * a single straight line. Flatness is defined as the maximal distance + * of a curve control point to the straight line that connects the + * curve start and end. A lower flatness threshold means a closer + * approximation. See {@link QuadCurve2D#getFlatness()} and {@link + * CubicCurve2D#getFlatness()} for drawings which illustrate the + * meaning of flatness.</li> + * + * <li>The <i>recursion limit</i> imposes an upper bound for how often + * a curved segment gets subdivided. A limit of <i>n</i> means that + * for each individual quadratic and cubic Bézier spline + * segment, at most 2<sup><small><i>n</i></small></sup> {@link + * PathIterator#SEG_LINETO} segments will be created.</li></ul> + * + * <p><b>Memory Efficiency:</b> The memory consumption grows linearly + * with the recursion limit. Neither the <i>flatness</i> parameter nor + * the number of segments in the flattened path will affect the memory + * consumption. + * + * <p><b>Thread Safety:</b> Multiple threads can safely work on + * separate instances of this class. However, multiple threads should + * not concurrently access the same instance, as no synchronization is + * performed. + * + * @see <a href="doc-files/FlatteningPathIterator-1.html" + * >Implementation Note</a> + * + * @author Sascha Brawer (brawer@dandelis.ch) + * + * @since 1.2 + */ +public class FlatteningPathIterator + implements PathIterator +{ + /** + * The PathIterator whose curved segments are being approximated. + */ + private final PathIterator srcIter; + + + /** + * The square of the flatness threshold value, which determines when + * a curve segment is considered flat enough that no further + * subdivision is needed. + * + * <p>Calculating flatness actually produces the squared flatness + * value. To avoid the relatively expensive calculation of a square + * root for each curve segment, we perform all flatness comparisons + * on squared values. + * + * @see QuadCurve2D#getFlatnessSq() + * @see CubicCurve2D#getFlatnessSq() + */ + private final double flatnessSq; + + + /** + * The maximal number of subdivions that are performed to + * approximate a quadratic or cubic curve segment. + */ + private final int recursionLimit; + + + /** + * A stack for holding the coordinates of subdivided segments. + * + * @see <a href="doc-files/FlatteningPathIterator-1.html" + * >Implementation Note</a> + */ + private double[] stack; + + + /** + * The current stack size. + * + * @see <a href="doc-files/FlatteningPathIterator-1.html" + * >Implementation Note</a> + */ + private int stackSize; + + + /** + * The number of recursions that were performed to arrive at + * a segment on the stack. + * + * @see <a href="doc-files/FlatteningPathIterator-1.html" + * >Implementation Note</a> + */ + private int[] recLevel; + + + + private final double[] scratch = new double[6]; + + + /** + * The segment type of the last segment that was returned by + * the source iterator. + */ + private int srcSegType; + + + /** + * The current <i>x</i> position of the source iterator. + */ + private double srcPosX; + + + /** + * The current <i>y</i> position of the source iterator. + */ + private double srcPosY; + + + /** + * A flag that indicates when this path iterator has finished its + * iteration over path segments. + */ + private boolean done; + + + /** + * Constructs a new PathIterator for approximating an input + * PathIterator with straight lines. The approximation works by + * recursive subdivisons, until the specified flatness threshold is + * not exceeded. + * + * <p>There will not be more than 10 nested recursion steps, which + * means that a single <code>SEG_QUADTO</code> or + * <code>SEG_CUBICTO</code> segment is approximated by at most + * 2<sup><small>10</small></sup> = 1024 straight lines. + */ + public FlatteningPathIterator(PathIterator src, double flatness) + { + this(src, flatness, 10); + } + + + /** + * Constructs a new PathIterator for approximating an input + * PathIterator with straight lines. The approximation works by + * recursive subdivisons, until the specified flatness threshold is + * not exceeded. Additionally, the number of recursions is also + * bound by the specified recursion limit. + */ + public FlatteningPathIterator(PathIterator src, double flatness, + int limit) + { + if (flatness < 0 || limit < 0) + throw new IllegalArgumentException(); + + srcIter = src; + flatnessSq = flatness * flatness; + recursionLimit = limit; + fetchSegment(); + } + + + /** + * Returns the maximally acceptable flatness. + * + * @see QuadCurve2D#getFlatness() + * @see CubicCurve2D#getFlatness() + */ + public double getFlatness() + { + return Math.sqrt(flatnessSq); + } + + + /** + * Returns the maximum number of recursive curve subdivisions. + */ + public int getRecursionLimit() + { + return recursionLimit; + } + + + // Documentation will be copied from PathIterator. + public int getWindingRule() + { + return srcIter.getWindingRule(); + } + + + // Documentation will be copied from PathIterator. + public boolean isDone() + { + return done; + } + + + // Documentation will be copied from PathIterator. + public void next() + { + if (stackSize > 0) + { + --stackSize; + if (stackSize > 0) + { + switch (srcSegType) + { + case PathIterator.SEG_QUADTO: + subdivideQuadratic(); + return; + + case PathIterator.SEG_CUBICTO: + subdivideCubic(); + return; + + default: + throw new IllegalStateException(); + } + } + } + + srcIter.next(); + fetchSegment(); + } + + + // Documentation will be copied from PathIterator. + public int currentSegment(double[] coords) + { + if (done) + throw new NoSuchElementException(); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return srcSegType; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + coords[0] = srcPosX; + coords[1] = srcPosY; + return srcSegType; + + case PathIterator.SEG_QUADTO: + if (stackSize == 0) + { + coords[0] = srcPosX; + coords[1] = srcPosY; + } + else + { + int sp = stack.length - 4 * stackSize; + coords[0] = stack[sp + 2]; + coords[1] = stack[sp + 3]; + } + return PathIterator.SEG_LINETO; + + case PathIterator.SEG_CUBICTO: + if (stackSize == 0) + { + coords[0] = srcPosX; + coords[1] = srcPosY; + } + else + { + int sp = stack.length - 6 * stackSize; + coords[0] = stack[sp + 4]; + coords[1] = stack[sp + 5]; + } + return PathIterator.SEG_LINETO; + } + + throw new IllegalStateException(); + } + + + // Documentation will be copied from PathIterator. + public int currentSegment(float[] coords) + { + if (done) + throw new NoSuchElementException(); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return srcSegType; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + return srcSegType; + + case PathIterator.SEG_QUADTO: + if (stackSize == 0) + { + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + } + else + { + int sp = stack.length - 4 * stackSize; + coords[0] = (float) stack[sp + 2]; + coords[1] = (float) stack[sp + 3]; + } + return PathIterator.SEG_LINETO; + + case PathIterator.SEG_CUBICTO: + if (stackSize == 0) + { + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + } + else + { + int sp = stack.length - 6 * stackSize; + coords[0] = (float) stack[sp + 4]; + coords[1] = (float) stack[sp + 5]; + } + return PathIterator.SEG_LINETO; + } + + throw new IllegalStateException(); + } + + + /** + * Fetches the next segment from the source iterator. + */ + private void fetchSegment() + { + int sp; + + if (srcIter.isDone()) + { + done = true; + return; + } + + srcSegType = srcIter.currentSegment(scratch); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + srcPosX = scratch[0]; + srcPosY = scratch[1]; + return; + + case PathIterator.SEG_QUADTO: + if (recursionLimit == 0) + { + srcPosX = scratch[2]; + srcPosY = scratch[3]; + stackSize = 0; + return; + } + sp = 4 * recursionLimit; + stackSize = 1; + if (stack == null) + { + stack = new double[sp + /* 4 + 2 */ 6]; + recLevel = new int[recursionLimit + 1]; + } + recLevel[0] = 0; + stack[sp] = srcPosX; // P1.x + stack[sp + 1] = srcPosY; // P1.y + stack[sp + 2] = scratch[0]; // C.x + stack[sp + 3] = scratch[1]; // C.y + srcPosX = stack[sp + 4] = scratch[2]; // P2.x + srcPosY = stack[sp + 5] = scratch[3]; // P2.y + subdivideQuadratic(); + break; + + case PathIterator.SEG_CUBICTO: + if (recursionLimit == 0) + { + srcPosX = scratch[4]; + srcPosY = scratch[5]; + stackSize = 0; + return; + } + sp = 6 * recursionLimit; + stackSize = 1; + if ((stack == null) || (stack.length < sp + 8)) + { + stack = new double[sp + /* 6 + 2 */ 8]; + recLevel = new int[recursionLimit + 1]; + } + recLevel[0] = 0; + stack[sp] = srcPosX; // P1.x + stack[sp + 1] = srcPosY; // P1.y + stack[sp + 2] = scratch[0]; // C1.x + stack[sp + 3] = scratch[1]; // C1.y + stack[sp + 4] = scratch[2]; // C2.x + stack[sp + 5] = scratch[3]; // C2.y + srcPosX = stack[sp + 6] = scratch[4]; // P2.x + srcPosY = stack[sp + 7] = scratch[5]; // P2.y + subdivideCubic(); + return; + } + } + + + /** + * Repeatedly subdivides the quadratic curve segment that is on top + * of the stack. The iteration terminates when the recursion limit + * has been reached, or when the resulting segment is flat enough. + */ + private void subdivideQuadratic() + { + int sp; + int level; + + sp = stack.length - 4 * stackSize - 2; + level = recLevel[stackSize - 1]; + while ((level < recursionLimit) + && (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq)) + { + recLevel[stackSize] = recLevel[stackSize - 1] = ++level; + QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp); + ++stackSize; + sp -= 4; + } + } + + + /** + * Repeatedly subdivides the cubic curve segment that is on top + * of the stack. The iteration terminates when the recursion limit + * has been reached, or when the resulting segment is flat enough. + */ + private void subdivideCubic() + { + int sp; + int level; + + sp = stack.length - 6 * stackSize - 2; + level = recLevel[stackSize - 1]; + while ((level < recursionLimit) + && (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq)) + { + recLevel[stackSize] = recLevel[stackSize - 1] = ++level; + + CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp); + ++stackSize; + sp -= 6; + } + } + + + /* These routines were useful for debugging. Since they would + * just bloat the implementation, they are commented out. + * + * + + private static String segToString(int segType, double[] d, int offset) + { + String s; + + switch (segType) + { + case PathIterator.SEG_CLOSE: + return "SEG_CLOSE"; + + case PathIterator.SEG_MOVETO: + return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")"; + + case PathIterator.SEG_LINETO: + return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")"; + + case PathIterator.SEG_QUADTO: + return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1] + + ") (" + d[offset + 2] + ", " + d[offset + 3] + ")"; + + case PathIterator.SEG_CUBICTO: + return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1] + + ") (" + d[offset + 2] + ", " + d[offset + 3] + + ") (" + d[offset + 4] + ", " + d[offset + 5] + ")"; + } + + throw new IllegalStateException(); + } + + + private void dumpQuadraticStack(String msg) + { + int sp = stack.length - 4 * stackSize - 2; + int i = 0; + System.err.print(" " + msg + ":"); + while (sp < stack.length) + { + System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")"); + if (i < recLevel.length) + System.out.print("/" + recLevel[i++]); + if (sp + 3 < stack.length) + System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]"); + sp += 4; + } + System.err.println(); + } + + + private void dumpCubicStack(String msg) + { + int sp = stack.length - 6 * stackSize - 2; + int i = 0; + System.err.print(" " + msg + ":"); + while (sp < stack.length) + { + System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")"); + if (i < recLevel.length) + System.out.print("/" + recLevel[i++]); + if (sp + 3 < stack.length) + { + System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]"); + System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]"); + } + sp += 6; + } + System.err.println(); + } + + * + * + */ +} diff --git a/libjava/classpath/java/awt/geom/GeneralPath.java b/libjava/classpath/java/awt/geom/GeneralPath.java new file mode 100644 index 00000000000..f54855874dc --- /dev/null +++ b/libjava/classpath/java/awt/geom/GeneralPath.java @@ -0,0 +1,958 @@ +/* GeneralPath.java -- represents a shape built from subpaths + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + + +/** + * A general geometric path, consisting of any number of subpaths + * constructed out of straight lines and cubic or quadratic Bezier + * curves. + * + * <p>The inside of the curve is defined for drawing purposes by a winding + * rule. Either the WIND_EVEN_ODD or WIND_NON_ZERO winding rule can be chosen. + * + * <p><img src="doc-files/GeneralPath-1.png" width="300" height="210" + * alt="A drawing of a GeneralPath" /> + * <p>The EVEN_ODD winding rule defines a point as inside a path if: + * A ray from the point towards infinity in an arbitrary direction + * intersects the path an odd number of times. Points <b>A</b> and + * <b>C</b> in the image are considered to be outside the path. + * (both intersect twice) + * Point <b>B</b> intersects once, and is inside. + * + * <p>The NON_ZERO winding rule defines a point as inside a path if: + * The path intersects the ray in an equal number of opposite directions. + * Point <b>A</b> in the image is outside (one intersection in the + * ’up’ + * direction, one in the ’down’ direction) Point <b>B</b> in + * the image is inside (one intersection ’down’) + * Point <b>C</b> in the image is outside (two intersections + * ’down’) + * + * @see Line2D + * @see CubicCurve2D + * @see QuadCurve2D + * + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public final class GeneralPath implements Shape, Cloneable +{ + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + /** Initial size if not specified. */ + private static final int INIT_SIZE = 10; + + /** A big number, but not so big it can't survive a few float operations */ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + + /** The winding rule. + * This is package-private to avoid an accessor method. + */ + int rule; + + /** + * The path type in points. Note that xpoints[index] and ypoints[index] maps + * to types[index]; the control points of quad and cubic paths map as + * well but are ignored. + * This is package-private to avoid an accessor method. + */ + byte[] types; + + /** + * The list of all points seen. Since you can only append floats, it makes + * sense for these to be float[]. I have no idea why Sun didn't choose to + * allow a general path of double precision points. + * Note: Storing x and y coords seperately makes for a slower transforms, + * But it speeds up and simplifies box-intersection checking a lot. + * These are package-private to avoid accessor methods. + */ + float[] xpoints; + float[] ypoints; + + /** The index of the most recent moveto point, or null. */ + private int subpath = -1; + + /** The next available index into points. + * This is package-private to avoid an accessor method. + */ + int index; + + /** + * Constructs a GeneralPath with the default (NON_ZERO) + * winding rule and initial capacity (20). + */ + public GeneralPath() + { + this(WIND_NON_ZERO, INIT_SIZE); + } + + /** + * Constructs a GeneralPath with a specific winding rule + * and the default initial capacity (20). + * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD) + */ + public GeneralPath(int rule) + { + this(rule, INIT_SIZE); + } + + /** + * Constructs a GeneralPath with a specific winding rule + * and the initial capacity. The initial capacity should be + * the approximate number of path segments to be used. + * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD) + * @param capacity the inital capacity, in path segments + */ + public GeneralPath(int rule, int capacity) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + if (capacity < INIT_SIZE) + capacity = INIT_SIZE; + types = new byte[capacity]; + xpoints = new float[capacity]; + ypoints = new float[capacity]; + } + + /** + * Constructs a GeneralPath from an arbitrary shape object. + * The Shapes PathIterator path and winding rule will be used. + * @param s the shape + */ + public GeneralPath(Shape s) + { + types = new byte[INIT_SIZE]; + xpoints = new float[INIT_SIZE]; + ypoints = new float[INIT_SIZE]; + PathIterator pi = s.getPathIterator(null); + setWindingRule(pi.getWindingRule()); + append(pi, false); + } + + /** + * Adds a new point to a path. + */ + public void moveTo(float x, float y) + { + subpath = index; + ensureSize(index + 1); + types[index] = PathIterator.SEG_MOVETO; + xpoints[index] = x; + ypoints[index++] = y; + } + + /** + * Appends a straight line to the current path. + * @param x x coordinate of the line endpoint. + * @param y y coordinate of the line endpoint. + */ + public void lineTo(float x, float y) + { + ensureSize(index + 1); + types[index] = PathIterator.SEG_LINETO; + xpoints[index] = x; + ypoints[index++] = y; + } + + /** + * Appends a quadratic Bezier curve to the current path. + * @param x1 x coordinate of the control point + * @param y1 y coordinate of the control point + * @param x2 x coordinate of the curve endpoint. + * @param y2 y coordinate of the curve endpoint. + */ + public void quadTo(float x1, float y1, float x2, float y2) + { + ensureSize(index + 2); + types[index] = PathIterator.SEG_QUADTO; + xpoints[index] = x1; + ypoints[index++] = y1; + xpoints[index] = x2; + ypoints[index++] = y2; + } + + /** + * Appends a cubic Bezier curve to the current path. + * @param x1 x coordinate of the first control point + * @param y1 y coordinate of the first control point + * @param x2 x coordinate of the second control point + * @param y2 y coordinate of the second control point + * @param x3 x coordinate of the curve endpoint. + * @param y3 y coordinate of the curve endpoint. + */ + public void curveTo(float x1, float y1, float x2, float y2, float x3, + float y3) + { + ensureSize(index + 3); + types[index] = PathIterator.SEG_CUBICTO; + xpoints[index] = x1; + ypoints[index++] = y1; + xpoints[index] = x2; + ypoints[index++] = y2; + xpoints[index] = x3; + ypoints[index++] = y3; + } + + /** + * Closes the current subpath by drawing a line + * back to the point of the last moveTo. + */ + public void closePath() + { + ensureSize(index + 1); + types[index] = PathIterator.SEG_CLOSE; + xpoints[index] = xpoints[subpath]; + ypoints[index++] = ypoints[subpath]; + } + + /** + * Appends the segments of a Shape to the path. If <code>connect</code> is + * true, the new path segments are connected to the existing one with a line. + * The winding rule of the Shape is ignored. + */ + public void append(Shape s, boolean connect) + { + append(s.getPathIterator(null), connect); + } + + /** + * Appends the segments of a PathIterator to this GeneralPath. + * Optionally, the initial {@link PathIterator#SEG_MOVETO} segment + * of the appended path is changed into a {@link + * PathIterator#SEG_LINETO} segment. + * + * @param iter the PathIterator specifying which segments shall be + * appended. + * + * @param connect <code>true</code> for substituting the initial + * {@link PathIterator#SEG_MOVETO} segment by a {@link + * PathIterator#SEG_LINETO}, or <code>false</code> for not + * performing any substitution. If this GeneralPath is currently + * empty, <code>connect</code> is assumed to be <code>false</code>, + * thus leaving the initial {@link PathIterator#SEG_MOVETO} + * unchanged. + */ + public void append(PathIterator iter, boolean connect) + { + // A bad implementation of this method had caused Classpath bug #6076. + float[] f = new float[6]; + while (! iter.isDone()) + { + switch (iter.currentSegment(f)) + { + case PathIterator.SEG_MOVETO: + if (! connect || (index == 0)) + { + moveTo(f[0], f[1]); + break; + } + if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE) + && (f[0] == xpoints[index - 1]) + && (f[1] == ypoints[index - 1])) + break; + + // Fall through. + case PathIterator.SEG_LINETO: + lineTo(f[0], f[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(f[0], f[1], f[2], f[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(f[0], f[1], f[2], f[3], f[4], f[5]); + break; + case PathIterator.SEG_CLOSE: + closePath(); + break; + } + + connect = false; + iter.next(); + } + } + + /** + * Returns the path’s current winding rule. + */ + public int getWindingRule() + { + return rule; + } + + /** + * Sets the path’s winding rule, which controls which areas are + * considered ’inside’ or ’outside’ the path + * on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule, + * or WIND_NON_ZERO for a non-zero winding rule. + */ + public void setWindingRule(int rule) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + } + + /** + * Returns the current appending point of the path. + */ + public Point2D getCurrentPoint() + { + if (subpath < 0) + return null; + return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]); + } + + /** + * Resets the path. All points and segments are destroyed. + */ + public void reset() + { + subpath = -1; + index = 0; + } + + /** + * Applies a transform to the path. + */ + public void transform(AffineTransform xform) + { + double nx; + double ny; + double[] m = new double[6]; + xform.getMatrix(m); + for (int i = 0; i < index; i++) + { + nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4]; + ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5]; + xpoints[i] = (float) nx; + ypoints[i] = (float) ny; + } + } + + /** + * Creates a transformed version of the path. + * @param xform the transform to apply + * @return a new transformed GeneralPath + */ + public Shape createTransformedShape(AffineTransform xform) + { + GeneralPath p = new GeneralPath(this); + p.transform(xform); + return p; + } + + /** + * Returns the path’s bounding box. + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Returns the path’s bounding box, in <code>float</code> precision + */ + public Rectangle2D getBounds2D() + { + float x1; + float y1; + float x2; + float y2; + + if (index > 0) + { + x1 = x2 = xpoints[0]; + y1 = y2 = ypoints[0]; + } + else + x1 = x2 = y1 = y2 = 0.0f; + + for (int i = 0; i < index; i++) + { + x1 = Math.min(xpoints[i], x1); + y1 = Math.min(ypoints[i], y1); + x2 = Math.max(xpoints[i], x2); + y2 = Math.max(ypoints[i], y2); + } + return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1)); + } + + /** + * Evaluates if a point is within the GeneralPath, + * The NON_ZERO winding rule is used, regardless of the + * set winding rule. + * @param x x coordinate of the point to evaluate + * @param y y coordinate of the point to evaluate + * @return true if the point is within the path, false otherwise + */ + public boolean contains(double x, double y) + { + return (getWindingNumber(x, y) != 0); + } + + /** + * Evaluates if a Point2D is within the GeneralPath, + * The NON_ZERO winding rule is used, regardless of the + * set winding rule. + * @param p The Point2D to evaluate + * @return true if the point is within the path, false otherwise + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Evaluates if a rectangle is completely contained within the path. + * This method will return false in the cases when the box + * intersects an inner segment of the path. + * (i.e.: The method is accurate for the EVEN_ODD winding rule) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, false, w) != 0 /* top */ + || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, true, h) != 0 /* right */ + || getAxisIntersections(x, y, true, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if (getWindingNumber(x, y) != 0) + return true; + + return false; + } + + /** + * Evaluates if a rectangle is completely contained within the path. + * This method will return false in the cases when the box + * intersects an inner segment of the path. + * (i.e.: The method is accurate for the EVEN_ODD winding rule) + * @param r the rectangle + * @return <code>true</code> if the rectangle is completely contained + * within the path, <code>false</code> otherwise + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Evaluates if a rectangle intersects the path. + * @param x x coordinate of the rectangle + * @param y y coordinate of the rectangle + * @param w width of the rectangle + * @param h height of the rectangle + * @return <code>true</code> if the rectangle intersects the path, + * <code>false</code> otherwise + */ + public boolean intersects(double x, double y, double w, double h) + { + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, false, w) != 0 /* top */ + || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, true, h) != 0 /* right */ + || getAxisIntersections(x, y, true, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if (getWindingNumber(x, y) != 0) + return true; + + return false; + } + + /** + * Evaluates if a Rectangle2D intersects the path. + * @param r The rectangle + * @return <code>true</code> if the rectangle intersects the path, + * <code>false</code> otherwise + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * A PathIterator that iterates over the segments of a GeneralPath. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static class GeneralPathIterator implements PathIterator + { + /** + * The number of coordinate values for each segment type. + */ + private static final int[] NUM_COORDS = { + /* 0: SEG_MOVETO */ 1, + /* 1: SEG_LINETO */ 1, + /* 2: SEG_QUADTO */ 2, + /* 3: SEG_CUBICTO */ 3, + /* 4: SEG_CLOSE */ 0}; + + /** + * The GeneralPath whose segments are being iterated. + * This is package-private to avoid an accessor method. + */ + final GeneralPath path; + + /** + * The affine transformation used to transform coordinates. + */ + private final AffineTransform transform; + + /** + * The current position of the iterator. + */ + private int pos; + + /** + * Constructs a new iterator for enumerating the segments of a + * GeneralPath. + * + * @param at an affine transformation for projecting the returned + * points, or <code>null</code> to return the original points + * without any mapping. + */ + GeneralPathIterator(GeneralPath path, AffineTransform transform) + { + this.path = path; + this.transform = transform; + } + + /** + * Returns the current winding rule of the GeneralPath. + */ + public int getWindingRule() + { + return path.rule; + } + + /** + * Determines whether the iterator has reached the last segment in + * the path. + */ + public boolean isDone() + { + return pos >= path.index; + } + + /** + * Advances the iterator position by one segment. + */ + public void next() + { + int seg; + + /* + * Increment pos by the number of coordinate pairs. + */ + seg = path.types[pos]; + if (seg == SEG_CLOSE) + pos++; + else + pos += NUM_COORDS[seg]; + } + + /** + * Returns the current segment in float coordinates. + */ + public int currentSegment(float[] coords) + { + int seg; + int numCoords; + + seg = path.types[pos]; + numCoords = NUM_COORDS[seg]; + if (numCoords > 0) + { + for (int i = 0; i < numCoords; i++) + { + coords[i << 1] = path.xpoints[pos + i]; + coords[(i << 1) + 1] = path.ypoints[pos + i]; + } + + if (transform != null) + transform.transform( /* src */ + coords, /* srcOffset */ + 0, /* dest */ coords, /* destOffset */ + 0, /* numPoints */ numCoords); + } + return seg; + } + + /** + * Returns the current segment in double coordinates. + */ + public int currentSegment(double[] coords) + { + int seg; + int numCoords; + + seg = path.types[pos]; + numCoords = NUM_COORDS[seg]; + if (numCoords > 0) + { + for (int i = 0; i < numCoords; i++) + { + coords[i << 1] = (double) path.xpoints[pos + i]; + coords[(i << 1) + 1] = (double) path.ypoints[pos + i]; + } + if (transform != null) + transform.transform( /* src */ + coords, /* srcOffset */ + 0, /* dest */ coords, /* destOffset */ + 0, /* numPoints */ numCoords); + } + return seg; + } + } + + /** + * Creates a PathIterator for iterating along the segments of the path. + * + * @param at an affine transformation for projecting the returned + * points, or <code>null</code> to let the created iterator return + * the original points without any mapping. + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new GeneralPathIterator(this, at); + } + + /** + * Creates a new FlatteningPathIterator for the path + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Creates a new shape of the same run-time type with the same contents + * as this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + // This class is final; no need to use super.clone(). + return new GeneralPath(this); + } + + /** + * Helper method - ensure the size of the data arrays, + * otherwise, reallocate new ones twice the size + */ + private void ensureSize(int size) + { + if (subpath < 0) + throw new IllegalPathStateException("need initial moveto"); + if (size <= xpoints.length) + return; + byte[] b = new byte[types.length << 1]; + System.arraycopy(types, 0, b, 0, index); + types = b; + float[] f = new float[xpoints.length << 1]; + System.arraycopy(xpoints, 0, f, 0, index); + xpoints = f; + f = new float[ypoints.length << 1]; + System.arraycopy(ypoints, 0, f, 0, index); + ypoints = f; + } + + /** + * Helper method - Get the total number of intersections from (x,y) along + * a given axis, within a given distance. + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + return (evaluateCrossings(x, y, false, useYaxis, distance)); + } + + /** + * Helper method - returns the winding number of a point. + */ + private int getWindingNumber(double x, double y) + { + /* Evaluate the crossings from x,y to infinity on the y axis (arbitrary + choice). Note that we don't actually use Double.INFINITY, since that's + slower, and may cause problems. */ + return (evaluateCrossings(x, y, true, true, BIG_VALUE)); + } + + /** + * Helper method - evaluates the number of intersections on an axis from + * the point (x,y) to the point (x,y+distance) or (x+distance,y). + * @param x x coordinate. + * @param y y coordinate. + * @param neg True if opposite-directed intersections should cancel, + * false to sum all intersections. + * @param useYaxis Use the Y axis, false uses the X axis. + * @param distance Interval from (x,y) on the selected axis to find + * intersections. + */ + private int evaluateCrossings(double x, double y, boolean neg, + boolean useYaxis, double distance) + { + float cx = 0.0f; + float cy = 0.0f; + float firstx = 0.0f; + float firsty = 0.0f; + + int negative = (neg) ? -1 : 1; + double x0; + double x1; + double x2; + double x3; + double y0; + double y1; + double y2; + double y3; + double[] r = new double[4]; + int nRoots; + double epsilon = 0.0; + int pos = 0; + int windingNumber = 0; + boolean pathStarted = false; + + if (index == 0) + return (0); + if (useYaxis) + { + float[] swap1; + swap1 = ypoints; + ypoints = xpoints; + xpoints = swap1; + double swap2; + swap2 = y; + y = x; + x = swap2; + } + + /* Get a value which is hopefully small but not insignificant relative + the path. */ + epsilon = ypoints[0] * 1E-7; + + if(epsilon == 0) + epsilon = 1E-7; + + pos = 0; + while (pos < index) + { + switch (types[pos]) + { + case PathIterator.SEG_MOVETO: + if (pathStarted) // close old path + { + x0 = cx; + y0 = cy; + x1 = firstx; + y1 = firsty; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = firstx; + cy = firsty; + } + cx = firstx = xpoints[pos] - (float) x; + cy = firsty = ypoints[pos++] - (float) y; + pathStarted = true; + break; + case PathIterator.SEG_CLOSE: + x0 = cx; + y0 = cy; + x1 = firstx; + y1 = firsty; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = firstx; + cy = firsty; + pos++; + pathStarted = false; + break; + case PathIterator.SEG_LINETO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - (float) x; + y1 = ypoints[pos++] - (float) y; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + case PathIterator.SEG_QUADTO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - x; + y1 = ypoints[pos++] - y; + x2 = xpoints[pos] - x; + y2 = ypoints[pos++] - y; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0)) + { + if (y0 == 0.0) + y0 -= epsilon; + if (y2 == 0.0) + y2 -= epsilon; + + r[0] = y0; + r[1] = 2 * (y1 - y0); + r[2] = (y2 - 2 * y1 + y0); + + /* degenerate roots (=tangent points) do not + contribute to the winding number. */ + if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2) + for (int i = 0; i < nRoots; i++) + { + float t = (float) r[i]; + if (t > 0.0f && t < 1.0f) + { + double crossing = t * t * (x2 - 2 * x1 + x0) + + 2 * t * (x1 - x0) + x0; + if (crossing >= 0.0 && crossing <= distance) + windingNumber += (2 * t * (y2 - 2 * y1 + y0) + + 2 * (y1 - y0) < 0) ? 1 : negative; + } + } + } + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + case PathIterator.SEG_CUBICTO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - x; + y1 = ypoints[pos++] - y; + x2 = xpoints[pos] - x; + y2 = ypoints[pos++] - y; + x3 = xpoints[pos] - x; + y3 = ypoints[pos++] - y; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0)) + { + if (y0 == 0.0) + y0 -= epsilon; + if (y3 == 0.0) + y3 -= epsilon; + + r[0] = y0; + r[1] = 3 * (y1 - y0); + r[2] = 3 * (y2 + y0 - 2 * y1); + r[3] = y3 - 3 * y2 + 3 * y1 - y0; + + if ((nRoots = CubicCurve2D.solveCubic(r)) != 0) + for (int i = 0; i < nRoots; i++) + { + float t = (float) r[i]; + if (t > 0.0 && t < 1.0) + { + double crossing = -(t * t * t) * (x0 - 3 * x1 + + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + + 3 * t * (x1 - x0) + x0; + if (crossing >= 0 && crossing <= distance) + windingNumber += (3 * t * t * (y3 + 3 * y1 + - 3 * y2 - y0) + + 6 * t * (y0 - 2 * y1 + y2) + + 3 * (y1 - y0) < 0) ? 1 : negative; + } + } + } + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + } + } + + // swap coordinates back + if (useYaxis) + { + float[] swap; + swap = ypoints; + ypoints = xpoints; + xpoints = swap; + } + return (windingNumber); + } +} // class GeneralPath + diff --git a/libjava/classpath/java/awt/geom/IllegalPathStateException.java b/libjava/classpath/java/awt/geom/IllegalPathStateException.java new file mode 100644 index 00000000000..4d190c74814 --- /dev/null +++ b/libjava/classpath/java/awt/geom/IllegalPathStateException.java @@ -0,0 +1,71 @@ +/* IllegalPathStateException.java -- an operation was in an illegal path state + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * Thrown when an operation on a path is in an illegal state, such as appending + * a segment to a <code>GeneralPath</code> without an initial moveto. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see GeneralPath + * @status updated to 1.4 + */ +public class IllegalPathStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -5158084205220481094L; + + /** + * Create an exception with no message. + */ + public IllegalPathStateException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public IllegalPathStateException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/awt/geom/Line2D.java b/libjava/classpath/java/awt/geom/Line2D.java new file mode 100644 index 00000000000..e15e7cfe3f5 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Line2D.java @@ -0,0 +1,1182 @@ +/* Line2D.java -- represents a line in 2-D space, plus operations on a line + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * Represents a directed line bewteen two points in (x,y) Cartesian space. + * Remember, on-screen graphics have increasing x from left-to-right, and + * increasing y from top-to-bottom. The storage is left to subclasses. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @author David Gilbert + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Line2D implements Shape, Cloneable +{ + /** + * The default constructor. + */ + protected Line2D() + { + } + + /** + * Return the x coordinate of the first point. + * + * @return the starting x coordinate + */ + public abstract double getX1(); + + /** + * Return the y coordinate of the first point. + * + * @return the starting y coordinate + */ + public abstract double getY1(); + + /** + * Return the first point. + * + * @return the starting point + */ + public abstract Point2D getP1(); + + /** + * Return the x coordinate of the second point. + * + * @return the ending x coordinate + */ + public abstract double getX2(); + + /** + * Return the y coordinate of the second point. + * + * @return the ending y coordinate + */ + public abstract double getY2(); + + /** + * Return the second point. + * + * @return the ending point + */ + public abstract Point2D getP2(); + + /** + * Set the coordinates of the line to the given coordinates. Loss of + * precision may occur due to rounding issues. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public abstract void setLine(double x1, double y1, double x2, double y2); + + /** + * Set the coordinates to the given points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setLine(Point2D p1, Point2D p2) + { + setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the coordinates to those of the given line. + * + * @param l the line to copy + * @throws NullPointerException if l is null + */ + public void setLine(Line2D l) + { + setLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Computes the relative rotation direction needed to pivot the line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + */ + public static int relativeCCW(double x1, double y1, double x2, double y2, + double px, double py) + { + if ((x1 == x2 && y1 == y2) + || (x1 == px && y1 == py)) + return 0; // Coincident points. + // Translate to the origin. + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + double slope2 = y2 / x2; + double slopep = py / px; + if (slope2 == slopep || (x2 == 0 && px == 0)) + return y2 > 0 // Colinear. + ? (py < 0 ? -1 : py > y2 ? 1 : 0) + : (py > 0 ? -1 : py < y2 ? 1 : 0); + if (x2 >= 0 && slope2 >= 0) + return px >= 0 // Quadrant 1. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (y2 > 0) + return px < 0 // Quadrant 2. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (slope2 >= 0.0) + return px >= 0 // Quadrant 3. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); + return px < 0 // Quadrant 4. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); + } + + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(double px, double py) + { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param p the reference point + * @return the relative rotation direction + * @throws NullPointerException if p is null + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(Point2D p) + { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Computes twice the (signed) area of the triangle defined by the three + * points. This method is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return Twice the area. + */ + private static double area2(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); + } + + /** + * Returns <code>true</code> if (x3, y3) lies between (x1, y1) and (x2, y2), + * and false otherwise, This test assumes that the three points are + * collinear, and is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return A boolean. + */ + private static boolean between(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + if (x1 != x2) { + return (x1 <= x3 && x3 <= x2) || (x1 >= x3 && x3 >= x2); + } + else { + return (y1 <= y3 && y3 <= y2) || (y1 >= y3 && y3 >= y2); + } + } + + /** + * Test if the line segment (x1,y1)->(x2,y2) intersects the line segment + * (x3,y3)->(x4,y4). + * + * @param x1 the first x coordinate of the first segment + * @param y1 the first y coordinate of the first segment + * @param x2 the second x coordinate of the first segment + * @param y2 the second y coordinate of the first segment + * @param x3 the first x coordinate of the second segment + * @param y3 the first y coordinate of the second segment + * @param x4 the second x coordinate of the second segment + * @param y4 the second y coordinate of the second segment + * @return true if the segments intersect + */ + public static boolean linesIntersect(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + double a1, a2, a3, a4; + + // deal with special cases + if ((a1 = area2(x1, y1, x2, y2, x3, y3)) == 0.0) + { + // check if p3 is between p1 and p2 OR + // p4 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x1, y1, x2, y2, x3, y3)) + { + return true; + } + else + { + if (area2(x1, y1, x2, y2, x4, y4) == 0.0) + { + return between(x3, y3, x4, y4, x1, y1) + || between (x3, y3, x4, y4, x2, y2); + } + else { + return false; + } + } + } + else if ((a2 = area2(x1, y1, x2, y2, x4, y4)) == 0.0) + { + // check if p4 is between p1 and p2 (we already know p3 is not + // collinear) + return between(x1, y1, x2, y2, x4, y4); + } + + if ((a3 = area2(x3, y3, x4, y4, x1, y1)) == 0.0) { + // check if p1 is between p3 and p4 OR + // p2 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x3, y3, x4, y4, x1, y1)) { + return true; + } + else { + if (area2(x3, y3, x4, y4, x2, y2) == 0.0) { + return between(x1, y1, x2, y2, x3, y3) + || between (x1, y1, x2, y2, x4, y4); + } + else { + return false; + } + } + } + else if ((a4 = area2(x3, y3, x4, y4, x2, y2)) == 0.0) { + // check if p2 is between p3 and p4 (we already know p1 is not + // collinear) + return between(x3, y3, x4, y4, x2, y2); + } + else { // test for regular intersection + return ((a1 > 0.0) ^ (a2 > 0.0)) && ((a3 > 0.0) ^ (a4 > 0.0)); + } + } + + /** + * Test if this line intersects the line given by (x1,y1)->(x2,y2). + * + * @param x1 the first x coordinate of the other segment + * @param y1 the first y coordinate of the other segment + * @param x2 the second x coordinate of the other segment + * @param y2 the second y coordinate of the other segment + * @return true if the segments intersect + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) + { + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + x1, y1, x2, y2); + } + + /** + * Test if this line intersects the given line. + * + * @param l the other segment + * @return true if the segments intersect + * @throws NullPointerException if l is null + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(Line2D l) + { + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the line segment. If the point is on the segment, the + * result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public static double ptSegDistSq(double x1, double y1, double x2, double y2, + double px, double py) + { + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + + if (u < 0) + { + // "Off the end" + x = x1; + y = y1; + } + else if (u > 1.0) + { + x = x2; + y = y2; + } + else + { + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + } + + return (x - px) * (x - px) + (y - py) * (y - py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the line segment. If the point is on the segment, the result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + * @see #ptLineDist(double, double, double, double, double, double) + */ + public static double ptSegDist(double x1, double y1, double x2, double y2, + double px, double py) + { + return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(double px, double py) + { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param p the point + * @return the square of the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(Point2D p) + { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(double px, double py) + { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param p the point + * @return the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(Point2D p) + { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from the segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public static double ptLineDistSq(double x1, double y1, double x2, double y2, + double px, double py) + { + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + + return (x - px) * (x - px) + (y - py) * (y - py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from the segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + * @see #ptSegDist(double, double, double, double, double, double) + */ + public static double ptLineDist(double x1, double y1, + double x2, double y2, + double px, double py) + { + return Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(double px, double py) + { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param p the point + * @return the square of the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(Point2D p) + { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), + p.getX(), p.getY()); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(double px, double py) + { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param p the point + * @return the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(Point2D p) + { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param x the x coordinate + * @param y the y coordinate + * @return false; the line does not contain points + */ + public boolean contains(double x, double y) + { + return false; + } + + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param p the point + * @return false; the line does not contain points + */ + public boolean contains(Point2D p) + { + return false; + } + + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the line intersects the rectangle + */ + public boolean intersects(double x, double y, double w, double h) + { + if (w <= 0 || h <= 0) + return false; + double x1 = getX1(); + double y1 = getY1(); + double x2 = getX2(); + double y2 = getY2(); + + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; + + double x3 = x + w; + double y3 = y + h; + + return (linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || linesIntersect(x1, y1, x2, y2, x3, y, x, y)); + } + + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param r the rectangle + * @return true if the line intersects the rectangle + * @throws NullPointerException if r is null + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return false; the line does not contain points + */ + public boolean contains(double x, double y, double w, double h) + { + return false; + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param r the rectangle + * @return false; the line does not contain points + */ + public boolean contains(Rectangle2D r) + { + return false; + } + + /** + * Gets a bounding box (not necessarily minimal) for this line. + * + * @return the integer bounding box + * @see #getBounds2D() + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Return a path iterator, possibly applying a transform on the result. This + * iterator is not threadsafe. + * + * @param at the transform, or null + * @return a new path iterator + */ + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getX2(); + coords[1] = (float) getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getX2(); + coords[1] = getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; + } + }; + } + + /** + * Return a flat path iterator, possibly applying a transform on the result. + * This iterator is not threadsafe. + * + * @param at the transform, or null + * @param flatness ignored, since lines are already flat + * @return a new path iterator + * @see #getPathIterator(AffineTransform) + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return getPathIterator(at); + } + + /** + * Create a new line of the same run-time type with the same contents as + * this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * This class defines a point in <code>double</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Line2D + { + /** The x coordinate of the first point. */ + public double x1; + + /** The y coordinate of the first point. */ + public double y1; + + /** The x coordinate of the second point. */ + public double x2; + + /** The y coordinate of the second point. */ + public double y2; + + /** + * Construct the line segment (0,0)->(0,0). + */ + public Double() + { + } + + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Double(double x1, double y1, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Double(Point2D p1, Point2D p2) + { + x1 = p1.getX(); + y1 = p1.getY(); + x2 = p2.getX(); + y2 = p2.getY(); + } + + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() + { + return x1; + } + + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() + { + return y1; + } + + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() + { + return x2; + } + + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() + { + return y2; + } + + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + double x = Math.min(x1, x2); + double y = Math.min(y1, y2); + double w = Math.abs(x1 - x2); + double h = Math.abs(y1 - y2); + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class defines a point in <code>float</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Line2D + { + /** The x coordinate of the first point. */ + public float x1; + + /** The y coordinate of the first point. */ + public float y1; + + /** The x coordinate of the second point. */ + public float x2; + + /** The y coordinate of the second point. */ + public float y2; + + /** + * Construct the line segment (0,0)->(0,0). + */ + public Float() + { + } + + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Float(float x1, float y1, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Float(Point2D p1, Point2D p2) + { + x1 = (float) p1.getX(); + y1 = (float) p1.getY(); + x2 = (float) p2.getX(); + y2 = (float) p2.getY(); + } + + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() + { + return x1; + } + + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() + { + return y1; + } + + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() + { + return x2; + } + + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() + { + return y2; + } + + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(float x1, float y1, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + float x = Math.min(x1, x2); + float y = Math.min(y1, y2); + float w = Math.abs(x1 - x2); + float h = Math.abs(y1 - y2); + return new Rectangle2D.Float(x, y, w, h); + } + } // class Float +} // class Line2D diff --git a/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java b/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java new file mode 100644 index 00000000000..7995a52eb61 --- /dev/null +++ b/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java @@ -0,0 +1,65 @@ +/* NoninvertibleTransformException.java -- a transform can't be inverted + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * Thrown if an operation requires an inverse of an + * <code>AffineTransform</code>, but the transform is in a non-invertible + * state. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see AffineTransform + * @status updated to 1.4 + */ +public class NoninvertibleTransformException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6137225240503990466L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoninvertibleTransformException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/awt/geom/PathIterator.java b/libjava/classpath/java/awt/geom/PathIterator.java new file mode 100644 index 00000000000..2cd08b9b48b --- /dev/null +++ b/libjava/classpath/java/awt/geom/PathIterator.java @@ -0,0 +1,189 @@ +/* PathIterator.java -- describes a shape by iterating over its vertices + Copyright (C) 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * This interface provides a directed path over the boundary of a shape. The + * path can contain 1st through 3rd order Bezier curves (lines, and quadratic + * and cubic splines). A shape can have multiple disjoint paths via the + * MOVETO directive, and can close a circular path back to the previos + * MOVETO via the CLOSE directive. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see java.awt.Shape + * @see java.awt.Stroke + * @see FlatteningPathIterator + * @since 1.2 + * @status updated to 1.4 + */ +public interface PathIterator +{ + /** + * The even-odd winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses an odd number of + * segments. + */ + int WIND_EVEN_ODD = 0; + + /** + * The non-zero winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses a different number + * of segments headed clockwise than those headed counterclockwise. + */ + int WIND_NON_ZERO = 1; + + /** + * Starts a new subpath. There is no segment from the previous vertex. + */ + int SEG_MOVETO = 0; + + /** + * The current segment is a line. + */ + int SEG_LINETO = 1; + + /** + * The current segment is a quadratic parametric curve. It is interpolated + * as t varies from 0 to 1 over the current point (CP), first control point + * (P1), and final interpolated control point (P2): + * <pre> + * P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2 + * 0 <= t <= 1 + * B(n,m) = mth coefficient of nth degree Bernstein polynomial + * = C(n,m) * t^(m) * (1 - t)^(n-m) + * C(n,m) = Combinations of n things, taken m at a time + * = n! / (m! * (n-m)!) + * </pre> + */ + int SEG_QUADTO = 2; + + /** + * The current segment is a cubic parametric curve (more commonly known as + * a Bezier curve). It is interpolated as t varies from 0 to 1 over the + * current point (CP), first control point (P1), the second control point + * (P2), and final interpolated control point (P3): + * <pre> + * P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3 + * 0 <= t <= 1 + * B(n,m) = mth coefficient of nth degree Bernstein polynomial + * = C(n,m) * t^(m) * (1 - t)^(n-m) + * C(n,m) = Combinations of n things, taken m at a time + * = n! / (m! * (n-m)!) + * </pre> + */ + int SEG_CUBICTO = 3; + + /** + * The current segment closes a loop by an implicit line to the previous + * SEG_MOVETO coordinate. + */ + int SEG_CLOSE = 4; + + /** + * Returns the winding rule to determine which points are inside this path. + * + * @return the winding rule + * @see #WIND_EVEN_ODD + * @see #WIND_NON_ZERO + */ + int getWindingRule(); + + /** + * Tests if the iterator is exhausted. If this returns true, currentSegment + * and next may throw a NoSuchElementException (although this is not + * required). + * + * @return true if the iteration is complete + */ + boolean isDone(); + + /** + * Advance to the next segment in the iteration. It is not specified what + * this does if called when isDone() returns true. + * + * @throws java.util.NoSuchElementException optional when isDone() is true + */ + void next(); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a float[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a float[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(float[] coords); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a double[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a double[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(double[] coords); +} // interface PathIterator diff --git a/libjava/classpath/java/awt/geom/Point2D.java b/libjava/classpath/java/awt/geom/Point2D.java new file mode 100644 index 00000000000..9f22a5a67c9 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Point2D.java @@ -0,0 +1,396 @@ +/* Point2D.java -- generic point in 2-D space + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +/** + * This class implements a generic point in 2D Cartesian space. The storage + * representation is left up to the subclass. Point includes two useful + * nested classes, for float and double storage respectively. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Point2D implements Cloneable +{ + /** + * The default constructor. + * + * @see java.awt.Point + * @see Point2D.Float + * @see Point2D.Double + */ + protected Point2D() + { + } + + /** + * Get the X coordinate, in double precision. + * + * @return the x coordinate + */ + public abstract double getX(); + + /** + * Get the Y coordinate, in double precision. + * + * @return the y coordinate + */ + public abstract double getY(); + + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public abstract void setLocation(double x, double y); + + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param p the point to copy + * @throws NullPointerException if p is null + */ + public void setLocation(Point2D p) + { + setLocation(p.getX(), p.getY()); + } + + /** + * Return the square of the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return (x2 - x1)^2 + (y2 - y1)^2 + */ + public static double distanceSq(double x1, double y1, double x2, double y2) + { + x2 -= x1; + y2 -= y1; + return x2 * x2 + y2 * y2; + } + + /** + * Return the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return the distance from (x1,y1) to (x2,y2) + */ + public static double distance(double x1, double y1, double x2, double y2) + { + return Math.sqrt(distanceSq(x1, y1, x2, y2)); + } + + /** + * Return the square of the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the square of the distance + */ + public double distanceSq(double x, double y) + { + return distanceSq(getX(), x, getY(), y); + } + + /** + * Return the square of the distance from this point to the given one. + * + * @param p the other point + * @return the square of the distance + * @throws NullPointerException if p is null + */ + public double distanceSq(Point2D p) + { + return distanceSq(getX(), p.getX(), getY(), p.getY()); + } + + /** + * Return the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the distance + */ + public double distance(double x, double y) + { + return distance(getX(), x, getY(), y); + } + + /** + * Return the distance from this point to the given one. + * + * @param p the other point + * @return the distance + * @throws NullPointerException if p is null + */ + public double distance(Point2D p) + { + return distance(getX(), p.getX(), getY(), p.getY()); + } + + /** + * Create a new point of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Return the hashcode for this point. The formula is not documented, but + * appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getY()); + * l = l * 31 ^ Double.doubleToLongBits(getX()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() + { + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getY()); + l = l * 31 ^ java.lang.Double.doubleToLongBits(getX()); + return (int) ((l >> 32) ^ l); + } + + /** + * Compares two points for equality. This returns true if they have the + * same coordinates. + * + * @param o the point to compare + * @return true if it is equal + */ + public boolean equals(Object o) + { + if (! (o instanceof Point2D)) + return false; + Point2D p = (Point2D) o; + return getX() == p.getX() && getY() == p.getY(); + } + + /** + * This class defines a point in <code>double</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Point2D + { + /** The X coordinate. */ + public double x; + + /** The Y coordinate. */ + public double y; + + /** + * Create a new point at (0,0). + */ + public Double() + { + } + + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Double(double x, double y) + { + this.x = x; + this.y = y; + } + + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() + { + return y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) + { + this.x = x; + this.y = y; + } + + /** + * Returns a string representation of this object. The format is: + * <code>"Point2D.Double[" + x + ", " + y + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return "Point2D.Double[" + x + ", " + y + ']'; + } + } // class Double + + /** + * This class defines a point in <code>float</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Point2D + { + /** The X coordinate. */ + public float x; + + /** The Y coordinate. */ + public float y; + + /** + * Create a new point at (0,0). + */ + public Float() + { + } + + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Float(float x, float y) + { + this.x = x; + this.y = y; + } + + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() + { + return y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) + { + this.x = (float) x; + this.y = (float) y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(float x, float y) + { + this.x = x; + this.y = y; + } + + /** + * Returns a string representation of this object. The format is: + * <code>"Point2D.Float[" + x + ", " + y + ']'</code>. + * + * @return a string representation of this object + */ + public String toString() + { + return "Point2D.Float[" + x + ", " + y + ']'; + } + } // class Float +} // class Point2D diff --git a/libjava/classpath/java/awt/geom/QuadCurve2D.java b/libjava/classpath/java/awt/geom/QuadCurve2D.java new file mode 100644 index 00000000000..41021dbc683 --- /dev/null +++ b/libjava/classpath/java/awt/geom/QuadCurve2D.java @@ -0,0 +1,1467 @@ +/* QuadCurve2D.java -- represents a parameterized quadratic curve in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * A two-dimensional curve that is parameterized with a quadratic + * function. + * + * <p><img src="doc-files/QuadCurve2D-1.png" width="350" height="180" + * alt="A drawing of a QuadCurve2D" /> + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Graydon Hoare (graydon@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public abstract class QuadCurve2D implements Shape, Cloneable +{ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + private static final double EPSILON = 1E-10; + + /** + * Constructs a new QuadCurve2D. Typical users will want to + * construct instances of a subclass, such as {@link + * QuadCurve2D.Float} or {@link QuadCurve2D.Double}. + */ + protected QuadCurve2D() + { + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public abstract double getX1(); + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public abstract double getY1(); + + /** + * Returns the curve’s start point. + */ + public abstract Point2D getP1(); + + /** + * Returns the <i>x</i> coordinate of the curve’s control + * point. + */ + public abstract double getCtrlX(); + + /** + * Returns the <i>y</i> coordinate of the curve’s control + * point. + */ + public abstract double getCtrlY(); + + /** + * Returns the curve’s control point. + */ + public abstract Point2D getCtrlPt(); + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public abstract double getX2(); + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public abstract double getY2(); + + /** + * Returns the curve’s end point. + */ + public abstract Point2D getP2(); + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + * @param x1 the <i>x</i> coordinate of the curve’s new start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new start + * point. + * + * @param cx the <i>x</i> coordinate of the curve’s new + * control point. + * + * @param cy the <i>y</i> coordinate of the curve’s new + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new end + * point. + */ + public abstract void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2); + + /** + * Changes the curve geometry, passing coordinate values in an + * array. + * + * @param coords an array containing the new coordinate values. The + * <i>x</i> coordinate of the new start point is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * new control point is located at <code>coords[offset + 2]</code>, + * its <i>y</i> coordinate at <code>coords[offset + 3]</code>. The + * <i>x</i> coordinate of the new end point is located at + * <code>coords[offset + 4]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 5]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++]); + } + + /** + * Changes the curve geometry, specifying coordinate values in + * separate Point objects. + * + * <p><img src="doc-files/QuadCurve2D-1.png" width="350" height="180" + * alt="A drawing of a QuadCurve2D" /> + * + * <p>The curve does not keep any reference to the passed point + * objects. Therefore, a later change to <code>p1</code>, + * <code>c</code> <code>p2</code> will not affect the curve + * geometry. + * + * @param p1 the new start point. + * @param c the new control point. + * @param p2 the new end point. + */ + public void setCurve(Point2D p1, Point2D c, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c.getX(), c.getY(), p2.getX(), p2.getY()); + } + + /** + * Changes the curve geometry, specifying coordinate values in an + * array of Point objects. + * + * <p><img src="doc-files/QuadCurve2D-1.png" width="350" height="180" + * alt="A drawing of a QuadCurve2D" /> + * + * <p>The curve does not keep references to the passed point + * objects. Therefore, a later change to the <code>pts</code> array + * or any of its elements will not affect the curve geometry. + * + * @param pts an array containing the points. The new start point + * is located at <code>pts[offset]</code>, the new control + * point at <code>pts[offset + 1]</code>, and the new end point + * at <code>pts[offset + 2]</code>. + * + * @param offset the offset of the start point in <code>pts</code>. + */ + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset].getY(), pts[offset + 1].getX(), + pts[offset + 1].getY(), pts[offset + 2].getX(), + pts[offset + 2].getY()); + } + + /** + * Changes the geometry of the curve to that of another curve. + * + * @param c the curve whose coordinates will be copied. + */ + public void setCurve(QuadCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX(), c.getCtrlY(), c.getX2(), + c.getY2()); + } + + /** + * Calculates the squared flatness of a quadratic curve, directly + * specifying each coordinate value. The flatness is the distance of + * the control point to the line between start and end point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. + * the squared length of the red line. + * + * @param x1 the <i>x</i> coordinate of the start point P1. + * @param y1 the <i>y</i> coordinate of the start point P1. + * @param cx the <i>x</i> coordinate of the control point C. + * @param cy the <i>y</i> coordinate of the control point C. + * @param x2 the <i>x</i> coordinate of the end point P2. + * @param y2 the <i>y</i> coordinate of the end point P2. + */ + public static double getFlatnessSq(double x1, double y1, double cx, + double cy, double x2, double y2) + { + return Line2D.ptSegDistSq(x1, y1, x2, y2, cx, cy); + } + + /** + * Calculates the flatness of a quadratic curve, directly specifying + * each coordinate value. The flatness is the distance of the + * control point to the line between start and end point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the distance between C and the gray line, i.e. the length of + * the red line. + * + * @param x1 the <i>x</i> coordinate of the start point P1. + * @param y1 the <i>y</i> coordinate of the start point P1. + * @param cx the <i>x</i> coordinate of the control point C. + * @param cy the <i>y</i> coordinate of the control point C. + * @param x2 the <i>x</i> coordinate of the end point P2. + * @param y2 the <i>y</i> coordinate of the end point P2. + */ + public static double getFlatness(double x1, double y1, double cx, double cy, + double x2, double y2) + { + return Line2D.ptSegDist(x1, y1, x2, y2, cx, cy); + } + + /** + * Calculates the squared flatness of a quadratic curve, specifying + * the coordinate values in an array. The flatness is the distance + * of the control point to the line between start and end point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. + * the squared length of the red line. + * + * @param coords an array containing the coordinate values. The + * <i>x</i> coordinate of the start point P1 is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * control point C is located at <code>coords[offset + 2]</code>, + * its <i>y</i> coordinate at <code>coords[offset + 3]</code>. The + * <i>x</i> coordinate of the end point P2 is located at + * <code>coords[offset + 4]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 5]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public static double getFlatnessSq(double[] coords, int offset) + { + return Line2D.ptSegDistSq(coords[offset], coords[offset + 1], + coords[offset + 4], coords[offset + 5], + coords[offset + 2], coords[offset + 3]); + } + + /** + * Calculates the flatness of a quadratic curve, specifying the + * coordinate values in an array. The flatness is the distance of + * the control point to the line between start and end point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the the distance between C and the gray line, i.e. the length of + * the red line. + * + * @param coords an array containing the coordinate values. The + * <i>x</i> coordinate of the start point P1 is located at + * <code>coords[offset]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the + * control point C is located at <code>coords[offset + 2]</code>, + * its <i>y</i> coordinate at <code>coords[offset + 3]</code>. The + * <i>x</i> coordinate of the end point P2 is located at + * <code>coords[offset + 4]</code>, its <i>y</i> coordinate at + * <code>coords[offset + 5]</code>. + * + * @param offset the offset of the first coordinate value in + * <code>coords</code>. + */ + public static double getFlatness(double[] coords, int offset) + { + return Line2D.ptSegDist(coords[offset], coords[offset + 1], + coords[offset + 4], coords[offset + 5], + coords[offset + 2], coords[offset + 3]); + } + + /** + * Calculates the squared flatness of this curve. The flatness is + * the distance of the control point to the line between start and + * end point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. the + * squared length of the red line. + */ + public double getFlatnessSq() + { + return Line2D.ptSegDistSq(getX1(), getY1(), getX2(), getY2(), getCtrlX(), + getCtrlY()); + } + + /** + * Calculates the flatness of this curve. The flatness is the + * distance of the control point to the line between start and end + * point. + * + * <p><img src="doc-files/QuadCurve2D-4.png" width="350" height="180" + * alt="A drawing that illustrates the flatness" /> + * + * <p>In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the distance between C and the gray line, i.e. the length of the + * red line. + */ + public double getFlatness() + { + return Line2D.ptSegDist(getX1(), getY1(), getX2(), getY2(), getCtrlX(), + getCtrlY()); + } + + /** + * Subdivides this curve into two halves. + * + * <p><img src="doc-files/QuadCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a QuadCurve2D" /> + * + * @param left a curve whose geometry will be set to the left half + * of this curve, or <code>null</code> if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of this curve, or <code>null</code> if the caller is not + * interested in the right half. + */ + public void subdivide(QuadCurve2D left, QuadCurve2D right) + { + // Use empty slots at end to share single array. + double[] d = new double[] + { + getX1(), getY1(), getCtrlX(), getCtrlY(), getX2(), getY2(), + 0, 0, 0, 0 + }; + subdivide(d, 0, d, 0, d, 4); + if (left != null) + left.setCurve(d, 0); + if (right != null) + right.setCurve(d, 4); + } + + /** + * Subdivides a quadratic curve into two halves. + * + * <p><img src="doc-files/QuadCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a QuadCurve2D" /> + * + * @param src the curve to be subdivided. + * + * @param left a curve whose geometry will be set to the left half + * of <code>src</code>, or <code>null</code> if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of <code>src</code>, or <code>null</code> if the caller is not + * interested in the right half. + */ + public static void subdivide(QuadCurve2D src, QuadCurve2D left, + QuadCurve2D right) + { + src.subdivide(left, right); + } + + /** + * Subdivides a quadratic curve into two halves, passing all + * coordinates in an array. + * + * <p><img src="doc-files/QuadCurve2D-3.png" width="700" + * height="180" alt="A drawing that illustrates the effects of + * subdividing a QuadCurve2D" /> + * + * <p>The left end point and the right start point will always be + * identical. Memory-concious programmers thus may want to pass the + * same array for both <code>left</code> and <code>right</code>, and + * set <code>rightOff</code> to <code>leftOff + 4</code>. + * + * @param src an array containing the coordinates of the curve to be + * subdivided. The <i>x</i> coordinate of the start point is + * located at <code>src[srcOff]</code>, its <i>y</i> at + * <code>src[srcOff + 1]</code>. The <i>x</i> coordinate of the + * control point is located at <code>src[srcOff + 2]</code>, its + * <i>y</i> at <code>src[srcOff + 3]</code>. The <i>x</i> + * coordinate of the end point is located at <code>src[srcOff + + * 4]</code>, its <i>y</i> at <code>src[srcOff + 5]</code>. + * + * @param srcOff an offset into <code>src</code>, specifying + * the index of the start point’s <i>x</i> coordinate. + * + * @param left an array that will receive the coordinates of the + * left half of <code>src</code>. It is acceptable to pass + * <code>src</code>. A caller who is not interested in the left half + * can pass <code>null</code>. + * + * @param leftOff an offset into <code>left</code>, specifying the + * index where the start point’s <i>x</i> coordinate will be + * stored. + * + * @param right an array that will receive the coordinates of the + * right half of <code>src</code>. It is acceptable to pass + * <code>src</code> or <code>left</code>. A caller who is not + * interested in the right half can pass <code>null</code>. + * + * @param rightOff an offset into <code>right</code>, specifying the + * index where the start point’s <i>x</i> coordinate will be + * stored. + */ + public static void subdivide(double[] src, int srcOff, double[] left, + int leftOff, double[] right, int rightOff) + { + double x1; + double y1; + double xc; + double yc; + double x2; + double y2; + + x1 = src[srcOff]; + y1 = src[srcOff + 1]; + xc = src[srcOff + 2]; + yc = src[srcOff + 3]; + x2 = src[srcOff + 4]; + y2 = src[srcOff + 5]; + + if (left != null) + { + left[leftOff] = x1; + left[leftOff + 1] = y1; + } + + if (right != null) + { + right[rightOff + 4] = x2; + right[rightOff + 5] = y2; + } + + x1 = (x1 + xc) / 2; + x2 = (xc + x2) / 2; + xc = (x1 + x2) / 2; + y1 = (y1 + yc) / 2; + y2 = (y2 + yc) / 2; + yc = (y1 + y2) / 2; + + if (left != null) + { + left[leftOff + 2] = x1; + left[leftOff + 3] = y1; + left[leftOff + 4] = xc; + left[leftOff + 5] = yc; + } + + if (right != null) + { + right[rightOff] = xc; + right[rightOff + 1] = yc; + right[rightOff + 2] = x2; + right[rightOff + 3] = y2; + } + } + + /** + * Finds the non-complex roots of a quadratic equation, placing the + * results into the same array as the equation coefficients. The + * following equation is being solved: + * + * <blockquote><code>eqn[2]</code> · <i>x</i><sup>2</sup> + * + <code>eqn[1]</code> · <i>x</i> + * + <code>eqn[0]</code> + * = 0 + * </blockquote> + * + * <p>For some background about solving quadratic equations, see the + * article <a href= + * "http://planetmath.org/encyclopedia/QuadraticFormula.html" + * >“Quadratic Formula”</a> in <a href= + * "http://planetmath.org/">PlanetMath</a>. For an extensive library + * of numerical algorithms written in the C programming language, + * see the <a href="http://www.gnu.org/software/gsl/">GNU Scientific + * Library</a>. + * + * @see #solveQuadratic(double[], double[]) + * @see CubicCurve2D#solveCubic(double[], double[]) + * + * @param eqn an array with the coefficients of the equation. When + * this procedure has returned, <code>eqn</code> will contain the + * non-complex solutions of the equation, in no particular order. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the <a href= + * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveQuadratic(double[] eqn) + { + return solveQuadratic(eqn, eqn); + } + + /** + * Finds the non-complex roots of a quadratic equation. The + * following equation is being solved: + * + * <blockquote><code>eqn[2]</code> · <i>x</i><sup>2</sup> + * + <code>eqn[1]</code> · <i>x</i> + * + <code>eqn[0]</code> + * = 0 + * </blockquote> + * + * <p>For some background about solving quadratic equations, see the + * article <a href= + * "http://planetmath.org/encyclopedia/QuadraticFormula.html" + * >“Quadratic Formula”</a> in <a href= + * "http://planetmath.org/">PlanetMath</a>. For an extensive library + * of numerical algorithms written in the C programming language, + * see the <a href="http://www.gnu.org/software/gsl/">GNU Scientific + * Library</a>. + * + * @see CubicCurve2D#solveCubic(double[],double[]) + * + * @param eqn an array with the coefficients of the equation. + * + * @param res an array into which the non-complex roots will be + * stored. The results may be in an arbitrary order. It is safe to + * pass the same array object reference for both <code>eqn</code> + * and <code>res</code>. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the <a href= + * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveQuadratic(double[] eqn, double[] res) + { + // Taken from poly/solve_quadratic.c in the GNU Scientific Library + // (GSL), cvs revision 1.7 of 2003-07-26. For the original source, + // see http://www.gnu.org/software/gsl/ + // + // Brian Gough, the author of that code, has granted the + // permission to use it in GNU Classpath under the GNU Classpath + // license, and has assigned the copyright to the Free Software + // Foundation. + // + // The Java implementation is very similar to the GSL code, but + // not a strict one-to-one copy. For example, GSL would sort the + // result. + double a; + double b; + double c; + double disc; + + c = eqn[0]; + b = eqn[1]; + a = eqn[2]; + + // Check for linear or constant functions. This is not done by the + // GNU Scientific Library. Without this special check, we + // wouldn't return -1 for constant functions, and 2 instead of 1 + // for linear functions. + if (a == 0) + { + if (b == 0) + return -1; + + res[0] = -c / b; + return 1; + } + + disc = b * b - 4 * a * c; + + if (disc < 0) + return 0; + + if (disc == 0) + { + // The GNU Scientific Library returns two identical results here. + // We just return one. + res[0] = -0.5 * b / a; + return 1; + } + + // disc > 0 + if (b == 0) + { + double r; + + r = Math.abs(0.5 * Math.sqrt(disc) / a); + res[0] = -r; + res[1] = r; + } + else + { + double sgnb; + double temp; + + sgnb = (b > 0 ? 1 : -1); + temp = -0.5 * (b + sgnb * Math.sqrt(disc)); + + // The GNU Scientific Library sorts the result here. We don't. + res[0] = temp / a; + res[1] = c / temp; + } + return 2; + } + + /** + * Determines whether a point is inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + */ + public boolean contains(double x, double y) + { + if (! getBounds2D().contains(x, y)) + return false; + + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } + + /** + * Determines whether a point is inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines whether any part of a rectangle is inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” in a CubicCurve2D. + */ + public boolean intersects(double x, double y, double w, double h) + { + if (! getBounds2D().contains(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether any part of a Rectangle2D is inside the area bounded + * by the curve and the straight line connecting its end points. + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines whether a rectangle is entirely inside the area bounded + * by the curve and the straight line connecting its end points. + * + * <p><img src="doc-files/QuadCurve2D-5.png" width="350" height="180" + * alt="A drawing of the area spanned by the curve" /> + * + * <p>The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + * @see #contains(double, double) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether a Rectangle2D is entirely inside the area that is + * bounded by the curve and the straight line connecting its end points. + * @see #contains(double, double, double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the illustration + * below shows, the invisible control point may cause the bounds to + * be much larger than the area that is actually covered by the + * curve. + * + * <p><img src="doc-files/QuadCurve2D-2.png" width="350" height="180" + * alt="An illustration of the bounds of a QuadCurve2D" /> + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getCtrlX(); + coords[1] = (float) getCtrlY(); + coords[2] = (float) getX2(); + coords[3] = (float) getY2(); + result = SEG_QUADTO; + break; + default: + throw new NoSuchElementException("quad iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getCtrlX(); + coords[1] = getCtrlY(); + coords[2] = getX2(); + coords[3] = getY2(); + result = SEG_QUADTO; + break; + default: + throw new NoSuchElementException("quad iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return result; + } + }; + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Creates a new curve with the same contents as this one. + * + * @return the clone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Helper method used by contains() and intersects() methods + * Return the number of curve/line intersections on a given axis + * extending from a certain point. useYaxis is true for using the Y axis, + * @param x x coordinate of the origin point + * @param y y coordinate of the origin point + * @param useYaxis axis to follow, if true the positive Y axis is used, + * false uses the positive X axis. + * + * This is an implementation of the line-crossings algorithm, + * Detailed in an article on Eric Haines' page: + * http://www.acm.org/tog/editors/erich/ptinpoly/ + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + int nCrossings = 0; + double a0; + double a1; + double a2; + double b0; + double b1; + double b2; + double[] r = new double[3]; + int nRoots; + + a0 = a2 = 0.0; + + if (useYaxis) + { + a0 = getY1() - y; + a1 = getCtrlY() - y; + a2 = getY2() - y; + b0 = getX1() - x; + b1 = getCtrlX() - x; + b2 = getX2() - x; + } + else + { + a0 = getX1() - x; + a1 = getCtrlX() - x; + a2 = getX2() - x; + b0 = getY1() - y; + b1 = getCtrlY() - y; + b2 = getY2() - y; + } + + /* If the axis intersects a start/endpoint, shift it up by some small + amount to guarantee the line is 'inside' + If this is not done,bad behaviour may result for points on that axis. */ + if (a0 == 0.0 || a2 == 0.0) + { + double small = getFlatness() * EPSILON; + if (a0 == 0.0) + a0 -= small; + + if (a2 == 0.0) + a2 -= small; + } + + r[0] = a0; + r[1] = 2 * (a1 - a0); + r[2] = (a2 - 2 * a1 + a0); + + nRoots = solveQuadratic(r); + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + double crossing = t * t * (b2 - 2 * b1 + b0) + 2 * t * (b1 - b0) + + b0; + /* single root is always doubly degenerate in quads */ + if (crossing > 0 && crossing < distance) + nCrossings += (nRoots == 1) ? 2 : 1; + } + } + + if (useYaxis) + { + if (Line2D.linesIntersect(b0, a0, b2, a2, EPSILON, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a2, b2, 0.0, EPSILON, 0.0, distance)) + nCrossings++; + } + + return (nCrossings); + } + + /** + * A two-dimensional curve that is parameterized with a quadratic + * function and stores coordinate values in double-precision + * floating-point format. + * + * @see QuadCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Double extends QuadCurve2D + { + /** + * The <i>x</i> coordinate of the curve’s start point. + */ + public double x1; + + /** + * The <i>y</i> coordinate of the curve’s start point. + */ + public double y1; + + /** + * The <i>x</i> coordinate of the curve’s control point. + */ + public double ctrlx; + + /** + * The <i>y</i> coordinate of the curve’s control point. + */ + public double ctrly; + + /** + * The <i>x</i> coordinate of the curve’s end point. + */ + public double x2; + + /** + * The <i>y</i> coordinate of the curve’s end point. + */ + public double y2; + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in double-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Double() + { + } + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in double-precision floating-point format, specifying the + * initial position of each point. + * + * @param x1 the <i>x</i> coordinate of the curve’s start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s start + * point. + * + * @param cx the <i>x</i> coordinate of the curve’s control + * point. + * + * @param cy the <i>y</i> coordinate of the curve’s control + * point. + * + * @param x2 the <i>x</i> coordinate of the curve’s end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s end + * point. + */ + public Double(double x1, double y1, double cx, double cy, double x2, + double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s control + * point. + */ + public double getCtrlX() + { + return ctrlx; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s control + * point. + */ + public double getCtrlY() + { + return ctrly; + } + + /** + * Returns the curve’s control point. + */ + public Point2D getCtrlPt() + { + return new Point2D.Double(ctrlx, ctrly); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Changes the geometry of the curve. + * + * @param x1 the <i>x</i> coordinate of the curve’s new + * start point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new + * start point. + * + * @param cx the <i>x</i> coordinate of the curve’s new + * control point. + * + * @param cy the <i>y</i> coordinate of the curve’s new + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new + * end point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new + * end point. + */ + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the + * illustration below shows, the invisible control point may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + * <p><img src="doc-files/QuadCurve2D-2.png" width="350" height="180" + * alt="An illustration of the bounds of a QuadCurve2D" /> + */ + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx), x2); + double ny1 = Math.min(Math.min(y1, ctrly), y2); + double nx2 = Math.max(Math.max(x1, ctrlx), x2); + double ny2 = Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } + + /** + * A two-dimensional curve that is parameterized with a quadratic + * function and stores coordinate values in single-precision + * floating-point format. + * + * @see QuadCurve2D.Double + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Float extends QuadCurve2D + { + /** + * The <i>x</i> coordinate of the curve’s start point. + */ + public float x1; + + /** + * The <i>y</i> coordinate of the curve’s start point. + */ + public float y1; + + /** + * The <i>x</i> coordinate of the curve’s control point. + */ + public float ctrlx; + + /** + * The <i>y</i> coordinate of the curve’s control point. + */ + public float ctrly; + + /** + * The <i>x</i> coordinate of the curve’s end point. + */ + public float x2; + + /** + * The <i>y</i> coordinate of the curve’s end point. + */ + public float y2; + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in single-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Float() + { + } + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in single-precision floating-point format, specifying the + * initial position of each point. + * + * @param x1 the <i>x</i> coordinate of the curve’s start + * point. + * + * @param y1 the <i>y</i> coordinate of the curve’s start + * point. + * + * @param cx the <i>x</i> coordinate of the curve’s control + * point. + * + * @param cy the <i>y</i> coordinate of the curve’s control + * point. + * + * @param x2 the <i>x</i> coordinate of the curve’s end + * point. + * + * @param y2 the <i>y</i> coordinate of the curve’s end + * point. + */ + public Float(float x1, float y1, float cx, float cy, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the <i>x</i> coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s control + * point. + */ + public double getCtrlX() + { + return ctrlx; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s control + * point. + */ + public double getCtrlY() + { + return ctrly; + } + + /** + * Returns the curve’s control point. + */ + public Point2D getCtrlPt() + { + return new Point2D.Float(ctrlx, ctrly); + } + + /** + * Returns the <i>x</i> coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the <i>y</i> coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Changes the geometry of the curve, specifying coordinate values + * as double-precision floating-point numbers. + * + * @param x1 the <i>x</i> coordinate of the curve’s new + * start point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new + * start point. + * + * @param cx the <i>x</i> coordinate of the curve’s new + * control point. + * + * @param cy the <i>y</i> coordinate of the curve’s new + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new + * end point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new + * end point. + */ + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx = (float) cx; + ctrly = (float) cy; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Changes the geometry of the curve, specifying coordinate values + * as single-precision floating-point numbers. + * + * @param x1 the <i>x</i> coordinate of the curve’s new + * start point. + * + * @param y1 the <i>y</i> coordinate of the curve’s new + * start point. + * + * @param cx the <i>x</i> coordinate of the curve’s new + * control point. + * + * @param cy the <i>y</i> coordinate of the curve’s new + * control point. + * + * @param x2 the <i>x</i> coordinate of the curve’s new + * end point. + * + * @param y2 the <i>y</i> coordinate of the curve’s new + * end point. + */ + public void setCurve(float x1, float y1, float cx, float cy, float x2, + float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the + * illustration below shows, the invisible control point may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + * <p><img src="doc-files/QuadCurve2D-2.png" width="350" height="180" + * alt="An illustration of the bounds of a QuadCurve2D" /> + */ + public Rectangle2D getBounds2D() + { + float nx1 = (float) Math.min(Math.min(x1, ctrlx), x2); + float ny1 = (float) Math.min(Math.min(y1, ctrly), y2); + float nx2 = (float) Math.max(Math.max(x1, ctrlx), x2); + float ny2 = (float) Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } +} diff --git a/libjava/classpath/java/awt/geom/Rectangle2D.java b/libjava/classpath/java/awt/geom/Rectangle2D.java new file mode 100644 index 00000000000..6a255f95391 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Rectangle2D.java @@ -0,0 +1,992 @@ +/* Rectangle2D.java -- generic rectangles in 2-D space + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.util.NoSuchElementException; + +/** + * This class describes a rectangle by a point (x,y) and dimension (w x h). + * The actual storage is left up to subclasses. + * + * <p>It is valid for a rectangle to have negative width or height; but it + * is considered to have no area or internal points. Therefore, the behavior + * in methods like <code>contains</code> or <code>intersects</code> is + * undefined unless the rectangle has positive width and height. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Rectangle2D extends RectangularShape +{ + /** + * The point lies left of the rectangle (p.x < r.x). + * + * @see #outcode(double, double) + */ + public static final int OUT_LEFT = 1; + + /** + * The point lies above the rectangle (p.y < r.y). + * + * @see #outcode(double, double) + */ + public static final int OUT_TOP = 2; + + /** + * The point lies right of the rectangle (p.x > r.maxX). + * + * @see #outcode(double, double) + */ + public static final int OUT_RIGHT = 4; + + /** + * The point lies below of the rectangle (p.y > r.maxY). + * + * @see #outcode(double, double) + */ + public static final int OUT_BOTTOM = 8; + + /** + * Default constructor. + */ + protected Rectangle2D() + { + } + + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height + */ + public abstract void setRect(double x, double y, double w, double h); + + /** + * Set the bounding box of this rectangle from the given one. + * + * @param r rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param x1 the first x coordinate of line segment + * @param y1 the first y coordinate of line segment + * @param x2 the second x coordinate of line segment + * @param y2 the second y coordinate of line segment + * @return true if the line intersects the rectangle + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) + { + double x = getX(); + double y = getY(); + double w = getWidth(); + double h = getHeight(); + if (w <= 0 || h <= 0) + return false; + + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; + + double x3 = x + w; + double y3 = y + h; + + return (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y, x, y)); + } + + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param l the line segment + * @return true if the line intersects the rectangle + * @throws NullPointerException if l is null + */ + public boolean intersectsLine(Line2D l) + { + return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public abstract int outcode(double x, double y); + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param p the point to check + * @return the binary OR of the result + * @throws NullPointerException if p is null + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public int outcode(Point2D p) + { + return outcode(p.getX(), p.getY()); + } + + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height + */ + public void setFrame(double x, double y, double w, double h) + { + setRect(x, y, w, h); + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return (Rectangle2D) clone(); + } + + /** + * Test if the given point is contained in the rectangle. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * @return true if (x,y) is in the rectangle + */ + public boolean contains(double x, double y) + { + double mx = getX(); + double my = getY(); + double w = getWidth(); + double h = getHeight(); + return w > 0 && h > 0 && x >= mx && x < mx + w && y >= my && y < my + h; + } + + /** + * Tests if the given rectangle intersects this one. In other words, test if + * the two rectangles share at least one internal point. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if the rectangles intersect + */ + public boolean intersects(double x, double y, double w, double h) + { + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x < mx + mw && x + w > mx && y < my + mh && y + h > my; + } + + /** + * Tests if this rectangle contains the given one. In other words, test if + * this rectangle contains all points in the given one. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if this rectangle contains the other + */ + public boolean contains(double x, double y, double w, double h) + { + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x >= mx && x + w <= mx + mw && y >= my && y + h <= my + mh; + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createIntersection(Rectangle2D r); + + /** + * Intersects a pair of rectangles, and places the result in the + * destination; this can be used to avoid object creation. This method + * even works when the destination is also a source, although you stand + * to lose the original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the intersection + * @throws NullPointerException if any rectangle is null + */ + public static void intersect(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) + { + double x = Math.max(src1.getX(), src2.getX()); + double y = Math.max(src1.getY(), src2.getY()); + double maxx = Math.min(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.min(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createUnion(Rectangle2D r); + + /** + * Joins a pair of rectangles, and places the result in the destination; + * this can be used to avoid object creation. This method even works when + * the destination is also a source, although you stand to lose the + * original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the union + * @throws NullPointerException if any rectangle is null + */ + public static void union(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) + { + double x = Math.min(src1.getX(), src2.getX()); + double y = Math.min(src1.getY(), src2.getY()); + double maxx = Math.max(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.max(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param newx the X coordinate of the point to add to this rectangle + * @param newy the Y coordinate of the point to add to this rectangle + */ + public void add(double newx, double newy) + { + double minx = Math.min(getX(), newx); + double maxx = Math.max(getMaxX(), newx); + double miny = Math.min(getY(), newy); + double maxy = Math.max(getMaxY(), newy); + setRect(minx, miny, maxx - minx, maxy - miny); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to <code>contains</code> may + * return false. + * + * @param p the point to add to this rectangle + * @throws NullPointerException if p is null + */ + public void add(Point2D p) + { + add(p.getX(), p.getY()); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified rectangle. + * + * @param r the rectangle to add to this rectangle + * @throws NullPointerException if r is null + * @see #union(Rectangle2D, Rectangle2D, Rectangle2D) + */ + public void add(Rectangle2D r) + { + union(this, r, this); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. + * + * @param at an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(final AffineTransform at) + { + final double minx = getX(); + final double miny = getY(); + final double maxx = minx + getWidth(); + final double maxy = miny + getHeight(); + return new PathIterator() + { + /** Current coordinate. */ + private int current = (maxx <= minx && maxy <= miny) ? 6 : 0; + + public int getWindingRule() + { + // A test program showed that Sun J2SE 1.3.1 and 1.4.1_01 + // return WIND_NON_ZERO paths. While this does not really + // make any difference for rectangles (because they are not + // self-intersecting), it seems appropriate to behave + // identically. + + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current > 5; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + switch (current) + { + case 1: + coords[0] = (float) maxx; + coords[1] = (float) miny; + break; + case 2: + coords[0] = (float) maxx; + coords[1] = (float) maxy; + break; + case 3: + coords[0] = (float) minx; + coords[1] = (float) maxy; + break; + case 0: + case 4: + coords[0] = (float) minx; + coords[1] = (float) miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + switch (current) + { + case 1: + coords[0] = maxx; + coords[1] = miny; + break; + case 2: + coords[0] = maxx; + coords[1] = maxy; + break; + case 3: + coords[0] = minx; + coords[1] = maxy; + break; + case 0: + case 4: + coords[0] = minx; + coords[1] = miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. As the rectangle is already flat, the flatness parameter + * is ignored. + * + * @param at an optional transform to apply to the iterator + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return getPathIterator(at); + } + + /** + * Return the hashcode for this rectangle. The formula is not documented, but + * appears to be the same as: + * <pre> + * long l = Double.doubleToLongBits(getX()) + * + 37 * Double.doubleToLongBits(getY()) + * + 43 * Double.doubleToLongBits(getWidth()) + * + 47 * Double.doubleToLongBits(getHeight()); + * return (int) ((l >> 32) ^ l); + * </pre> + * + * @return the hashcode + */ + public int hashCode() + { + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getX()) + + 37 * java.lang.Double.doubleToLongBits(getY()) + + 43 * java.lang.Double.doubleToLongBits(getWidth()) + + 47 * java.lang.Double.doubleToLongBits(getHeight()); + return (int) ((l >> 32) ^ l); + } + + /** + * Tests this rectangle for equality against the specified object. This + * will be true if an only if the specified object is an instance of + * Rectangle2D with the same coordinates and dimensions. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Rectangle2D)) + return false; + Rectangle2D r = (Rectangle2D) obj; + return r.getX() == getX() && r.getY() == getY() + && r.getWidth() == getWidth() && r.getHeight() == getHeight(); + } + + /** + * This class defines a rectangle in <code>double</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Rectangle2D + { + /** The x coordinate of the lower left corner. */ + public double x; + + /** The y coordinate of the lower left corner. */ + public double y; + + /** The width of the rectangle. */ + public double width; + + /** The height of the rectangle. */ + public double height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Double() + { + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Double(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return new Double(x, y, width, height); + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + Double res = new Double(); + intersect(this, r, res); + return res; + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) + { + Double res = new Double(); + union(this, r, res); + return res; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'</code>. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; + } + } + + /** + * This class defines a rectangle in <code>float</code> precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Rectangle2D + { + /** The x coordinate of the lower left corner. */ + public float x; + + /** The y coordinate of the lower left corner. */ + public float y; + + /** The width of the rectangle. */ + public float width; + + /** The height of the rectangle. */ + public float height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Float() + { + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Float(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + Float(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + } + + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return new Float(x, y, width, height); + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + Float res = new Float(); + intersect(this, r, res); + return res; + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) + { + Float res = new Float(); + union(this, r, res); + return res; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'</code>. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; + } + } +} diff --git a/libjava/classpath/java/awt/geom/RectangularShape.java b/libjava/classpath/java/awt/geom/RectangularShape.java new file mode 100644 index 00000000000..8f66dabf2e7 --- /dev/null +++ b/libjava/classpath/java/awt/geom/RectangularShape.java @@ -0,0 +1,385 @@ +/* RectangularShape.java -- a rectangular frame for several generic shapes + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + +/** + * This class provides a generic framework, and several helper methods, for + * subclasses which represent geometric objects inside a rectangular frame. + * This does not specify any geometry except for the bounding box. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @see Arc2D + * @see Ellipse2D + * @see Rectangle2D + * @see RoundRectangle2D + * @status updated to 1.4 + */ +public abstract class RectangularShape implements Shape, Cloneable +{ + /** + * Default constructor. + */ + protected RectangularShape() + { + } + + /** + * Get the x coordinate of the upper-left corner of the framing rectangle. + * + * @return the x coordinate + */ + public abstract double getX(); + + /** + * Get the y coordinate of the upper-left corner of the framing rectangle. + * + * @return the y coordinate + */ + public abstract double getY(); + + /** + * Get the width of the framing rectangle. + * + * @return the width + */ + public abstract double getWidth(); + + /** + * Get the height of the framing rectangle. + * + * @return the height + */ + public abstract double getHeight(); + + /** + * Get the minimum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX() even when + * getWidth() is negative. + * + * @return the minimum x coordinate + */ + public double getMinX() + { + return getX(); + } + + /** + * Get the minimum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY() even when + * getHeight() is negative. + * + * @return the minimum y coordinate + */ + public double getMinY() + { + return getY(); + } + + /** + * Get the maximum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX()+getWidth() even + * when getWidth() is negative. + * + * @return the maximum x coordinate + */ + public double getMaxX() + { + return getX() + getWidth(); + } + + /** + * Get the maximum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY()+getHeight() even + * when getHeight() is negative. + * + * @return the maximum y coordinate + */ + public double getMaxY() + { + return getY() + getHeight(); + } + + /** + * Return the x coordinate of the center point of the framing rectangle. + * + * @return the central x coordinate + */ + public double getCenterX() + { + return getX() + getWidth() / 2; + } + + /** + * Return the y coordinate of the center point of the framing rectangle. + * + * @return the central y coordinate + */ + public double getCenterY() + { + return getY() + getHeight() / 2; + } + + /** + * Return the frame around this object. Note that this may be a looser + * bounding box than getBounds2D. + * + * @return the frame, in double precision + * @see #setFrame(double, double, double, double) + */ + public Rectangle2D getFrame() + { + return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight()); + } + + /** + * Test if the shape is empty, meaning that no points are inside it. + * + * @return true if the shape is empty + */ + public abstract boolean isEmpty(); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public abstract void setFrame(double x, double y, double w, double h); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param p the new point + * @param d the new dimension + * @throws NullPointerException if p or d is null + * @see #getFrame() + */ + public void setFrame(Point2D p, Dimension2D d) + { + setFrame(p.getX(), p.getY(), d.getWidth(), d.getHeight()); + } + + /** + * Set the framing rectangle of this shape to the given rectangle. + * + * @param r the new framing rectangle + * @throws NullPointerException if r is null + * @see #getFrame() + */ + public void setFrame(Rectangle2D r) + { + setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public void setFrameFromDiagonal(double x1, double y1, double x2, double y2) + { + if (x1 > x2) + { + double t = x2; + x2 = x1; + x1 = t; + } + if (y1 > y2) + { + double t = y2; + y2 = y1; + y1 = t; + } + setFrame(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setFrameFromDiagonal(Point2D p1, Point2D p2) + { + setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param centerX the x coordinate at the center + * @param centerY the y coordinate at the center + * @param cornerX the x coordinate at a corner + * @param cornerY the y coordinate at a corner + */ + public void setFrameFromCenter(double centerX, double centerY, + double cornerX, double cornerY) + { + double halfw = Math.abs(cornerX - centerX); + double halfh = Math.abs(cornerY - centerY); + setFrame(centerX - halfw, centerY - halfh, halfw + halfw, halfh + halfh); + } + + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param center the center point + * @param corner a corner point + * @throws NullPointerException if either point is null + */ + public void setFrameFromCenter(Point2D center, Point2D corner) + { + setFrameFromCenter(center.getX(), center.getY(), + corner.getX(), corner.getY()); + } + + /** + * Tests if a point is inside the boundary of the shape. + * + * @param p the point to test + * @return true if the point is inside the shape + * @throws NullPointerException if p is null + * @see #contains(double, double) + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Tests if a rectangle and this shape share common internal points. + * + * @param r the rectangle to test + * @return true if the rectangle intersects this shpae + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the shape completely contains the given rectangle. + * + * @param r the rectangle to test + * @return true if r is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns a bounding box for this shape, in integer format. Notice that you + * may get a tighter bound with getBounds2D. If the frame is empty, the + * box is the default empty box at the origin. + * + * @return a bounding box + */ + public Rectangle getBounds() + { + if (isEmpty()) + return new Rectangle(); + double x = getX(); + double y = getY(); + double maxx = Math.ceil(x + getWidth()); + double maxy = Math.ceil(y + getHeight()); + x = Math.floor(x); + y = Math.floor(y); + return new Rectangle((int) x, (int) y, (int) (maxx - x), (int) (maxy - y)); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. The path is + * flattened until all segments differ from the curve by at most the value + * of the flatness parameter, within the limits of the default interpolation + * recursion limit of 1024 segments between actual points. Each call + * returns a new object, independent from others in use. The result is + * threadsafe if and only if the iterator returned by + * {@link #getPathIterator(AffineTransform)} is as well. + * + * @param at an optional transform to apply to the iterator + * @param flatness the desired flatness + * @return a new iterator over the boundary + * @throws IllegalArgumentException if flatness is invalid + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new shape of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class RectangularShape diff --git a/libjava/classpath/java/awt/geom/RoundRectangle2D.java b/libjava/classpath/java/awt/geom/RoundRectangle2D.java new file mode 100644 index 00000000000..ac0e6f8128a --- /dev/null +++ b/libjava/classpath/java/awt/geom/RoundRectangle2D.java @@ -0,0 +1,533 @@ +/* RoundRectangle2D.java -- represents a rectangle with rounded corners + Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** This class implements a rectangle with rounded corners. + * @author Tom Tromey (tromey@cygnus.com) + * @date December 3, 2000 + */ +public abstract class RoundRectangle2D extends RectangularShape +{ + /** Return the arc height of this round rectangle. */ + public abstract double getArcHeight(); + + /** Return the arc width of this round rectangle. */ + public abstract double getArcWidth(); + + /** Set the values of this round rectangle + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public abstract void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight); + + /** Create a RoundRectangle2D. This is protected because this class + * is abstract and cannot be instantiated. + */ + protected RoundRectangle2D() + { + } + + /** Return true if this object contains the specified point. + * @param x The x coordinate + * @param y The y coordinate + */ + public boolean contains(double x, double y) + { + double mx = getX(); + double mw = getWidth(); + if (x < mx || x >= mx + mw) + return false; + double my = getY(); + double mh = getHeight(); + if (y < my || y >= my + mh) + return false; + + // Now check to see if the point is in range of an arc. + double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y)); + double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x)); + + // The arc dimensions are that of the corresponding ellipse + // thus a 90 degree segment is half of that. + double aw = getArcWidth() / 2.0; + double ah = getArcHeight() / 2.0; + if (dx > aw || dy > ah) + return true; + + // At this point DX represents the distance from the nearest edge + // of the rectangle. But we want to transform it to represent the + // scaled distance from the center of the ellipse that forms the + // arc. Hence this code: + dy = (ah - dy) / ah; + dx = (aw - dx) / aw; + + return dx * dx + dy * dy <= 1.0; + } + + /** Return true if this object contains the specified rectangle + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public boolean contains(double x, double y, double w, double h) + { + // We have to check all four points here (for ordinary rectangles + // we can just check opposing corners). + return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h) + && contains(x + w, y)); + } + + /** Return a new path iterator which iterates over this rectangle. + * @param at An affine transform to apply to the object + */ + public PathIterator getPathIterator(final AffineTransform at) + { + final double minx = getX(); + final double miny = getY(); + final double maxx = minx + getWidth(); + final double maxy = miny + getHeight(); + final double arcwidth = getArcWidth(); + final double archeight = getArcHeight(); + return new PathIterator() + { + /** We iterate counterclockwise around the rectangle, starting in the + * upper right. This variable tracks our current point, which + * can be on either side of a given corner. */ + private int current = 0; + + /** Child path iterator, used for corners. */ + private PathIterator corner; + + /** This is used when rendering the corners. We re-use the arc + * for each corner. */ + private Arc2D arc = new Arc2D.Double(); + + /** Temporary array used by getPoint. */ + private double[] temp = new double[2]; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current > 9; + } + + private void getPoint(int val) + { + switch (val) + { + case 0: + case 8: + temp[0] = maxx; + temp[1] = miny + archeight; + break; + case 7: + temp[0] = maxx; + temp[1] = maxy - archeight; + break; + case 6: + temp[0] = maxx - arcwidth; + temp[1] = maxy; + break; + case 5: + temp[0] = minx + arcwidth; + temp[1] = maxy; + break; + case 4: + temp[0] = minx; + temp[1] = maxy - archeight; + break; + case 3: + temp[0] = minx; + temp[1] = miny + archeight; + break; + case 2: + temp[0] = minx + arcwidth; + temp[1] = miny; + break; + case 1: + temp[0] = maxx - arcwidth; + temp[1] = miny; + break; + } + } + + public void next() + { + if (current >= 8) + ++current; + else if (corner != null) + { + // We're iterating through the corner. Work on the child + // iterator; if it finishes, reset and move to the next + // point along the rectangle. + corner.next(); + if (corner.isDone()) + { + corner = null; + ++current; + } + } + else + { + // Make an arc between this point on the rectangle and + // the next one, and then iterate over this arc. + getPoint(current); + double x1 = temp[0]; + double y1 = temp[1]; + getPoint(current + 1); + Rectangle2D.Double r = new Rectangle2D.Double(Math.min(x1, + temp[0]), + Math.min(y1, + temp[1]), + Math.abs(x1 + - temp[0]), + Math.abs(y1 + - temp[1])); + arc.setArc(r, (current >> 1) * 90.0, 90.0, Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + } + + public int currentSegment(float[] coords) + { + if (corner != null) + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + + if (current < 9) + { + getPoint(current); + coords[0] = (float) temp[0]; + coords[1] = (float) temp[1]; + } + else if (current == 9) + return SEG_CLOSE; + else + throw new NoSuchElementException("rect iterator out of bounds"); + + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + if (corner != null) + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + + if (current < 9) + { + getPoint(current); + coords[0] = temp[0]; + coords[1] = temp[1]; + } + else if (current == 9) + return SEG_CLOSE; + else + throw new NoSuchElementException("rect iterator out of bounds"); + + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; + } + + /** Return true if the given rectangle intersects this shape. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public boolean intersects(double x, double y, double w, double h) + { + // Check if any corner is within the rectangle + return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h) + || contains(x + w, y)); + } + + /** Set the boundary of this round rectangle. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public void setFrame(double x, double y, double w, double h) + { + // This is a bit lame. + setRoundRect(x, y, w, h, getArcWidth(), getArcHeight()); + } + + /** Set the values of this round rectangle to be the same as those + * of the argument. + * @param rr The round rectangle to copy + */ + public void setRoundRect(RoundRectangle2D rr) + { + setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), + rr.getArcWidth(), rr.getArcHeight()); + } + + /** A subclass of RoundRectangle which keeps its parameters as + * doubles. */ + public static class Double extends RoundRectangle2D + { + /** The height of the corner arc. */ + public double archeight; + + /** The width of the corner arc. */ + public double arcwidth; + + /** The x coordinate of this object. */ + public double x; + + /** The y coordinate of this object. */ + public double y; + + /** The width of this object. */ + public double width; + + /** The height of this object. */ + public double height; + + /** Construct a new instance, with all parameters set to 0. */ + public Double() + { + } + + /** Construct a new instance with the given arguments. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public Double(double x, double y, double w, double h, double arcWidth, + double arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public double getArcHeight() + { + return archeight; + } + + public double getArcWidth() + { + return arcwidth; + } + + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Double(x, y, width, height); + } + + public double getX() + { + return x; + } + + public double getY() + { + return y; + } + + public double getWidth() + { + return width; + } + + public double getHeight() + { + return height; + } + + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + } // class Double + + /** A subclass of RoundRectangle which keeps its parameters as + * floats. */ + public static class Float extends RoundRectangle2D + { + /** The height of the corner arc. */ + public float archeight; + + /** The width of the corner arc. */ + public float arcwidth; + + /** The x coordinate of this object. */ + public float x; + + /** The y coordinate of this object. */ + public float y; + + /** The width of this object. */ + public float width; + + /** The height of this object. */ + public float height; + + /** Construct a new instance, with all parameters set to 0. */ + public Float() + { + } + + /** Construct a new instance with the given arguments. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public Float(float x, float y, float w, float h, float arcWidth, + float arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public double getArcHeight() + { + return archeight; + } + + public double getArcWidth() + { + return arcwidth; + } + + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Float(x, y, width, height); + } + + public double getX() + { + return x; + } + + public double getY() + { + return y; + } + + public double getWidth() + { + return width; + } + + public double getHeight() + { + return height; + } + + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + public void setRoundRect(float x, float y, float w, float h, + float arcWidth, float arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) + { + this.x = (float) x; + this.y = (float) y; + this.width = (float) w; + this.height = (float) h; + this.arcwidth = (float) arcWidth; + this.archeight = (float) arcHeight; + } + } // class Float +} // class RoundRectangle2D diff --git a/libjava/classpath/java/awt/geom/doc-files/Area-1.png b/libjava/classpath/java/awt/geom/doc-files/Area-1.png Binary files differnew file mode 100644 index 00000000000..44650f2d8a1 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/Area-1.png diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png Binary files differnew file mode 100644 index 00000000000..1784509be61 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png Binary files differnew file mode 100644 index 00000000000..1ddae9fc84f --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png Binary files differnew file mode 100644 index 00000000000..b200dad37a8 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png Binary files differnew file mode 100644 index 00000000000..e57ffdc5cf0 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png Binary files differnew file mode 100644 index 00000000000..701ab138f0b --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png diff --git a/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png b/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png Binary files differnew file mode 100644 index 00000000000..8317db66196 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png diff --git a/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html b/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html new file mode 100644 index 00000000000..5a52d693edd --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html @@ -0,0 +1,481 @@ +<?xml version="1.0" encoding="US-ASCII"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <title>The GNU Implementation of java.awt.geom.FlatteningPathIterator</title> + <meta name="author" content="Sascha Brawer" /> + <style type="text/css"><!-- + td { white-space: nowrap; } + li { margin: 2mm 0; } + --></style> +</head> +<body> + +<h1>The GNU Implementation of FlatteningPathIterator</h1> + +<p><i><a href="http://www.dandelis.ch/people/brawer/">Sascha +Brawer</a>, November 2003</i></p> + +<p>This document describes the GNU implementation of the class +<code>java.awt.geom.FlatteningPathIterator</code>. It does +<em>not</em> describe how a programmer should use this class; please +refer to the generated API documentation for this purpose. Instead, it +is intended for maintenance programmers who want to understand the +implementation, for example because they want to extend the class or +fix a bug.</p> + + +<h2>Data Structures</h2> + +<p>The algorithm uses a stack. Its allocation is delayed to the time +when the source path iterator actually returns the first curved +segment (either <code>SEG_QUADTO</code> or <code>SEG_CUBICTO</code>). +If the input path does not contain any curved segments, the value of +the <code>stack</code> variable stays <code>null</code>. In this quite +common case, the memory consumption is minimal.</p> + +<dl><dt><code>stack</code></dt><dd>The variable <code>stack</code> is +a <code>double</code> array that holds the start, control and end +points of individual sub-segments.</dd> + +<dt><code>recLevel</code></dt><dd>The variable <code>recLevel</code> +holds how many recursive sub-divisions were needed to calculate a +segment. The original curve has recursion level 0. For each +sub-division, the corresponding recursion level is increased by +one.</dd> + +<dt><code>stackSize</code></dt><dd>Finally, the variable +<code>stackSize</code> indicates how many sub-segments are stored on +the stack.</dd></dl> + +<h2>Algorithm</h2> + +<p>The implementation separately processes each segment that the +base iterator returns.</p> + +<p>In the case of <code>SEG_CLOSE</code>, +<code>SEG_MOVETO</code> and <code>SEG_LINETO</code> segments, the +implementation simply hands the segment to the consumer, without actually +doing anything.</p> + +<p>Any <code>SEG_QUADTO</code> and <code>SEG_CUBICTO</code> segments +need to be flattened. Flattening is performed with a fixed-sized +stack, holding the coordinates of subdivided segments. When the base +iterator returns a <code>SEG_QUADTO</code> and +<code>SEG_CUBICTO</code> segments, it is recursively flattened as +follows:</p> + +<ol><li>Intialization: Allocate memory for the stack (unless a +sufficiently large stack has been allocated previously). Push the +original quadratic or cubic curve onto the stack. Mark that segment as +having a <code>recLevel</code> of zero.</li> + +<li>If the stack is empty, flattening the segment is complete, +and the next segment is fetched from the base iterator.</li> + +<li>If the stack is not empty, pop a curve segment from the +stack. + + <ul><li>If its <code>recLevel</code> exceeds the recursion limit, + hand the current segment to the consumer.</li> + + <li>Calculate the squared flatness of the segment. If it smaller + than <code>flatnessSq</code>, hand the current segment to the + consumer.</li> + + <li>Otherwise, split the segment in two halves. Push the right + half onto the stack. Then, push the left half onto the stack. + Continue with step two.</li></ul></li> +</ol> + +<p>The implementation is slightly complicated by the fact that +consumers <em>pull</em> the flattened segments from the +<code>FlatteningPathIterator</code>. This means that we actually +cannot “hand the curent segment over to the consumer.” +But the algorithm is easier to understand if one assumes a +<em>push</em> paradigm.</p> + + +<h2>Example</h2> + +<p>The following example shows how a +<code>FlatteningPathIterator</code> processes a +<code>SEG_QUADTO</code> segment. It is (arbitrarily) assumed that the +recursion limit was set to 2.</p> + +<blockquote> +<table border="1" cellspacing="0" cellpadding="8"> + <tr align="center" valign="baseline"> + <th></th><th>A</th><th>B</th><th>C</th> + <th>D</th><th>E</th><th>F</th><th>G</th><th>H</th> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[0]</code></th> + <td>—</td> + <td>—</td> + <td><i>S<sub>ll</sub>.x</i></td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[1]</code></th> + <td>—</td> + <td>—</td> + <td><i>S<sub>ll</sub>.y</i></td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[2]</code></th> + <td>—</td> + <td>—</td> + <td><i>C<sub>ll</sub>.x</i></td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[3]</code></th> + <td>—</td> + <td>—</td> + <td><i>C<sub>ll</sub>.y</i></td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[4]</code></th> + <td>—</td> + <td><i>S<sub>l</sub>.x</i></td> + <td><i>E<sub>ll</sub>.x</i> + = <i>S<sub>lr</sub>.x</i></td> + <td><i>S<sub>lr</sub>.x</i></td> + <td>—</td> + <td><i>S<sub>rl</sub>.x</i></td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[5]</code></th> + <td>—</td> + <td><i>S<sub>l</sub>.y</i></td> + <td><i>E<sub>ll</sub>.x</i> + = <i>S<sub>lr</sub>.y</i></td> + <td><i>S<sub>lr</sub>.y</i></td> + <td>—</td> + <td><i>S<sub>rl</sub>.y</i></td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[6]</code></th> + <td>—</td> + <td><i>C<sub>l</sub>.x</i></td> + <td><i>C<sub>lr</sub>.x</i></td> + <td><i>C<sub>lr</sub>.x</i></td> + <td>—</td> + <td><i>C<sub>rl</sub>.x</i></td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[7]</code></th> + <td>—</td> + <td><i>C<sub>l</sub>.y</i></td> + <td><i>C<sub>lr</sub>.y</i></td> + <td><i>C<sub>lr</sub>.y</i></td> + <td>—</td> + <td><i>C<sub>rl</sub>.y</i></td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[8]</code></th> + <td><i>S.x</i></td> + <td><i>E<sub>l</sub>.x</i> + = <i>S<sub>r</sub>.x</i></td> + <td><i>E<sub>lr</sub>.x</i> + = <i>S<sub>r</sub>.x</i></td> + <td><i>E<sub>lr</sub>.x</i> + = <i>S<sub>r</sub>.x</i></td> + <td><i>S<sub>r</sub>.x</i></td> + <td><i>E<sub>rl</sub>.x</i> + = <i>S<sub>rr</sub>.x</i></td> + <td><i>S<sub>rr</sub>.x</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[9]</code></th> + <td><i>S.y</i></td> + <td><i>E<sub>l</sub>.y</i> + = <i>S<sub>r</sub>.y</i></td> + <td><i>E<sub>lr</sub>.y</i> + = <i>S<sub>r</sub>.y</i></td> + <td><i>E<sub>lr</sub>.y</i> + = <i>S<sub>r</sub>.y</i></td> + <td><i>S<sub>r</sub>.y</i></td> + <td><i>E<sub>rl</sub>.y</i> + = <i>S<sub>rr</sub>.y</i></td> + <td><i>S<sub>rr</sub>.y</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[10]</code></th> + <td><i>C.x</i></td> + <td><i>C<sub>r</sub>.x</i></td> + <td><i>C<sub>r</sub>.x</i></td> + <td><i>C<sub>r</sub>.x</i></td> + <td><i>C<sub>r</sub>.x</i></td> + <td><i>C<sub>rr</sub>.x</i></td> + <td><i>C<sub>rr</sub>.x</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[11]</code></th> + <td><i>C.y</i></td> + <td><i>C<sub>r</sub>.y</i></td> + <td><i>C<sub>r</sub>.y</i></td> + <td><i>C<sub>r</sub>.y</i></td> + <td><i>C<sub>r</sub>.y</i></td> + <td><i>C<sub>rr</sub>.y</i></td> + <td><i>C<sub>rr</sub>.y</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[12]</code></th> + <td><i>E.x</i></td> + <td><i>E<sub>r</sub>.x</i></td> + <td><i>E<sub>r</sub>.x</i></td> + <td><i>E<sub>r</sub>.x</i></td> + <td><i>E<sub>r</sub>.x</i></td> + <td><i>E<sub>rr</sub>.x</i></td> + <td><i>E<sub>rr</sub>.x</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stack[13]</code></th> + <td><i>E.y</i></td> + <td><i>E<sub>r</sub>.y</i></td> + <td><i>E<sub>r</sub>.y</i></td> + <td><i>E<sub>r</sub>.y</i></td> + <td><i>E<sub>r</sub>.y</i></td> + <td><i>E<sub>rr</sub>.y</i></td> + <td><i>E<sub>rr</sub>.x</i></td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>stackSize</code></th> + <td>1</td> + <td>2</td> + <td>3</td> + <td>2</td> + <td>1</td> + <td>2</td> + <td>1</td> + <td>0</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>recLevel[2]</code></th> + <td>—</td> + <td>—</td> + <td>2</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>recLevel[1]</code></th> + <td>—</td> + <td>1</td> + <td>2</td> + <td>2</td> + <td>—</td> + <td>2</td> + <td>—</td> + <td>—</td> + </tr> + <tr align="center" valign="baseline"> + <th><code>recLevel[0]</code></th> + <td>0</td> + <td>1</td> + <td>1</td> + <td>1</td> + <td>1</td> + <td>2</td> + <td>2</td> + <td>—</td> + </tr> + </table> +</blockquote> + +<ol> + +<li>The data structures are initialized as follows. + +<ul><li>The segment’s end point <i>E</i>, control point +<i>C</i>, and start point <i>S</i> are pushed onto the stack.</li> + + <li>Currently, the curve in the stack would be approximated by one + single straight line segment (<i>S</i> – <i>E</i>). + Therefore, <code>stackSize</code> is set to 1.</li> + + <li>This single straight line segment is approximating the original + curve, which can be seen as the result of zero recursive + splits. Therefore, <code>recLevel[0]</code> is set to + zero.</li></ul> + +Column A shows the state after the initialization step.</li> + +<li>The algorithm proceeds by taking the topmost curve segment +(<i>S</i> – <i>C</i> – <i>E</i>) from the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[0]</code>) is zero, which is smaller than + the limit 2.</li> + + <li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code> + is called to calculate the squared flatness.</li> + + <li>For the sake of argument, we assume that the squared flatness is + exceeding the threshold stored in <code>flatnessSq</code>. Thus, the + curve segment <i>S</i> – <i>C</i> – <i>E</i> gets + subdivided into a left and a right half, namely + <i>S<sub>l</sub></i> – <i>C<sub>l</sub></i> – + <i>E<sub>l</sub></i> and <i>S<sub>r</sub></i> – + <i>C<sub>r</sub></i> – <i>E<sub>r</sub></i>. Both halves are + pushed onto the stack, so the left half is now on top. + + <br /> <br />The left half starts at the same point + as the original curve, so <i>S<sub>l</sub></i> has the same + coordinates as <i>S</i>. Similarly, the end point of the right + half and of the original curve are identical + (<i>E<sub>r</sub></i> = <i>E</i>). More interestingly, the left + half ends where the right half starts. Because + <i>E<sub>l</sub></i> = <i>S<sub>r</sub></i>, their coordinates need + to be stored only once, which amounts to saving 16 bytes (two + <code>double</code> values) for each iteration.</li></ul> + +Column B shows the state after the first iteration.</li> + +<li>Again, the topmost curve segment (<i>S<sub>l</sub></i> +– <i>C<sub>l</sub></i> – <i>E<sub>l</sub></i>) is +taken from the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[1]</code>) is 1, which is smaller than + the limit 2.</li> + + <li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code> + is called to calculate the squared flatness.</li> + + <li>Assuming that the segment is still not considered + flat enough, it gets subdivided into a left + (<i>S<sub>ll</sub></i> – <i>C<sub>ll</sub></i> – + <i>E<sub>ll</sub></i>) and a right (<i>S<sub>lr</sub></i> + – <i>C<sub>lr</sub></i> – <i>E<sub>lr</sub></i>) + half.</li></ul> + +Column C shows the state after the second iteration.</li> + +<li>The topmost curve segment (<i>S<sub>ll</sub></i> – +<i>C<sub>ll</sub></i> – <i>E<sub>ll</sub></i>) is popped from +the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than + the limit 2. Therefore, a <code>SEG_LINETO</code> (from + <i>S<sub>ll</sub></i> to <i>E<sub>ll</sub></i>) is passed to the + consumer.</li></ul> + + The new state is shown in column D.</li> + + +<li>The topmost curve segment (<i>S<sub>lr</sub></i> – +<i>C<sub>lr</sub></i> – <i>E<sub>lr</sub></i>) is popped from +the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[1]</code>) is 2, which is <em>not</em> smaller than + the limit 2. Therefore, a <code>SEG_LINETO</code> (from + <i>S<sub>lr</sub></i> to <i>E<sub>lr</sub></i>) is passed to the + consumer.</li></ul> + + The new state is shown in column E.</li> + +<li>The algorithm proceeds by taking the topmost curve segment +(<i>S<sub>r</sub></i> – <i>C<sub>r</sub></i> – +<i>E<sub>r</sub></i>) from the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[0]</code>) is 1, which is smaller than + the limit 2.</li> + + <li>The method <code>java.awt.geom.QuadCurve2D.getFlatnessSq</code> + is called to calculate the squared flatness.</li> + + <li>For the sake of argument, we again assume that the squared + flatness is exceeding the threshold stored in + <code>flatnessSq</code>. Thus, the curve segment + (<i>S<sub>r</sub></i> – <i>C<sub>r</sub></i> – + <i>E<sub>r</sub></i>) is subdivided into a left and a right half, + namely + <i>S<sub>rl</sub></i> – <i>C<sub>rl</sub></i> – + <i>E<sub>rl</sub></i> and <i>S<sub>rr</sub></i> – + <i>C<sub>rr</sub></i> – <i>E<sub>rr</sub></i>. Both halves + are pushed onto the stack.</li></ul> + + The new state is shown in column F.</li> + +<li>The topmost curve segment (<i>S<sub>rl</sub></i> – +<i>C<sub>rl</sub></i> – <i>E<sub>rl</sub></i>) is popped from +the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than + the limit 2. Therefore, a <code>SEG_LINETO</code> (from + <i>S<sub>rl</sub></i> to <i>E<sub>rl</sub></i>) is passed to the + consumer.</li></ul> + + The new state is shown in column G.</li> + +<li>The topmost curve segment (<i>S<sub>rr</sub></i> – +<i>C<sub>rr</sub></i> – <i>E<sub>rr</sub></i>) is popped from +the stack. + + <ul><li>The recursion level of this segment (stored in + <code>recLevel[2]</code>) is 2, which is <em>not</em> smaller than + the limit 2. Therefore, a <code>SEG_LINETO</code> (from + <i>S<sub>rr</sub></i> to <i>E<sub>rr</sub></i>) is passed to the + consumer.</li></ul> + + The new state is shown in column H.</li> + +<li>The stack is now empty. The FlatteningPathIterator will fetch the +next segment from the base iterator, and process it.</li> + +</ol> + +<p>In order to split the most recently pushed segment, the +<code>subdivideQuadratic()</code> method passes <code>stack</code> +directly to +<code>QuadCurve2D.subdivide(double[],int,double[],int,double[],int)</code>. +Because the stack grows towards the beginning of the array, no data +needs to be copied around: <code>subdivide</code> will directly store +the result into the stack, which will have the contents shown to the +right.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png b/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png Binary files differnew file mode 100644 index 00000000000..d1d75d57526 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png Binary files differnew file mode 100644 index 00000000000..7c2ec0ea9cb --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png Binary files differnew file mode 100644 index 00000000000..496180c4474 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png Binary files differnew file mode 100644 index 00000000000..a7557ba7baf --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png Binary files differnew file mode 100644 index 00000000000..835c0643b29 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png Binary files differnew file mode 100644 index 00000000000..72110cd5a62 --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png diff --git a/libjava/classpath/java/awt/geom/package.html b/libjava/classpath/java/awt/geom/package.html new file mode 100644 index 00000000000..c8ee8272f6a --- /dev/null +++ b/libjava/classpath/java/awt/geom/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.geom package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.geom</title></head> + +<body> +<p>Classes to represent 2D objects and different path transformations.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/im/InputContext.java b/libjava/classpath/java/awt/im/InputContext.java new file mode 100644 index 00000000000..e289677fe9c --- /dev/null +++ b/libjava/classpath/java/awt/im/InputContext.java @@ -0,0 +1,434 @@ +/* InputContext.java -- provides the context for text input + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.im; + +import gnu.java.util.EmptyEnumeration; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Component; +import java.awt.im.spi.InputMethod; +import java.awt.im.spi.InputMethodDescriptor; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; + +/** + * Provides a context for controlling input methods and keyboard layouts. + * This class provides the communication layer between the client component, + * and the various locale-dependent text entry input methods that can be used + * for the client. By default, there is one instance per Window, shared among + * all components, but this limits text entry to one component at a time. + * Thus, text components can create their own instance to allow text entry + * in multiple components at a time. + * + * <p>By using the interfaces of {@link java.awt.im.spi}, you can install + * extensions which allow additional input methods. Some of these may use + * platform native input methods, or keyboard layouts provided by the platform. + * Input methods are unavailable if none have been installed and the platform + * has no underlying native input methods. Extensions are installed as jar + * files, usually accessed in the default extension location or specified by + * the -extdir VM flag. The jar must contain a file named + * "META_INF/services/java.awt.im.spi.InputMethodDescriptor" which lists, + * one entry per line in UTF-8 encoding, each class in the jar that implements + * java.awt.im.spi.InputMethodDescriptor. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Component#getInputContext() + * @see Component#enableInputMethods(boolean) + * @since 1.2 + * @status updated to 1.4, but unverified + */ +public class InputContext +{ + /** + * The list of installed input method descriptors. + */ + private static final ArrayList descriptors = new ArrayList(); + static + { + Enumeration e; + try + { + e = ClassLoader.getSystemResources + ("META_INF/services/java.awt.im.spi.InputMethodDescriptor"); + } + catch (IOException ex) + { + // XXX Should we do something else? + e = EmptyEnumeration.getInstance(); + } + while (e.hasMoreElements()) + { + URL url = (URL) e.nextElement(); + BufferedReader in; + String line; + try + { + in = new BufferedReader + (new InputStreamReader(url.openConnection().getInputStream(), + "UTF-8")); + line = in.readLine().trim(); + } + catch (IOException ignored) + { + continue; + } + outer: + while (line != null) + { + try + { + if (line.charAt(0) != '#') + { + Class c = Class.forName(line); + descriptors.add((InputMethodDescriptor) c.newInstance()); + } + line = in.readLine().trim(); + } + catch (IOException ex) + { + continue outer; + } + catch (Exception ignored) + { + } + } + } + } + + /** The current input method; null if no input methods are installed. */ + private InputMethod im; + + /** Map of locales to the most recently selected input method. */ + private final HashMap recent = new HashMap(); + + /** The list of acceptable character subsets. */ + private Character.Subset[] subsets; + + /** + * Construct an InputContext. This is protected, so clients must use + * {@link #getInstance()} instead. + */ + protected InputContext() + { + } + + /** + * Returns a new InputContext. + * + * @return a new instance, initialized to the default locale if available + */ + public static InputContext getInstance() + { + InputContext ic = new InputContext(); + ic.selectInputMethod(Locale.getDefault()); + return ic; + } + + /** + * Attempts to select an input method or keyboard layout which supports the + * given locale. This returns true if a locale is available and was selected. + * The following steps are taken in choosing an input method:<ul> + * <li>If the currently selected input method or keyboard layout supports + * the requested locale, it remains selected.</li> + * <li>If there is no input method or keyboard layout available that + * supports the requested locale, the current input method or keyboard + * layout remains selected.</li> + * <li>If the user has previously selected an input method or keyboard + * layout for the requested locale from the user interface, then the most + * recently selected such input method or keyboard layout is reselected.</li> + * <li>Otherwise, an input method or keyboard layout that supports the + * requested locale is selected in an implementation dependent way. This + * implementation chooses the first input method which supports the requested + * locale based on the InputMethodDescriptors loaded from the extensions + * installed on the CLASSPATH.</li> + * </ul> + * + * <p>Before switching away from an input method, any currently uncommitted + * text is committed. Not all host operating systems provide API to + * determine the locale of the currently selected native input method or + * keyboard layout, and to select a native input method or keyboard layout + * by locale. For host operating systems that don't provide such API, + * selectInputMethod assumes that native input methods or keyboard layouts + * provided by the host operating system support only the system's default + * locale. + * + * <p>An example of where this may be called is in a multi-language document, + * when moving the insertion point between sections of different locale, so + * that the user may use the input method appropriate to that section of the + * document. + * + * @param locale the desired new locale + * @return true if the new locale is active + * @throws NullPointerException if locale is null + */ + public boolean selectInputMethod(Locale locale) + { + if (im != null && im.setLocale(locale)) + { + recent.put(locale, im); + return true; + } + InputMethod next = (InputMethod) recent.get(locale); + outer: + if (next != null) + for (int i = 0, limit = descriptors.size(); i < limit; i++) + { + InputMethodDescriptor d = (InputMethodDescriptor) descriptors.get(i); + Locale[] list; + try + { + list = d.getAvailableLocales(); + } + catch (AWTException ignored) + { + continue; + } + for (int j = list.length; --j >= 0; ) + if (locale.equals(list[j])) + { + try + { + next = d.createInputMethod(); + recent.put(locale, next); + } + catch (Exception ignored) + { + continue; + } + } + } + if (next == null) + return false; + // XXX I'm not sure if this does all the necessary steps in the switch. + if (im != null) + { + try + { + next.setCompositionEnabled(im.isCompositionEnabled()); + } + catch (UnsupportedOperationException ignored) + { + } + im.endComposition(); + im.deactivate(false); + im.hideWindows(); + } + im = next; + im.setLocale(locale); + im.setCharacterSubsets(subsets); + return true; + } + + /** + * Returns the current locale of the current input method or keyboard + * layout. Returns null if the input context does not have a current input + * method or keyboard layout or if the current input method's + * {@link InputMethod#getLocale()} method returns null. Not all host + * operating systems provide API to determine the locale of the currently + * selected native input method or keyboard layout. For host operating + * systems that don't provide such API, getLocale assumes that the current + * locale of all native input methods or keyboard layouts provided by the + * host operating system is the system's default locale. + * + * @return the locale of the current input method, or null + * @since 1.3 + */ + public Locale getLocale() + { + return im == null ? null : im.getLocale(); + } + + /** + * Sets the subsets of Unicode characters allowed to be input by the current + * input method, as well as subsequent input methods. The value of null + * implies all characters are legal. Applications should not rely on this + * behavior, since native host input methods may not allow restrictions. + * If no current input method is available, this has no immediate effect. + * + * @param subsets the set of Unicode subsets to accept, or null + */ + public void setCharacterSubsets(Character.Subset[] subsets) + { + this.subsets = subsets; + if (im != null) + im.setCharacterSubsets(subsets); + } + + /** + * Changes the enabled status of the current input method. An input method + * that is enabled for composition interprets incoming events for both + * composition and control purposes, while a disabled input method only + * interprets control commands (including commands to enable itself). + * + * @param enable whether to enable the input method + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support enabling + * @see #isCompositionEnabled() + * @since 1.3 + */ + public void setCompositionEnabled(boolean enable) + { + if (im == null) + throw new UnsupportedOperationException(); + im.setCompositionEnabled(enable); + } + + /** + * Find out if the current input method is enabled. + * + * @return true if the current input method is enabled + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support enabling + * @see #setCompositionEnabled(boolean) + * @since 1.3 + */ + public boolean isCompositionEnabled() + { + if (im == null) + throw new UnsupportedOperationException(); + return im.isCompositionEnabled(); + } + + /** + * Starts a reconversion operation in the current input method. The input + * method gets theh text to reconvert from the client component, using + * {@link InputMethodRequests#getSelectedText(Attribute[])}. Then the + * composed and committed text produced by the operation is sent back to + * the client using a sequence of InputMethodRequests. + * + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support reconversion + * @since 1.3 + */ + public void reconvert() + { + if (im == null) + throw new UnsupportedOperationException(); + im.reconvert(); + } + + /** + * Dispatches an event to the current input method. This is called + * automatically by AWT. If no input method is available, then the event + * will never be consumed. + * + * @param event the event to dispatch + * @throws NullPointerException if event is null + */ + public void dispatchEvent(AWTEvent event) + { + if (im != null) + im.dispatchEvent(event); + } + + /** + * Notifies the input context that a client component has been removed from + * its containment hierarchy, or that input method support has been disabled + * for the component. This method is usually called from the client + * component's {@link Component#removeNotify()} method. Potentially pending + * input from input methods for this component is discarded. If no input + * methods are available, then this method has no effect. + * + * @param client the client component + * @throws NullPointerException if client is null + */ + public void removeNotify(Component client) + { + // XXX What to do with client information? + if (im != null) + { + im.deactivate(false); + im.removeNotify(); + } + } + + /** + * Ends any input composition that may currently be going on in this + * context. Depending on the platform and possibly user preferences, this + * may commit or delete uncommitted text. Any changes to the text are + * communicated to the active component using an input method event. If no + * input methods are available, then this method has no effect. This may + * be called for a variety of reasons, such as when the user moves the + * insertion point in the client text outside the range of the composed text, + * or when text is saved to file. + */ + public void endComposition() + { + if (im != null) + im.endComposition(); + } + + /** + * Disposes of the input context and release the resources used by it. + * Called automatically by AWT for the default input context of each + * Window. If no input methods are available, then this method has no + * effect. + */ + public void dispose() + { + if (im != null) + { + im.deactivate(false); + im.dispose(); + } + } + + /** + * Returns a control object from the current input method, or null. A + * control object provides implementation-dependent methods that control + * the behavior of the input method or obtain information from the input + * method. Clients have to compare the result against known input method + * control object types. If no input methods are available or the current + * input method does not provide an input method control object, then null + * is returned. + * + * @return the control object, or null + */ + public Object getInputMethodControlObject() + { + return im == null ? null : im.getControlObject(); + } +} // class InputContext diff --git a/libjava/classpath/java/awt/im/InputMethodHighlight.java b/libjava/classpath/java/awt/im/InputMethodHighlight.java new file mode 100644 index 00000000000..0d664b19366 --- /dev/null +++ b/libjava/classpath/java/awt/im/InputMethodHighlight.java @@ -0,0 +1,185 @@ +/* InputMethodHighlight.java -- highlights the current text selection + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +import java.util.Map; + +/** + * This describes the highlight attributes of text composed in an input method. + * The description includes an abstract level (whether text has been converted + * yet, and whether it is selected), and a concrete level (which style + * attributes are used in rendering). If no concrete level is defined, the + * renderer should use + * {@link Toolkit#mapInputMethodHighlight(InputMethodHighlight)}. An example + * of conversion state is kana -> kanji. + * + * <p>Instances of this class are typically used in + * AttributedCharacterIterators, and may be wrapped in Annotations to separate + * text segments. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see AttributedCharacterIterators + * @see Annotation + * @since 1.2 + * @status updated to 1.4 + */ +public class InputMethodHighlight +{ + /** Raw text state (before conversion). */ + public static final int RAW_TEXT = 0; + + /** Converted text state (after conversion). */ + public static final int CONVERTED_TEXT = 1; + + /** Default do-nothing highlighting for unselected raw text. */ + public static final InputMethodHighlight UNSELECTED_RAW_TEXT_HIGHLIGHT + = new InputMethodHighlight(false, RAW_TEXT); + + /** Default do-nothing highlighting for selected raw text. */ + public static final InputMethodHighlight SELECTED_RAW_TEXT_HIGHLIGHT + = new InputMethodHighlight(true, RAW_TEXT); + + /** Default do-nothing highlighting for unselected converted text. */ + public static final InputMethodHighlight UNSELECTED_CONVERTED_TEXT_HIGHLIGHT + = new InputMethodHighlight(false, CONVERTED_TEXT); + + /** Default do-nothing highlighting for selected converted text. */ + public static final InputMethodHighlight SELECTED_CONVERTED_TEXT_HIGHLIGHT + = new InputMethodHighlight(true, CONVERTED_TEXT); + + /** Whether the highlighting applies to selected text. */ + private final boolean selected; + + /** The state of highlighted text. */ + private final int state; + + /** Any variation on the highlighting style. */ + private final int variation; + + /** The unmodifiable map of rendering styles. */ + private final Map style; + + /** + * Create an input method highlight style, with variation 0 and null style + * mapping. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @throws IllegalArgumentException if state is invalid + */ + public InputMethodHighlight(boolean selected, int state) + { + this(selected, state, 0, null); + } + + /** + * Create an input method highlight style, with null style mapping. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @param variation the style variation + * @throws IllegalArgumentException if state is invalid + */ + public InputMethodHighlight(boolean selected, int state, int variation) + { + this(selected, state, variation, null); + } + + /** + * Create an input method highlight style. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @param variation the style variation + * @param style an unmodifiable map of rendering styles, or null + * @throws IllegalArgumentException if state is invalid + * @since 1.3 + */ + public InputMethodHighlight(boolean selected, int state, int variation, + Map style) + { + if (state != RAW_TEXT && state != CONVERTED_TEXT) + throw new IllegalArgumentException(); + this.selected = selected; + this.state = state; + this.variation = variation; + this.style = style; + } + + /** + * Return whether the highlighting applies to selected text. + * + * @return the selection status + */ + public boolean isSelected() + { + return selected; + } + + /** + * Return the conversion state of the highlighted text. + * + * @return one of {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + */ + public int getState() + { + return state; + } + + /** + * Return the highlighting style variation. + * + * @return the variation + */ + public int getVariation() + { + return variation; + } + + /** + * Return the rendering style attributes map, or null if it should be the + * default mapping. + * + * @return the style map + * @since 1.3 + */ + public Map getStyle() + { + return style; + } +} // class InputMethodHighlight diff --git a/libjava/classpath/java/awt/im/InputMethodRequests.java b/libjava/classpath/java/awt/im/InputMethodRequests.java new file mode 100644 index 00000000000..d50ec33c5c8 --- /dev/null +++ b/libjava/classpath/java/awt/im/InputMethodRequests.java @@ -0,0 +1,153 @@ +/* InputMethodRequests.java -- handles text insertion via input methods + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +import java.awt.Rectangle; +import java.awt.font.TextHitInfo; +import java.text.AttributedCharacterIterator; +import java.text.AttributedCharacterIterator.Attribute; + +/** + * This interface handles requests made by input methods on text editing + * components. A component must specify a handler for input methods that + * implements this interface, and which supports one of two user interfaces: + * <ul><li><em>on-the-spot</em>: composed text is shown in place</li> + * <li><em>below-the-spot</em>: composed text is in a separate window, + * usually below the main text window, until it is committed into place at + * the insertion point, overwriting any selected text</li></ul> + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Component#getInputMethodRequests() + * @see InputMethodListener + * @since 1.2 + * @status updated to 1.4 + */ +public interface InputMethodRequests +{ + /** + * Gets the location of a given offset of the text. This can be used to + * position a composition window near the location of where the composed + * text will be inserted. + * + * <p>If the component has composed text (from the most recent + * InputMethodEvent), then offset 0 indicates the location of the first + * character of this composed text. Otherwise, the offset is ignored, and + * the location should be the beginning of the final line of selected + * text (in horizontal left-to-right text, like English, this would be the + * lower left corner of the selction; in vertical top-to-bottom text, like + * Chinese, this would be the top right corner of the selection). + * + * <p>The location returned is a 0-thickness caret (either horizontal or + * vertical, depending on text flow), mapped to absolute screen coordinates. + * + * @param offset offset within composed text, or null + * @return the screen location of the caret at the offset + */ + Rectangle getTextLocation(TextHitInfo offset); + + /** + * Get the text offset for the given screen coordinate. The offset is + * relative to the composed text, and the return is null if it is outside + * the range of composed text. For example, this can be used to find + * where a mouse click should pop up a text composition window. + * + * @param x the x screen coordinate + * @param y the y screen coordinate + * @return a text hit info describing the composed text offset + */ + TextHitInfo getLocationOffset(int x, int y); + + /** + * Gets the offset where the committed text exists in the text editing + * component. This can be used to examine the text surrounding the insert + * position. + * + * @return the offset of the insert position + */ + int getInsertPositionOffset(); + + /** + * Gets an interator which provides access to the text and its attributes, + * except for the uncommitted text. The input method may provide a list of + * attributes it is interested in; and the iterator need not provide + * information on the remaining attributes. If the attribute list is null, + * the iterator must list all attributes. + * + * @param beginIndex the index of the first character in the iteration + * @param endIndex the index of the last character in the iteration + * @param attributes a list of attributes interested in, or null + * @return an iterator over the region of text with its attributes + */ + AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex, + Attribute[] attributes); + + /** + * Gets the length of committed text. + * + * @return the number of committed characters + */ + int getCommittedTextLength(); + + /** + * Gets the latest committed text, and removes it from the component's text + * body. This allows an input method to provide an "Undo" command. In + * general, this should only be supported immediately after a commit, and + * not when other actions intervene; if not supported, simply return null. + * The input method may provide a list of attributes it is interested in; + * and the iterator need not provide information on the remaining attributes. + * If the attribute list is null, the iterator must list all attributes. + * + * @param attributes a list of attributes interested in, or null + * @return the latest committed text, or null + */ + AttributedCharacterIterator cancelLatestCommittedText + (Attribute[] attributes); + + /** + * Gets the currently selected text. One use of this is to implement a + * "Reconvert" feature in an input method, which modifies the selection + * based on the text in the composition window. The input method may + * provide a list of attributes it is interested in; and the iterator need + * not provide information on the remaining attributes. If the attribute + * list is null, the iterator must list all attributes. + * + * @param attributes a list of attributes interested in, or null + * @return the current selection + */ + AttributedCharacterIterator getSelectedText(Attribute[] attributes); +} // interface InputMethodRequests diff --git a/libjava/classpath/java/awt/im/InputSubset.java b/libjava/classpath/java/awt/im/InputSubset.java new file mode 100644 index 00000000000..5e7d58e7f42 --- /dev/null +++ b/libjava/classpath/java/awt/im/InputSubset.java @@ -0,0 +1,129 @@ +/* InputSubset.java -- subsets of Unicode important in text input + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +/** + * Defines additional Unicode character blocks for use by input methods. + * These constants encompass several Unicode blocks, or portions thereof, for + * simplification over {@link Character.UnicodeBlock}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class InputSubset extends Character.Subset +{ + /** + * Constant for all Latin characters, including the characters in the + * BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A, LATIN_EXTENDED_B + * Unicode character blocks. + */ + public static final InputSubset LATIN = new InputSubset("LATIN"); + + /** + * Constant for the digits included in the BASIC_LATIN Unicode character + * block. + */ + public static final InputSubset LATIN_DIGITS + = new InputSubset("LATIN_DIGITS"); + + /** + * Constant for all Han characters used in writing Traditional Chinese, + * including a subset of the CJK unified ideographs as well as Traditional + * Chinese Han characters that may be defined as surrogate characters. + */ + public static final InputSubset TRADITIONAL_HANZI + = new InputSubset("TRADITIONAL_HANZI"); + + /** + * Constant for all Han characters used in writing Simplified Chinese, + * including a subset of the CJK unified ideographs as well as Simplified + * Chinese Han characters that may be defined as surrogate characters. + */ + public static final InputSubset SIMPLIFIED_HANZI + = new InputSubset("SIMPLIFIED_HANZI"); + + /** + * Constant for all Han characters used in writing Japanese, including a + * subset of the CJK unified ideographs as well as Japanese Han characters + * that may be defined as surrogate characters. + */ + public static final InputSubset KANJI = new InputSubset("KANJI"); + + /** + * Constant for all Han characters used in writing Korean, including a + * subset of the CJK unified ideographs as well as Korean Han characters + * that may be defined as surrogate characters. + */ + public static final InputSubset HANJA = new InputSubset("HANJA"); + + /** + * Constant for the halfwidth katakana subset of the Unicode halfwidth and + * fullwidth forms character block. + */ + public static final InputSubset HALFWIDTH_KATAKANA + = new InputSubset("HALFWIDTH_KATAKANA"); + + /** + * Constant for the fullwidth ASCII variants subset of the Unicode + * halfwidth and fullwidth forms character block. + * + * @since 1.3 + */ + public static final InputSubset FULLWIDTH_LATIN + = new InputSubset("FULLWIDTH_LATIN"); + + /** + * Constant for the fullwidth digits included in the Unicode halfwidth and + * fullwidth forms character block. + * + * @since 1.3 + */ + public static final InputSubset FULLWIDTH_DIGITS + = new InputSubset("FULLWIDTH_DIGITS"); + + /** + * Construct a subset. + * + * @param name the subset name + */ + private InputSubset(String name) + { + super(name); + } +} // class InputSubset diff --git a/libjava/classpath/java/awt/im/package.html b/libjava/classpath/java/awt/im/package.html new file mode 100644 index 00000000000..895da66d5d9 --- /dev/null +++ b/libjava/classpath/java/awt/im/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.im package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.im</title></head> + +<body> +<p>Support for text input methods.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/im/spi/InputMethod.java b/libjava/classpath/java/awt/im/spi/InputMethod.java new file mode 100644 index 00000000000..840d193a8d8 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethod.java @@ -0,0 +1,240 @@ +/* InputMethod.java -- defines an interface for complex text input + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im.spi; + +import java.awt.AWTEvent; +import java.awt.Rectangle; +import java.util.Locale; + +/** + * This interface supports complex text input, often for situations where + * the text is more complex than a keyboard will accomodate. For example, + * this can be used for Chinese, Japanese, and Korean, where multiple + * keystrokes are necessary to compose text. This could also support things + * like phonetic English, or reordering Thai. + * + * <p>These contexts can be loaded by the input method framework, using + * {@link InputContext#selectInputMethod(Locale)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethod +{ + /** + * Set the input method context, which ties the input method to a client + * component. This is called once automatically when creating the input + * method. + * + * @param context the context for this input method + * @throws NullPointerException if context is null + */ + void setInputMethodContext(InputMethodContext context); + + /** + * Sets the input locale. If the input method supports that locale, it + * changes its behavior to be consistent with the locale and returns true. + * Otherwise, it returns false. This is called by + * {@link InputContext#selectInputMethod(Locale)} when the user specifies + * a locale, or when the previously selected input method had a locale. + * + * @param locale the locale to use for input + * @return true if the change is successful + * @throws NullPointerException if locale is null + */ + boolean setLocale(Locale locale); + + /** + * Returns the current input locale, or null if none is defined. This is + * called by {@link InputContext#getLocale()}, or before switching input + * methods. + * + * @return the current input locale, or null + */ + Locale getLocale(); + + /** + * Sets the allowed Unicode subsets that this input method can use. Null + * indicates that all characters are allowed. This is called after creation, + * or when switching to this input method, by + * {@link InputContext#setCharacterSubsets(Character.Subset[])}. + * + * @param subsets the accepted subsets for this input method, or null for all + */ + void setCharacterSubsets(Character.Subset[] subsets); + + /** + * Changes the enabled status of this input method. An enabled input method + * accepts incoming events for composition and control purposes, while a + * disabled input method ignores events (except for control purposes). This + * is called by {@link InputContext#setCompositionEnabled(boolean)} or when + * switching from an input method if the previous input method returned + * without exception on {@link #isCompositionEnabled()}. + * + * @param enable whether to enable this input method + * @throws UnsupportedOperationException if enabling/disabling is unsupported + * @see #isCompositionEnabled() + */ + void setCompositionEnabled(boolean enable); + + /** + * Find out if this input method is enabled. This is called by + * {@link InputContext#isCompositionEnabled()}, or when switching input + * methods via {@link InputContext#selectInputMethod(Locale)}. + * + * @return true if this input method is enabled + * @throws UnsupportedOperationException if enabling/disabling is unsupported + * @see #setCompositionEnabled(boolean) + */ + boolean isCompositionEnabled(); + + /** + * Starts a reconversion operation. The input method gets its text from the + * client, using {@link InputMethodRequests#getSelectedText(Attribute[])}. + * Then the composed and committed text produced by the operation is sent + * back to the client using a sequence of InputMethodEvents. This is called + * by {@link InputContext#reconvert()}. + * + * @throws UnsupportedOperationException if reconversion is unsupported + */ + void reconvert(); + + /** + * Dispatch an event to the input method. If input method support is enabled, + * certain events are dispatched to the input method before the client + * component or event listeners. The input method must either consume the + * event or pass it on to the component. Instances of InputEvent, including + * KeyEvent and MouseEvent, are given to this input method. This method is + * called by {@link InputContext#dispatchEvent(AWTEvent)}. + * + * @param event the event to dispatch + * @throws NullPointerException if event is null + */ + void dispatchEvent(AWTEvent event); + + /** + * Notify this input method of changes in the client window. This is called + * when notifications are enabled (see {@link + * InputMethodContext#enableClientWindowNotification(InputMethod, boolean)}, + * if {@link #removeNotify(Component)} has not been called. The following + * situations trigger a notification:<ul> + * <li>The client window changes in location, size, visibility, + * iconification, or is closed.</li> + * <li>When enabling client notification (or on the first activation after + * enabling if no client existed at the time).</li> + * <li>When activating a new client after <code>removeNotify</code> was + * called on a previous client.</li> + * </ul> + * + * @param bounds the client window's current bounds, or null + */ + void notifyClientWindowChange(Rectangle bounds); + + /** + * Activate this input method for input processing. If the input method + * provides its own windows, it should make them open and visible at this + * time. This method is called when a client component receives a + * FOCUS_GAINED event, or when switching to this input method from another + * one. It is only called when the input method is inactive, assuming that + * new instances begin in an inactive state. + */ + void activate(); + + /** + * Deactivate this input method, either temporarily or permanently for the + * given client. If the input method provides its own windows, it should + * only close those related to the current composition (such as a lookup + * choice panel), while leaving more persistant windows (like a control + * panel) open to avoid screen flicker. Before control is given to another + * input method, {@link #hideWindows()} will be called on this instance. + * This method is called when a client component receives a + * FOCUS_LOST event, when switching to another input method, or before + * {@link #removeNotify()} when the client is removed. + * + * @param isTemporary true if the focus change is temporary + */ + void deactivate(boolean isTemporary); + + /** + * Close or hide all windows opened by this input method. This is called + * before activating a different input method, and before calling + * {@link #dispose()} on this instance. It is only called when the input + * method is inactive. + */ + void hideWindows(); + + /** + * Notify the input method that a client component has been removed from its + * hierarchy, or that input method support has been disabled. This is + * called by {@link InputContext#removeNotify()}, and only when the input + * method is inactive. + */ + void removeNotify(); + + /** + * End any input composition currently taking place. Depending on the + * platform and user preferences, this may commit or delete uncommitted text, + * using input method events. This may be called for a variety of reasons, + * such as when the user moves the insertion point in the client text outside + * the range of the composed text, or when text is saved to file. This is + * called by {@link InputContext#endComposition()}, when switching to a + * new input method, or by {@link InputContext#selectInputMethod(Locale)}. + */ + void endComposition(); + + /** + * Disposes the input method and release any resources it is using. In + * particular, the input method should dispose windows and close files. This + * is called by {@link InputContext#dispose()}, when the input method is + * inactive; and nothing will be called on this instance afterwards. + */ + void dispose(); + + /** + * Returns a control object from this input method, or null. A control object + * provides method to control the behavior of this input method, as well as + * query information about it. The object is implementation dependent, so + * clients must compare the result against known input method control + * object types. This is called by + * {@link InputContext#getInputMethodControlObject()}. + * + * @return the control object, or null + */ + Object getControlObject(); +} // interface InputMethod diff --git a/libjava/classpath/java/awt/im/spi/InputMethodContext.java b/libjava/classpath/java/awt/im/spi/InputMethodContext.java new file mode 100644 index 00000000000..43bee8d8617 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethodContext.java @@ -0,0 +1,123 @@ +/* InputMethodContext.java -- communication between an input method and client + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.im.spi; + +import java.awt.Window; +import java.awt.font.TextHitInfo; +import java.awt.im.InputMethodRequests; +import java.text.AttributedCharacterIterator; + +import javax.swing.JFrame; + +/** + * Provides methods for the communication context between an input method + * and the client component. This should be passed to + * {@link InputMethod#setInputMethodContext(InputMethodContext)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethodContext extends InputMethodRequests +{ + /** + * Create an input method event and dispatch it to the client. + * + * @param id the event type + * @param text an iterator over the text to be committed + * @param count the count of characters to be committed + * @param caret the insertion point of the commit, or null + * @param visiblePosition the best location to make visible, or null + */ + void dispatchInputMethodEvent(int id, AttributedCharacterIterator text, + int count, TextHitInfo caret, + TextHitInfo visiblePosition); + + /** + * Creates a top-level window for use by the input method. This window should + * float above all document windows and dialogs, not receive focus, and have + * lightweight decorations (such as no title, reduced drag regions). But + * this behavior may be modified to meet the platform style. The title may + * or may not be displayed, depending on the platform. + * + * <p>If attachToInputContext is true, the new window will share the input + * context of the input method, so that events in the new window are + * dispatched to the input method. Also, this supresses deactivate and + * activate calls to the input method caused by setVisible. + * + * @param title the window title, if one is displayed; null becomes "" + * @param attachToInputContext true for the window to share context with + * the input method + * @return the new window for use by the input method + * @throws HeadlessException if GraphicsEnvironment.isHeadless is true + */ + Window createInputMethodWindow(String title, boolean attachToInputContext); + + /** + * Creates a top-level Swing JFrame for use by the input method. This frame + * should float above all document windows and dialogs, not receive focus, + * and have lightweight decorations (such as no title, reduced drag + * regions). But this behavior may be modified to meet the platform style. + * The title may or may not be displayed, depending on the platform. + * + * <p>If attachToInputContext is true, the new window will share the input + * context of the input method, so that events in the new window are + * dispatched to the input method. Also, this supresses deactivate and + * activate calls to the input method caused by setVisible. + * + * @param title the window title, if one is displayed; null becomes "" + * @param attachToInputContext true for the window to share context with + * the input method + * @return the new window for use by the input method + * @throws HeadlessException if GraphicsEnvironment.isHeadless is true + * @since 1.4 + */ + JFrame createInputMethodJFrame(String title, boolean attachToInputContext); + + /** + * Sets whether notification of the client window's location and state should + * be enabled for the input method. When enabled, the input method's + * {@link #notifyClientWindowChange(Rectangle)} method is called. + * Notification is automatically disabled when the input method is disposed. + * + * @param inputMethod the method to change status of + * @param enable true to enable notification + */ + void enableClientWindowNotification(InputMethod inputMethod, boolean enable); +} // interface InputMethodContext diff --git a/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java b/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java new file mode 100644 index 00000000000..093d7319217 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java @@ -0,0 +1,113 @@ +/* InputMethodDescriptor.java -- enables loading and use of an input method + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im.spi; + +import java.awt.AWTException; +import java.awt.Image; +import java.util.Locale; + +/** + * This interface provides information about an InputMethod before it is + * loaded. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethodDescriptor +{ + /** + * Returns the locales supported by the input method this describes. This + * allows the selection of input methods by locale (by language only, or + * also by country and variant), via + * {@link InputContext#selectInputMethod(Locale)}. The returned list should + * ignore pass-through locales, so it is usually a subset of locales for + * which {@link InputMethod#setContext(Locale)} returns true. If + * {@link #hasDynamicLocaleList()} returns true, this is called each time + * information is needed, allowing dynamic addition or removal of supported + * locales. + * + * @return the list of supported locales + * @throws AWTException if the input method is not available + */ + Locale[] getAvailableLocales() throws AWTException; + + /** + * Test whether the input method this describes has a static or dynamic + * locale list. For example, this would return true if the list of supported + * locales depends on adapters currently loaded over a network. + * + * @return true if the locale list is dynamic + */ + boolean hasDynamicLocaleList(); + + /** + * Returns a user visible name of the input locale, displayed in the + * specified locale. The inputLocale parameter must be one obtained from + * the list in {@link #getAvailableLocales()}, or null for a + * locale-independent description of the input method. If a translation to + * the desired display language is not available, another language may be + * used. + * + * @param inputLocale the locale of the input method, or null + * @param displayLanguage the language of the result + * @return the name of the input method when using the given inputLocale + */ + String getInputMethodDisplayName(Locale inputLocale, + Locale displayLanguage); + + /** + * Returns a 16x16 icon for the input locale. The inputLocale parameter + * must be one obtained from the list in {@link #getAvailableLocales()}, or + * null for a locale-independent icon for the input method. + * + * @param inputLocale the locale of the input method, or null + * @return a 16x16 icon for the input method when using the given inputLocale + */ + Image getInputMethodIcon(Locale inputLocale); + + /** + * Creates a new instance of the input method. + * + * @return the newly created input method + * @throws Exception if anything goes wrong + */ + InputMethod createInputMethod() throws Exception; + +} // interface InputMethodDescriptor + diff --git a/libjava/classpath/java/awt/im/spi/package.html b/libjava/classpath/java/awt/im/spi/package.html new file mode 100644 index 00000000000..c526ee1590d --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.im.spi package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.im.spi</title></head> + +<body> +<p>Interfaces for implementation of text input methods.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/image/AffineTransformOp.java b/libjava/classpath/java/awt/image/AffineTransformOp.java new file mode 100644 index 00000000000..f11066e4e3d --- /dev/null +++ b/libjava/classpath/java/awt/image/AffineTransformOp.java @@ -0,0 +1,375 @@ +/* AffineTransformOp.java -- This class performs affine + transformation between two images or rasters in 2 dimensions. + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * This class performs affine transformation between two images or + * rasters in 2 dimensions. + * + * @author Olga Rodimina (rodimina@redhat.com) + */ +public class AffineTransformOp implements BufferedImageOp, RasterOp +{ + public static final int TYPE_NEAREST_NEIGHBOR = 1; + + public static final int TYPE_BILINEAR = 2; + + /** + * @since 1.5.0 + */ + public static final int TYPE_BICUBIC = 3; + + private AffineTransform transform; + private RenderingHints hints; + + /** + * Construct AffineTransformOp with the given xform and interpolationType. + * Interpolation type can be TYPE_BILINEAR, TYPE_BICUBIC or + * TYPE_NEAREST_NEIGHBOR. + * + * @param xform AffineTransform that will applied to the source image + * @param interpolationType type of interpolation used + */ + public AffineTransformOp (AffineTransform xform, int interpolationType) + { + this.transform = xform; + if (xform.getDeterminant() == 0) + throw new ImagingOpException(null); + + switch (interpolationType) + { + case TYPE_BILINEAR: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + break; + case TYPE_BICUBIC: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + break; + default: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + } + + /** + * Construct AffineTransformOp with the given xform and rendering hints. + * + * @param xform AffineTransform that will applied to the source image + * @param hints rendering hints that will be used during transformation + */ + public AffineTransformOp (AffineTransform xform, RenderingHints hints) + { + this.transform = xform; + this.hints = hints; + if (xform.getDeterminant() == 0) + throw new ImagingOpException(null); + } + + /** + * Creates empty BufferedImage with the size equal to that of the + * transformed image and correct number of bands. The newly created + * image is created with the specified ColorModel. + * If the ColorModel is equal to null, then image is created + * with the ColorModel of the source image. + * + * @param src source image + * @param destCM color model for the destination image + * @return new compatible destination image + */ + public BufferedImage createCompatibleDestImage (BufferedImage src, + ColorModel destCM) + { + + // if destCm is not specified, use color model of the source image + + if (destCM == null) + destCM = src.getColorModel (); + + return new BufferedImage (destCM, + createCompatibleDestRaster (src.getRaster ()), + src.isAlphaPremultiplied (), + null); + + } + + /** + * Creates empty WritableRaster with the size equal to the transformed + * source raster and correct number of bands + * + * @param src source raster + * @throws RasterFormatException if resulting width or height of raster is 0 + * @return new compatible raster + */ + public WritableRaster createCompatibleDestRaster (Raster src) + { + Rectangle rect = (Rectangle) getBounds2D (src); + + // throw RasterFormatException if resulting width or height of the + // transformed raster is 0 + + if (rect.getWidth () == 0 || rect.getHeight () == 0) + throw new RasterFormatException("width or height is 0"); + + return src.createCompatibleWritableRaster ((int) rect.getWidth (), + (int) rect.getHeight ()); + } + + /** + * Transforms source image using transform specified at the constructor. + * The resulting transformed image is stored in the destination image. + * + * @param src source image + * @param dst destination image + * @return transformed source image + */ + public final BufferedImage filter (BufferedImage src, BufferedImage dst) + { + + if (dst == src) + throw new IllegalArgumentException ("src image cannot be the same as the dst image"); + + // If the destination image is null, then BufferedImage is + // created with ColorModel of the source image + + if (dst == null) + dst = createCompatibleDestImage(src, src.getColorModel ()); + + // FIXME: Must check if color models of src and dst images are the same. + // If it is not, then source image should be converted to color model + // of the destination image + + Graphics2D gr = (Graphics2D) dst.createGraphics (); + gr.setRenderingHints (hints); + gr.drawImage (src, transform, null); + return dst; + + } + + /** + * Transforms source raster using transform specified at the constructor. + * The resulting raster is stored in the destination raster. + * + * @param src source raster + * @param dst destination raster + * @return transformed raster + */ + public final WritableRaster filter (Raster src, WritableRaster dst) + { + if (dst == src) + throw new IllegalArgumentException("src image cannot be the same as" + + " the dst image"); + + if (dst == null) + dst = createCompatibleDestRaster(src); + + if (src.getNumBands() != dst.getNumBands()) + throw new IllegalArgumentException("src and dst must have same number" + + " of bands"); + + double[] dpts = new double[dst.getWidth() * 2]; + double[] pts = new double[dst.getWidth() * 2]; + for (int x = 0; x < dst.getWidth(); x++) + { + dpts[2 * x] = x + dst.getMinX(); + dpts[2 * x + 1] = x; + } + Rectangle srcbounds = src.getBounds(); + if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) + { + for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) + { + try { + transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); + } catch (NoninvertibleTransformException e) { + // Can't happen since the constructor traps this + e.printStackTrace(); + } + + for (int x = 0; x < dst.getWidth(); x++) + { + if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) + continue; + dst.setDataElements(x + dst.getMinX(), y, + src.getDataElements((int)pts[2 * x], + (int)pts[2 * x + 1], + null)); + } + } + } + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + { + double[] tmp = new double[4 * src.getNumBands()]; + for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) + { + try { + transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); + } catch (NoninvertibleTransformException e) { + // Can't happen since the constructor traps this + e.printStackTrace(); + } + + for (int x = 0; x < dst.getWidth(); x++) + { + if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) + continue; + int xx = (int)pts[2 * x]; + int yy = (int)pts[2 * x + 1]; + double dx = (pts[2 * x] - xx); + double dy = (pts[2 * x + 1] - yy); + + // TODO write this more intelligently + if (xx == src.getMinX() + src.getWidth() - 1 || + yy == src.getMinY() + src.getHeight() - 1) + { + // bottom or right edge + Arrays.fill(tmp, 0); + src.getPixel(xx, yy, tmp); + } + else + { + // Normal case + src.getPixels(xx, yy, 2, 2, tmp); + for (int b = 0; b < src.getNumBands(); b++) + tmp[b] = dx * dy * tmp[b] + + (1 - dx) * dy * tmp[b + src.getNumBands()] + + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()] + + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()]; + } + dst.setPixel(x, y, tmp); + } + } + } + else + { + // Bicubic + throw new UnsupportedOperationException("not implemented yet"); + } + + return dst; + } + + /** + * Transforms source image using transform specified at the constructor and + * returns bounds of the transformed image. + * + * @param src image to be transformed + * @return bounds of the transformed image. + */ + public final Rectangle2D getBounds2D (BufferedImage src) + { + return getBounds2D (src.getRaster()); + } + + /** + * Returns bounds of the transformed raster. + * + * @param src raster to be transformed + * @return bounds of the transformed raster. + */ + public final Rectangle2D getBounds2D (Raster src) + { + // determine new size for the transformed raster. + // Need to calculate transformed coordinates of the lower right + // corner of the raster. The upper left corner is always (0,0) + + double x2 = (double) src.getWidth () + src.getMinX (); + double y2 = (double) src.getHeight () + src.getMinY (); + Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null); + + Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ()); + return rect.getBounds (); + } + + /** + * Returns interpolation type used during transformations + * + * @return interpolation type + */ + public final int getInterpolationType () + { + if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + return TYPE_BILINEAR; + else + return TYPE_NEAREST_NEIGHBOR; + } + + /** + * Returns location of the transformed source point. The resulting point + * is stored in the dstPt if one is specified. + * + * @param srcPt point to be transformed + * @param dstPt destination point + * @return the location of the transformed source point. + */ + public Point2D getPoint2D (Point2D srcPt, Point2D dstPt) + { + return transform.transform (srcPt, dstPt); + } + + /** + * Returns rendering hints that are used during transformation. + * + * @return rendering hints + */ + public final RenderingHints getRenderingHints () + { + return hints; + } + + /** + * Returns transform used in transformation between source and destination + * image. + * + * @return transform + */ + public final AffineTransform getTransform () + { + return transform; + } +} diff --git a/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java b/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java new file mode 100644 index 00000000000..b9ca1b70758 --- /dev/null +++ b/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java @@ -0,0 +1,127 @@ +/* AreaAveragingScaleFilter.java -- Java class for filtering images + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This filter should produce images which do not have image artifacts + * like broken lines which were originally unbroken. The cost is of + * course speed. Using bi-linear interpolation here against 4 pixel + * points should give the desired results although Sun does not + * specify what the exact algorithm should be. + * <br> + * Currently this filter does nothing and needs to be implemented. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class AreaAveragingScaleFilter extends ReplicateScaleFilter +{ + /** + * Construct an instance of <code>AreaAveragingScaleFilter</code> which + * should be used in conjunction with a <code>FilteredImageSource</code> + * object. + * + * @param width the width of the destination image + * @param height the height of the destination image + */ + public AreaAveragingScaleFilter(int width, int height) { + super(width, height); + } + + /** + * The <code>ImageProducer</code> should call this method with a + * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, + * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, + * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the + * <code>ImageConsumer</code> interface. + * <br> + * FIXME - more than likely Sun's implementation desires + * <code>TOPDOWNLEFTRIGHT</code> order and this method is overloaded here + * in order to assure that mask is part of the hints added to + * the consumer. + * + * @param flags a bit mask of hints + * @see ImageConsumer + */ + public void setHints(int flags) + { + consumer.setHints(flags); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a <code>byte</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an <code>int</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + +} + diff --git a/libjava/classpath/java/awt/image/BandCombineOp.java b/libjava/classpath/java/awt/image/BandCombineOp.java new file mode 100644 index 00000000000..79efb02e713 --- /dev/null +++ b/libjava/classpath/java/awt/image/BandCombineOp.java @@ -0,0 +1,168 @@ +/* Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Filter Raster pixels by applying a matrix. + * + * BandCombineOp applies a matrix to each pixel to produce new pixel values. + * The width of the matrix must be the same or one more than the number of + * bands in the source Raster. If one more, the pixels in the source are + * assumed to contain an implicit 1.0 at the end. + * + * The rows of the matrix are multiplied by the pixel to produce the values + * for the destination. Therefore the destination Raster must contain the + * same number of bands as the number of rows in the filter matrix. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class BandCombineOp implements RasterOp +{ + private RenderingHints hints; + private float[][] matrix; + + /** + * Construct a BandCombineOp. + * + * @param matrix The matrix to filter pixels with. + * @param hints Rendering hints to apply. Ignored. + */ + public BandCombineOp(float[][] matrix, RenderingHints hints) + { + this.matrix = matrix; + this.hints = hints; + } + + /** + * Filter Raster pixels through a matrix. + * + * Applies the Op matrix to source pixes to produce dest pixels. Each row + * of the matrix is multiplied by the src pixel components to produce the + * dest pixel. If matrix is one more than the number of bands in the src, + * the last element is implicitly multiplied by 1, i.e. added to the sum + * for that dest component. + * + * If dest is null, a suitable Raster is created. This implementation uses + * createCompatibleDestRaster. + * + * @param src The source Raster. + * @param dest The destination Raster, or null. + * @returns The destination Raster or an allocated Raster. + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, + *java.awt.image.WritableRaster) + */ + public WritableRaster filter(Raster src, WritableRaster dest) { + if (dest == null) + dest = createCompatibleDestRaster(src); + + // Filter the pixels + float[] spix = new float[matrix[0].length]; + float[] dpix = new float[matrix.length]; + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + { + // In case matrix rows have implicit translation + spix[spix.length - 1] = 1.0f; + src.getPixel(x, y, spix); + for (int i = 0; i < matrix.length; i++) + { + dpix[i] = 0; + for (int j = 0; j < matrix[0].length; j++) + dpix[i] += spix[j] * matrix[i][j]; + } + dest.setPixel(x, y, dpix); + } + + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /** + * Creates a new WritableRaster that can be used as the destination for this + * Op. This implementation creates a Banded Raster with data type FLOAT. + * @see + *java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return Raster.createBandedRaster(DataBuffer.TYPE_FLOAT, src.getWidth(), + src.getHeight(), matrix.length, + new Point(src.getMinX(), src.getMinY())); + } + + /** Return corresponding destination point for source point. + * + * LookupOp will return the value of src unchanged. + * @param src The source point. + * @param dst The destination point. + * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, + *java.awt.geom.Point2D) + */ + public Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) return (Point2D)src.clone(); + dst.setLocation(src); + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public RenderingHints getRenderingHints() + { + return hints; + } + + /** Return the matrix for this Op. */ + public float[][] getMatrix() + { + return matrix; + } + +} diff --git a/libjava/classpath/java/awt/image/BandedSampleModel.java b/libjava/classpath/java/awt/image/BandedSampleModel.java new file mode 100644 index 00000000000..24d315a1c35 --- /dev/null +++ b/libjava/classpath/java/awt/image/BandedSampleModel.java @@ -0,0 +1,548 @@ +/* Copyright (C) 2004, 2005, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/** + * MultiPixelPackedSampleModel provides a single band model that supports + * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit + * per data element. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public final class BandedSampleModel extends ComponentSampleModel +{ + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + private int dataBitOffset; + private int elemBits; + private int numberOfBits; + private int numElems; + + private static int[] createBankArray(int size) + { + int[] result = new int[size]; + for (int i = 0; i < size; i++) + result[i] = i; + return result; + } + + public BandedSampleModel(int dataType, int w, int h, int numBands) + { + this(dataType, w, h, w, createBankArray(numBands), new int[numBands]); + } + + public BandedSampleModel(int dataType, int w, int h, int scanlineStride, + int[] bankIndices, int[] bandOffsets) + { + super(dataType, w, h, 1, scanlineStride, bankIndices, bandOffsets); + } + + public SampleModel createCompatibleSampleModel(int w, int h) + { + // NOTE: blackdown 1.4.1 sets all offsets to 0. Sun's 1.4.2 docs + // disagree. + + // Compress offsets so minimum is 0, others w*scanlineStride + int[] newoffsets = new int[bandOffsets.length]; + int[] order = new int[bandOffsets.length]; + for (int i=0; i < bandOffsets.length; i++) + order[i] = i; + // FIXME: This is N^2, but not a big issue, unless there's a lot of + // bands... + for (int i=0; i < bandOffsets.length; i++) + for (int j=i+1; j < bandOffsets.length; i++) + if (bankIndices[order[i]] > bankIndices[order[j]] + || (bankIndices[order[i]] == bankIndices[order[j]] + && bandOffsets[order[i]] > bandOffsets[order[j]])) + { + int t = order[i]; order[i] = order[j]; order[j] = t; + } + int bank = 0; + int offset = 0; + for (int i=0; i < bandOffsets.length; i++) + { + if (bankIndices[order[i]] != bank) + { + bank = bankIndices[order[i]]; + offset = 0; + } + newoffsets[order[i]] = offset; + offset += w * scanlineStride; + } + + return new BandedSampleModel(dataType, w, h, scanlineStride, bankIndices, newoffsets); + } + + + public SampleModel createSubsetSampleModel(int[] bands) + { + if (bands.length > bankIndices.length) + throw new + RasterFormatException("BandedSampleModel createSubsetSampleModel too" + +" many bands"); + int[] newoff = new int[bands.length]; + int[] newbanks = new int[bands.length]; + for (int i=0; i < bands.length; i++) + { + int b = bands[i]; + newoff[i] = bandOffsets[b]; + newbanks[i] = bankIndices[b]; + } + + return new BandedSampleModel(dataType, width, height, scanlineStride, + newbanks, newoff); + } + + /** + * Extract all samples of one pixel and return in an array of transfer type. + * + * Extracts the pixel at x, y from data and stores samples into the array + * obj. If obj is null, a new array of getTransferType() is created. + * + * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param obj The primitive array to store the pixels into or null to force creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + * @see java.awt.image.SampleModel#getDataElements(int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, + DataBuffer data) + { + int pixel = getSample(x, y, 0, data); + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + { + byte[] b = (byte[])obj; + if (b == null) b = new byte[numBands]; + for (int i=0; i < numBands; i++) + b[i] = (byte)getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + { + short[] b = (short[])obj; + if (b == null) b = new short[numBands]; + for (int i=0; i < numBands; i++) + b[i] = (short)getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_INT: + { + int[] b = (int[])obj; + if (b == null) b = new int[numBands]; + for (int i=0; i < numBands; i++) + b[i] = getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_FLOAT: + { + float[] b = (float[])obj; + if (b == null) b = new float[numBands]; + for (int i=0; i < numBands; i++) + b[i] = getSampleFloat(x, y, i, data); + return b; + } + case DataBuffer.TYPE_DOUBLE: + { + double[] b = (double[])obj; + if (b == null) b = new double[numBands]; + for (int i=0; i < numBands; i++) + b[i] = getSample(x, y, i, data); + return b; + } + + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) iArray = new int[numBands]; + for (int i=0; i < numBands; i++) + iArray[i] = getSample(x, y, i, data); + + return iArray; + } + + /** + * Copy pixels from a region into an array. + * + * Copies the samples of the pixels in the rectangle starting at x, y that + * is w pixels wide and h scanlines high. When there is more than one band, + * the samples stored in order before the next pixel. This ordering isn't + * well specified in Sun's docs as of 1.4.2. + * + * If iArray is null, a new array is allocated, filled, and returned. + * + * @param x The x-coordinate of the pixel rectangle to store in + * <code>iArray</code>. + * @param y The y-coordinate of the pixel rectangle to store in + * <code>iArray</code>. + * @param w The width in pixels of the rectangle. + * @param h The height in pixels of the rectangle. + * @param iArray The int array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + if (iArray == null) iArray = new int[w*h*numBands]; + int outOffset = 0; + int maxX = x + w; + int maxY = y + h; + for (int yy = x; yy < maxY; yy++) + { + for (int xx = x; xx < maxX; xx++) + { + for (int b = 0; b < numBands; b++) + { + int offset = bandOffsets[b] + yy * scanlineStride + xx; + iArray[outOffset++] = + data.getElem(bankIndices[b], offset); + } + } + } + return iArray; + } + + public int getSample(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElem(bankIndices[b], offset); + } + + public float getSampleFloat(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElemFloat(bankIndices[b], offset); + } + + public double getSampleDouble(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElemDouble(bankIndices[b], offset); + } + + /** + * Copy one band's samples from a region into an array. + * + * Copies from one band the samples of the pixels in the rectangle starting + * at x, y that is w pixels wide and h scanlines high. + * + * If iArray is null, a new array is allocated, filled, and returned. + * + * @param x The x-coordinate of the pixel rectangle to store in + * <code>iArray</code>. + * @param y The y-coordinate of the pixel rectangle to store in + * <code>iArray</code>. + * @param w The width in pixels of the rectangle. + * @param h The height in pixels of the rectangle. + * @param b The band to retrieve. + * @param iArray The int array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + */ + public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, + DataBuffer data) + { + if (iArray == null) iArray = new int[w*h]; + int outOffset = 0; + int maxX = x + w; + int maxY = y + h; + for (int yy = y; yy < maxY; yy++) + { + for (int xx = x; xx < maxX; xx++) + { + int offset = bandOffsets[b] + yy * scanlineStride + xx; + iArray[outOffset++] = + data.getElem(bankIndices[b], offset); + } + } + return iArray; + } + + + /** + * Set the pixel at x, y to the value in the first element of the primitive + * array obj. + * + * @param x The x-coordinate of the data elements in <code>obj</code>. + * @param y The y-coordinate of the data elements in <code>obj</code>. + * @param obj The primitive array containing the data elements to set. + * @param data The DataBuffer to store the data elements into. + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int transferType = getTransferType(); + if (getTransferType() != data.getDataType()) + { + throw new IllegalArgumentException("transfer type ("+ + getTransferType()+"), "+ + "does not match data "+ + "buffer type (" + + data.getDataType() + + ")."); + } + + int offset = y * scanlineStride + x; + + try + { + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] in = (byte[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_SHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] in = (short[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferUShort out = (DataBufferUShort) data; + short[] in = (short[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] in = (int[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_FLOAT: + { + DataBufferFloat out = (DataBufferFloat) data; + float[] in = (float[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_DOUBLE: + { + DataBufferDouble out = (DataBufferDouble) data; + double[] in = (double[]) obj; + for (int i=0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + default: + throw new ClassCastException("Unsupported data type"); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While writing data elements" + + ", x="+x+", y="+y+ + ", width="+width+", height="+height+ + ", scanlineStride="+scanlineStride+ + ", offset="+offset+ + ", data.getSize()="+data.getSize()+ + ", data.getOffset()="+data.getOffset()+ + ": " + + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + for (int b=0; b < numBands; b++) + data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + iArray[b]); + } + + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = y * scanlineStride + (x + ww); + for (int b=0; b < numBands; b++) + data.setElem(bankIndices[b], bandOffsets[b] + offset, + iArray[inOffset++]); + } + y++; + } + } + + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + } + + public void setSample(int x, int y, int b, float s, DataBuffer data) + { + data.setElemFloat(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + } + + public void setSample(int x, int y, int b, double s, DataBuffer data) + { + data.setElemDouble(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + } + + public void setSamples(int x, int y, int w, int h, int b, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (byte)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_SHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (short)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (short)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_FLOAT: + case DataBuffer.TYPE_DOUBLE: + break; + default: + throw new ClassCastException("Unsupported data type"); + } + + // Default implementation probably slower for float and double + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + data.setElem(bankIndices[b], offset, iArray[inOffset++]); + } + y++; + } + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i=0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/BufferStrategy.java b/libjava/classpath/java/awt/image/BufferStrategy.java new file mode 100644 index 00000000000..e86aad60fe9 --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferStrategy.java @@ -0,0 +1,124 @@ +/* BufferStrategy.java -- describes image buffering resources + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.BufferCapabilities; +import java.awt.Graphics; + +/** + * This class describes a strategy for managing image buffering + * resources on a Canvas or Window. A given buffer strategy may make + * use of hardware acceleration or take advantage of features of the + * native graphics system. Examples of buffering strategies are + * double or triple buffering using either flipping or blitting. For + * the details of these algorithms see BufferCapabilities. + * + * To use a buffer strategy, you retrieve it from either the current + * GraphicsConfiguration or from the Component on which you'd like to + * draw. Then you can query the strategy's capabilities to make sure + * they're suitable. + * + * If the strategy's capabilities are suitable, you can obtain a + * graphics object and use it to draw with this strategy. Drawing + * with a buffer strategy requires extra care, however. You'll need + * to manually cause the next buffer to be shown on the output device. + * And since buffer strategies are usually implemented with a + * VolatileImage, you must frequently check that the contents of the + * buffer are valid and that the buffer still exists. + * + * A buffer strategy is usually implemented using a VolatileImage. + * + * @see VolatileImage + * @since 1.4 + */ +public abstract class BufferStrategy +{ + /** + * Creates a new buffer strategy. + */ + public BufferStrategy() + { + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return this buffer strategy's capabilities + */ + public abstract BufferCapabilities getCapabilities(); + + /** + * Retrieves a graphics object that can be used to draw using this + * buffer strategy. This method may not be synchronized so be + * careful when calling it from multiple threads. You also must + * manually dispose of this graphics object. + * + * @return a graphics object that can be used to draw using this + * buffer strategy + */ + public abstract Graphics getDrawGraphics(); + + /** + * Returns whether or not the buffer's resources have been reclaimed + * by the native graphics system. If the buffer resources have been + * lost then you'll need to obtain new resources before drawing + * again. For details, see the documentation for VolatileImage. + * + * @return true if the contents were lost, false otherwise + */ + public abstract boolean contentsLost(); + + /** + * Returns whether or not the buffer's resources were re-created and + * cleared to the default background color. If the buffer's + * resources have recently been re-created and initialized then the + * buffer's image may need to be re-rendered. For details, see the + * documentation for VolatileImage. + * + * @return true if the contents were restored, false otherwise + */ + public abstract boolean contentsRestored(); + + /** + * Applies this buffer strategy. In other words, this method brings + * the contents of the back or intermediate buffers to the front + * buffer. + */ + public abstract void show(); +} diff --git a/libjava/classpath/java/awt/image/BufferedImage.java b/libjava/classpath/java/awt/image/BufferedImage.java new file mode 100644 index 00000000000..124b81368e2 --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferedImage.java @@ -0,0 +1,693 @@ +/* BufferedImage.java -- + Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.ComponentDataBlitOp; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; + +/** + * A buffered image always starts at coordinates (0, 0). + * + * The buffered image is not subdivided into multiple tiles. Instead, + * the image consists of one large tile (0,0) with the width and + * height of the image. This tile is always considered to be checked + * out. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class BufferedImage extends Image + implements WritableRenderedImage +{ + public static final int TYPE_CUSTOM = 0, + TYPE_INT_RGB = 1, + TYPE_INT_ARGB = 2, + TYPE_INT_ARGB_PRE = 3, + TYPE_INT_BGR = 4, + TYPE_3BYTE_BGR = 5, + TYPE_4BYTE_ABGR = 6, + TYPE_4BYTE_ABGR_PRE = 7, + TYPE_USHORT_565_RGB = 8, + TYPE_USHORT_555_RGB = 9, + TYPE_BYTE_GRAY = 10, + TYPE_USHORT_GRAY = 11, + TYPE_BYTE_BINARY = 12, + TYPE_BYTE_INDEXED = 13; + + static final int[] bits3 = { 8, 8, 8 }; + static final int[] bits4 = { 8, 8, 8 }; + static final int[] bits1byte = { 8 }; + static final int[] bits1ushort = { 16 }; + + static final int[] masks_int = { 0x00ff0000, + 0x0000ff00, + 0x000000ff, + DataBuffer.TYPE_INT }; + static final int[] masks_565 = { 0xf800, + 0x07e0, + 0x001f, + DataBuffer.TYPE_USHORT}; + static final int[] masks_555 = { 0x7c00, + 0x03e0, + 0x001f, + DataBuffer.TYPE_USHORT}; + + Vector observers; + + public BufferedImage(int w, int h, int type) + { + ColorModel cm = null; + + boolean alpha = false; + boolean premultiplied = false; + switch (type) + { + case TYPE_4BYTE_ABGR_PRE: + case TYPE_INT_ARGB_PRE: + premultiplied = true; + // fall through + case TYPE_INT_ARGB: + case TYPE_4BYTE_ABGR: + alpha = true; + } + + ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); + switch (type) + { + case TYPE_INT_RGB: + case TYPE_INT_ARGB: + case TYPE_INT_ARGB_PRE: + case TYPE_USHORT_565_RGB: + case TYPE_USHORT_555_RGB: + int[] masks = null; + switch (type) + { + case TYPE_INT_RGB: + case TYPE_INT_ARGB: + case TYPE_INT_ARGB_PRE: + masks = masks_int; + break; + case TYPE_USHORT_565_RGB: + masks = masks_565; + break; + case TYPE_USHORT_555_RGB: + masks = masks_555; + break; + } + + cm = new DirectColorModel(cs, + 32, // 32 bits in an int + masks[0], // r + masks[1], // g + masks[2], // b + alpha ? 0xff000000 : 0, + premultiplied, + masks[3] // data type + ); + break; + + case TYPE_INT_BGR: + String msg = + "FIXME: Programmer is confused. Why (and how) does a " + + "TYPE_INT_BGR image use ComponentColorModel to store " + + "8-bit values? Is data type TYPE_INT or TYPE_BYTE. What " + + "is the difference between TYPE_INT_BGR and TYPE_3BYTE_BGR?"; + throw new UnsupportedOperationException(msg); + + case TYPE_3BYTE_BGR: + case TYPE_4BYTE_ABGR: + case TYPE_4BYTE_ABGR_PRE: + case TYPE_BYTE_GRAY: + case TYPE_USHORT_GRAY: + int[] bits = null; + int dataType = DataBuffer.TYPE_BYTE; + switch (type) { + case TYPE_3BYTE_BGR: + bits = bits3; + break; + case TYPE_4BYTE_ABGR: + case TYPE_4BYTE_ABGR_PRE: + bits = bits4; + break; + case TYPE_BYTE_GRAY: + bits = bits1byte; + break; + case TYPE_USHORT_GRAY: + bits = bits1ushort; + dataType = DataBuffer.TYPE_USHORT; + break; + } + cm = new ComponentColorModel(cs, bits, alpha, premultiplied, + alpha ? + Transparency.TRANSLUCENT: + Transparency.OPAQUE, + dataType); + break; + case TYPE_BYTE_BINARY: + byte[] vals = { 0, (byte) 0xff }; + cm = new IndexColorModel(8, 2, vals, vals, vals); + break; + case TYPE_BYTE_INDEXED: + String msg2 = "type not implemented yet"; + throw new UnsupportedOperationException(msg2); + // FIXME: build color-cube and create color model + } + + init(cm, + cm.createCompatibleWritableRaster(w, h), + premultiplied, + null, // no properties + type + ); + } + + public BufferedImage(int w, int h, int type, + IndexColorModel indexcolormodel) + { + if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED)) + throw new IllegalArgumentException("type must be binary or indexed"); + + init(indexcolormodel, + indexcolormodel.createCompatibleWritableRaster(w, h), + false, // not premultiplied (guess) + null, // no properties + type); + } + + public BufferedImage(ColorModel colormodel, + WritableRaster writableraster, + boolean premultiplied, + Hashtable properties) + { + init(colormodel, writableraster, premultiplied, properties, + TYPE_CUSTOM); + // TODO: perhaps try to identify type? + } + + WritableRaster raster; + ColorModel colorModel; + Hashtable properties; + boolean isPremultiplied; + int type; + + private void init(ColorModel cm, + WritableRaster writableraster, + boolean premultiplied, + Hashtable properties, + int type) + { + raster = writableraster; + colorModel = cm; + this.properties = properties; + isPremultiplied = premultiplied; + this.type = type; + } + + //public void addTileObserver(TileObserver tileobserver) {} + + public void coerceData(boolean premultiplied) + { + colorModel = colorModel.coerceData(raster, premultiplied); + } + + public WritableRaster copyData(WritableRaster dest) + { + if (dest == null) + dest = raster.createCompatibleWritableRaster(getMinX(), getMinY(), + getWidth(),getHeight()); + + int x = dest.getMinX(); + int y = dest.getMinY(); + int w = dest.getWidth(); + int h = dest.getHeight(); + + // create a src child that has the right bounds... + WritableRaster src = + raster.createWritableChild(x, y, w, h, x, y, + null // same bands + ); + if (src.getSampleModel () instanceof ComponentSampleModel + && dest.getSampleModel () instanceof ComponentSampleModel) + // Refer to ComponentDataBlitOp for optimized data blitting: + ComponentDataBlitOp.INSTANCE.filter(src, dest); + else + { + // slower path + int samples[] = src.getPixels (x, y, w, h, (int [])null); + dest.setPixels (x, y, w, h, samples); + } + return dest; + } + + public Graphics2D createGraphics() + { + GraphicsEnvironment env; + env = GraphicsEnvironment.getLocalGraphicsEnvironment (); + return env.createGraphics (this); + } + + public void flush() { + } + + public WritableRaster getAlphaRaster() + { + return colorModel.getAlphaRaster(raster); + } + + public ColorModel getColorModel() + { + return colorModel; + } + + public Raster getData() + { + return copyData(null); + /* TODO: this might be optimized by returning the same + raster (not writable) as long as image data doesn't change. */ + } + + public Raster getData(Rectangle rectangle) + { + WritableRaster dest = + raster.createCompatibleWritableRaster(rectangle); + return copyData(dest); + } + + public Graphics getGraphics() + { + return createGraphics(); + } + + public int getHeight() + { + return raster.getHeight(); + } + + public int getHeight(ImageObserver imageobserver) + { + return getHeight(); + } + + public int getMinTileX() + { + return 0; + } + + public int getMinTileY() + { + return 0; + } + + public int getMinX() + { + return 0; + } + + public int getMinY() + { + return 0; + } + + public int getNumXTiles() + { + return 1; + } + + public int getNumYTiles() + { + return 1; + } + + public Object getProperty(String string) + { + if (properties == null) + return null; + return properties.get(string); + } + + public Object getProperty(String string, ImageObserver imageobserver) + { + return getProperty(string); + } + + + public String[] getPropertyNames() + { + // FIXME: implement + return null; + } + + public int getRGB(int x, int y) + { + Object rgbElem = raster.getDataElements(x, y, + null // create as needed + ); + return colorModel.getRGB(rgbElem); + } + + public int[] getRGB(int startX, int startY, int w, int h, + int[] rgbArray, + int offset, int scanlineStride) + { + if (rgbArray == null) + { + /* + 000000000000000000 + 00000[#######----- [ = start + -----########----- ] = end + -----#######]00000 + 000000000000000000 */ + int size = (h-1)*scanlineStride + w; + rgbArray = new int[size]; + } + + int endX = startX + w; + int endY = startY + h; + + /* *TODO*: + Opportunity for optimization by examining color models... + + Perhaps wrap the rgbArray up in a WritableRaster with packed + sRGB color model and perform optimized rendering into the + array. */ + + Object rgbElem = null; + for (int y=startY; y<endY; y++) + { + int xoffset = offset; + for (int x=startX; x<endX; x++) + { + int rgb; + rgbElem = raster.getDataElements(x, y, rgbElem); + rgb = colorModel.getRGB(rgbElem); + rgbArray[xoffset++] = rgb; + } + offset += scanlineStride; + } + return rgbArray; + } + + public WritableRaster getRaster() + { + return raster; + } + + public SampleModel getSampleModel() + { + return raster.getSampleModel(); + } + + public ImageProducer getSource() + { + return new ImageProducer() { + + Vector consumers = new Vector(); + + public void addConsumer(ImageConsumer ic) + { + if(!consumers.contains(ic)) + consumers.add(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return consumers.contains(ic); + } + + public void removeConsumer(ImageConsumer ic) + { + consumers.remove(ic); + } + + public void startProduction(ImageConsumer ic) + { + int x = 0; + int y = 0; + int width = getWidth(); + int height = getHeight(); + int stride = width; + int offset = 0; + int[] pixels = getRGB(x, y, + width, height, + (int[])null, offset, stride); + ColorModel model = getColorModel(); + + consumers.add(ic); + + for(int i=0;i<consumers.size();i++) + { + ImageConsumer c = (ImageConsumer) consumers.elementAt(i); + c.setHints(ImageConsumer.SINGLEPASS); + c.setDimensions(getWidth(), getHeight()); + c.setPixels(x, y, width, height, model, pixels, offset, stride); + c.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + }; + } + + public Vector getSources() + { + return null; + } + + public BufferedImage getSubimage(int x, int y, int w, int h) + { + WritableRaster subRaster = + getRaster().createWritableChild(x, y, w, h, 0, 0, null); + + return new BufferedImage(getColorModel(), + subRaster, + isPremultiplied, + properties); + } + + public Raster getTile(int tileX, int tileY) + { + return getWritableTile(tileX, tileY); + } + + public int getTileGridXOffset() + { + return 0; // according to javadocs + } + + public int getTileGridYOffset() + { + return 0; // according to javadocs + } + + public int getTileHeight() + { + return getHeight(); // image is one big tile + } + + public int getTileWidth() + { + return getWidth(); // image is one big tile + } + + public int getType() + { + return type; + } + + public int getWidth() + { + return raster.getWidth(); + } + + public int getWidth(ImageObserver imageobserver) + { + return getWidth(); + } + + public WritableRaster getWritableTile(int tileX, int tileY) + { + isTileWritable(tileX, tileY); // for exception + return raster; + } + + private static final Point[] tileIndices = { new Point() }; + + public Point[] getWritableTileIndices() + { + return tileIndices; + } + + public boolean hasTileWriters() + { + return true; + } + + public boolean isAlphaPremultiplied() + { + return isPremultiplied; + } + + public boolean isTileWritable(int tileX, int tileY) + { + if ((tileX != 0) || (tileY != 0)) + throw new ArrayIndexOutOfBoundsException("only tile is (0,0)"); + return true; + } + + public void releaseWritableTile(int tileX, int tileY) + { + isTileWritable(tileX, tileY); // for exception + } + + //public void removeTileObserver(TileObserver tileobserver) {} + + public void setData(Raster src) + { + int x = src.getMinX(); + int y = src.getMinY(); + int w = src.getWidth(); + int h = src.getHeight(); + + // create a dest child that has the right bounds... + WritableRaster dest = + raster.createWritableChild(x, y, w, h, x, y, + null // same bands + ); + + if (src.getSampleModel () instanceof ComponentSampleModel + && dest.getSampleModel () instanceof ComponentSampleModel) + + // Refer to ComponentDataBlitOp for optimized data blitting: + ComponentDataBlitOp.INSTANCE.filter(src, dest); + else + { + // slower path + int samples[] = src.getPixels (x, y, w, h, (int [])null); + dest.setPixels (x, y, w, h, samples); + } + } + + public void setRGB(int x, int y, int argb) + { + Object rgbElem = colorModel.getDataElements(argb, null); + raster.setDataElements(x, y, rgbElem); + } + + public void setRGB(int startX, int startY, int w, int h, + int[] argbArray, int offset, int scanlineStride) + { + int endX = startX + w; + int endY = startY + h; + + Object rgbElem = null; + for (int y=startY; y<endY; y++) + { + int xoffset = offset; + for (int x=startX; x<endX; x++) + { + int argb = argbArray[xoffset++]; + rgbElem = colorModel.getDataElements(argb, rgbElem); + raster.setDataElements(x, y, rgbElem); + } + offset += scanlineStride; + } + } + + public String toString() + { + StringBuffer buf; + + buf = new StringBuffer(/* estimated length */ 120); + buf.append("BufferedImage@"); + buf.append(Integer.toHexString(hashCode())); + buf.append(": type="); + buf.append(type); + buf.append(' '); + buf.append(colorModel); + buf.append(' '); + buf.append(raster); + + return buf.toString(); + } + + + /** + * Adds a tile observer. If the observer is already present, it receives + * multiple notifications. + * + * @param to The TileObserver to add. + */ + public void addTileObserver (TileObserver to) + { + if (observers == null) + observers = new Vector (); + + observers.add (to); + } + + /** + * Removes a tile observer. If the observer was not registered, + * nothing happens. If the observer was registered for multiple + * notifications, it is now registered for one fewer notification. + * + * @param to The TileObserver to remove. + */ + public void removeTileObserver (TileObserver to) + { + if (observers == null) + return; + + observers.remove (to); + } +} diff --git a/libjava/classpath/java/awt/image/BufferedImageFilter.java b/libjava/classpath/java/awt/image/BufferedImageFilter.java new file mode 100644 index 00000000000..50d627d66a3 --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferedImageFilter.java @@ -0,0 +1,110 @@ +/* Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.awt.Point; + +/** + * The BufferedImageFilter class wraps BufferedImageOp objects in a Filter. + * + * When pixels are pushed through the filter, we create a BufferedImage, + * apply the BufferedImageOp, and pass the filtered pixels to the base class. + * + * @author jlquinn@optonline.net + */ +public class BufferedImageFilter extends ImageFilter implements Cloneable +{ + private BufferedImageOp op; + + /** + * + */ + public BufferedImageFilter(BufferedImageOp op) + { + super(); + if (op == null) + throw new NullPointerException("BufferedImageFilter null" + + " op in constructor"); + this.op = op; + } + + /** + * @return Returns the contained BufferedImageOp. + */ + public BufferedImageOp getBufferedImageOp() + { + return op; + } + + // FIXME: Definitely not sure this is the right thing. I'm not sure how to + // create a compatible sample model that incorporates scansize != w. I + // asume off is handled by the db itself. + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) + { + // Create an input BufferedImage + DataBufferByte db = new DataBufferByte(pixels, scansize * h + off, off); + SampleModel sm = model.createCompatibleSampleModel(scansize, h); + WritableRaster wr = new WritableRaster(sm, db, new Point(0, 0)); + BufferedImage in = + new BufferedImage(model, wr, model.isAlphaPremultiplied(), null); + BufferedImage out = op.createCompatibleDestImage(in, model); + op.filter(in, out); + DataBuffer dbout = out.getRaster().getDataBuffer(); + super.setPixels(0, 0, w, h, model, ((DataBufferByte)dbout).getData(), 0, + scansize); + } + + // FIXME: Definitely not sure this is the right thing. I'm not sure how + // to create a compatible sample model that incorporates + // scansize != w. I asume off is handled by the db itself. + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) + { + // Create an input BufferedImage + DataBufferInt db = new DataBufferInt(pixels, scansize * h + off, off); + SampleModel sm = model.createCompatibleSampleModel(scansize, h); + WritableRaster wr = new WritableRaster(sm, db, new Point(0, 0)); + BufferedImage in = + new BufferedImage(model, wr, model.isAlphaPremultiplied(), null); + BufferedImage out = op.createCompatibleDestImage(in, model); + op.filter(in, out); + DataBuffer dbout = out.getRaster().getDataBuffer(); + super.setPixels(0, 0, w, h, model, ((DataBufferInt)dbout).getData(), 0, + scansize); + } +} diff --git a/libjava/classpath/java/awt/image/BufferedImageOp.java b/libjava/classpath/java/awt/image/BufferedImageOp.java new file mode 100644 index 00000000000..2ecbec056a0 --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferedImageOp.java @@ -0,0 +1,55 @@ +/* BufferedImageOp.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * NEEDS DOCUMENTATION + */ +public interface BufferedImageOp +{ + BufferedImage filter(BufferedImage src, BufferedImage dst); + Rectangle2D getBounds2D(BufferedImage src); + BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM); + Point2D getPoint2D(Point2D src, Point2D dst); + RenderingHints getRenderingHints(); +} // interface BufferedImageOp diff --git a/libjava/classpath/java/awt/image/ByteLookupTable.java b/libjava/classpath/java/awt/image/ByteLookupTable.java new file mode 100644 index 00000000000..df02d0a1bf7 --- /dev/null +++ b/libjava/classpath/java/awt/image/ByteLookupTable.java @@ -0,0 +1,166 @@ +/* ByteLookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * ByteLookupTable represents translation arrays for pixel values. It wraps + * one or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public class ByteLookupTable extends LookupTable +{ + // Array of translation tables. + private byte data[][]; + + /** + * Creates a new <code>ByteLookupTable</code> instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * tables. If data.length is one, the same table is applied to all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param data Array of lookup tables. + * @exception IllegalArgumentException if offset < 0 or data.length < 1. + */ + public ByteLookupTable(int offset, byte[][] data) + throws IllegalArgumentException + { + super(offset, data.length); + this.data = data; + } + + /** + * Creates a new <code>ByteLookupTable</code> instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * table. The same table is applied to all pixel components. + * + * @param offset Offset to be subtracted. + * @param data Lookup table for all components. + * @exception IllegalArgumentException if offset < 0. + */ + public ByteLookupTable(int offset, byte[] data) + throws IllegalArgumentException + { + super(offset, 1); + this.data = new byte[][] {data}; + } + + /** + * Return the lookup tables. + * + * @return the tables + */ + public final byte[][] getTable() + { + return data; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public int[] lookupPixel(int[] src, int[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new int[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][src[i] - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][src[i] - offset]; + + return dst; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public byte[] lookupPixel(byte[] src, byte[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new byte[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][((int)src[i]) - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][((int)src[i]) - offset]; + + return dst; + + } +} diff --git a/libjava/classpath/java/awt/image/ColorConvertOp.java b/libjava/classpath/java/awt/image/ColorConvertOp.java new file mode 100644 index 00000000000..18609e0c4b0 --- /dev/null +++ b/libjava/classpath/java/awt/image/ColorConvertOp.java @@ -0,0 +1,319 @@ +/* ColorModel.java -- + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * ColorConvertOp is a filter for converting an image from one colorspace to + * another colorspace. The filter can convert the image through a sequence + * of colorspaces or just from source to destination. + * + * Color conversion is done on the color components without alpha. Thus + * if a BufferedImage has alpha premultiplied, this is divided out before + * color conversion, and premultiplication applied if the destination + * requires it. + * + * Color rendering and dithering hints may be applied if specified. This is + * likely platform-dependent. + * + * @author jlquinn@optonline.net + */ +public class ColorConvertOp implements BufferedImageOp, RasterOp +{ + private ColorSpace srccs; + private ColorSpace dstcs; + private RenderingHints hints; + private ICC_Profile[] profiles; + private ColorSpace[] spaces; + private boolean rasterValid; + + + /** + * Convert BufferedImage through a ColorSpace. + * + * This filter version is only valid for BufferedImages. The source image + * is converted to cspace. If the destination is not null, it is then + * converted to the destination colorspace. Normally this filter will only + * be used with a null destination. + * + * @param cspace The target color space. + * @param hints Rendering hints to use in conversion, or null. + */ + public ColorConvertOp(ColorSpace cspace, RenderingHints hints) + { + if (cspace == null) + throw new NullPointerException(); + spaces = new ColorSpace[]{cspace}; + this.hints = hints; + rasterValid = false; + } + + public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, + RenderingHints hints) + { + if (srcCspace == null || dstCspace == null) + throw new NullPointerException(); + spaces = new ColorSpace[]{srcCspace, dstCspace}; + this.hints = hints; + } + + /** + * Convert from a source image destination image color space. + * + * This constructor builds a ColorConvertOp from an array of ICC_Profiles. + * The source image will be converted through the sequence of color spaces + * defined by the profiles. If the sequence of profiles doesn't give a + * well-defined conversion, throws IllegalArgumentException. + * + * NOTE: Sun's docs don't clearly define what a well-defined conversion is + * - or perhaps someone smarter can come along and sort it out. + * + * For BufferedImages, when the first and last profiles match the + * requirements of the source and destination color space respectively, the + * corresponding conversion is unnecessary. TODO: code this up. I don't + * yet understand how you determine this. + * + * For Rasters, the first and last profiles must have the same number of + * bands as the source and destination Rasters, respectively. If this is + * not the case, or there fewer than 2 profiles, an IllegalArgumentException + * will be thrown. + * + * @param profiles + * @param hints + */ + public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints) + { + if (profiles == null) + throw new NullPointerException(); + this.hints = hints; + this.profiles = profiles; + // TODO: Determine if this is well-defined. + // Create colorspace array with space for src and dest colorspace + spaces = new ColorSpace[profiles.length]; + for (int i = 0; i < profiles.length; i++) + spaces[i] = new ICC_ColorSpace(profiles[i]); + } + + /** Convert from source image color space to destination image color space. + * + * Only valid for BufferedImage objects, this Op converts from the source + * color space to the destination color space. The destination can't be + * null for this operation. + * + * @param hints Rendering hints to use during conversion, or null. + */ + public ColorConvertOp(RenderingHints hints) + { + this.hints = hints; + srccs = null; + dstcs = null; + rasterValid = false; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, + java.awt.image.BufferedImage) + */ + public final BufferedImage filter(BufferedImage src, BufferedImage dst) + { + // TODO: The plan is to create a scanline buffer for intermediate buffers. + // For now we just suck it up and create intermediate buffers. + + if (dst == null && spaces.length == 0) + throw new IllegalArgumentException(); + + // Make sure input isn't premultiplied by alpha + if (src.isAlphaPremultiplied()) + { + BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel()); + copyimage(src, tmp); + tmp.coerceData(false); + src = tmp; + } + + ColorModel scm = src.getColorModel(); + for (int i = 0; i < spaces.length; i++) + { + ColorModel cm = scm.cloneColorModel(spaces[i]); + BufferedImage tmp = createCompatibleDestImage(src, cm); + copyimage(src, tmp); + src = tmp; + } + + // Intermediate conversions leave result in src + if (dst == null) + return src; + + // Apply final conversion + copyimage(src, dst); + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + // FIXME: set properties to those in src + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isPremultiplied, + null); + } + + public final ICC_Profile[] getICC_Profiles() + { + return profiles; + } + + /** Return the rendering hints for this op. */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + if (!rasterValid) + throw new IllegalArgumentException(); + + // Need to iterate through each color space - there must be at least 2 + for (int i = 1; i < spaces.length - 1; i++) + { + // FIXME: this is wrong. tmp needs to have the same number of bands as + // spaces[i] has. + WritableRaster tmp = createCompatibleDestRaster(src); + copyraster(src, spaces[i - 1], tmp, spaces[i]); + src = tmp; + } + + // FIXME: this is wrong. dst needs to have the same number of bands as + // spaces[i] has. + if (dest == null) + dest = createCompatibleDestRaster(src); + copyraster(src, spaces[spaces.length - 2], + dest, spaces[spaces.length - 1]); + + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + + /** Return corresponding destination point for source point. + * + * LookupOp will return the value of src unchanged. + * @param src The source point. + * @param dst The destination point. + * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + */ + public final Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) return (Point2D)src.clone(); + dst.setLocation(src); + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public final Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + // According to Sven de Marothy, we need to copy the src into the dest + // using Graphics2D, in order to use the rendering hints. + private void copyimage(BufferedImage src, BufferedImage dst) + { + Graphics2D gg = dst.createGraphics(); + gg.setRenderingHints(hints); + gg.drawImage(src, 0, 0, null); + gg.dispose(); + } + + private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, + ColorSpace dcs) + { + float[] sbuf = new float[src.getNumBands()]; + + if (hints.get(RenderingHints.KEY_COLOR_RENDERING) == + RenderingHints.VALUE_COLOR_RENDER_QUALITY) + { + // use cie for accuracy + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dst.setPixel(x, y, + dcs.fromCIEXYZ(scs.toCIEXYZ(src.getPixel(x, y, sbuf)))); + } + else + { + // use rgb - it's probably faster + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dst.setPixel(x, y, + dcs.fromRGB(scs.toRGB(src.getPixel(x, y, sbuf)))); + } + } + +} diff --git a/libjava/classpath/java/awt/image/ColorModel.java b/libjava/classpath/java/awt/image/ColorModel.java new file mode 100644 index 00000000000..1ebcb98a76b --- /dev/null +++ b/libjava/classpath/java/awt/image/ColorModel.java @@ -0,0 +1,758 @@ +/* ColorModel.java -- + Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.Point; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.lang.reflect.Constructor; +import java.util.Arrays; + +/** + * A color model operates with colors in several formats: + * + * <ul> + * <li>normalized: component samples are in range [0.0, 1.0].</li> + * + * <li>color model pixel value: all the color component samples for a + * sigle pixel packed/encoded in a way natural for the color + * model.</li> + * + * <li>color model pixel int value: only makes sense if the natural + * encoding of a single pixel can fit in a single int value.</li> + * + * <li>array of transferType containing a single pixel: the pixel is + * encoded in the natural way of the color model, taking up as many + * array elements as needed.</li> + * + * <li>sRGB pixel int value: a pixel in sRGB color space, encoded in + * default 0xAARRGGBB format, assumed not alpha premultiplied.</li> + * + * <li>single [0, 255] scaled int samples from default sRGB color + * space. These are always assumed to be alpha non-premultiplied.</li> + * + * <li>arrays of unnormalized component samples of single pixel: these + * samples are scaled and multiplied according to the color model, but + * is otherwise not packed or encoded. Each element of the array is one + * separate component sample. The color model only operate on the + * components from one pixel at a time, but using offsets, allows + * manipulation of arrays that contain the components of more than one + * pixel.</li> + * + * </ul> + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author C. Brian Jones (cbj@gnu.org) + */ +public abstract class ColorModel implements Transparency +{ + protected int pixel_bits; + protected int transferType; + + int[] bits; + ColorSpace cspace; + int transparency; + boolean hasAlpha; + boolean isAlphaPremultiplied; + + static int[] nArray(int value, int times) + { + int[] array = new int[times]; + java.util.Arrays.fill(array, value); + return array; + } + + static byte[] nArray(byte value, int times) + { + byte[] array = new byte[times]; + java.util.Arrays.fill(array, value); + return array; + } + + /** + * Constructs the default color model. The default color model + * can be obtained by calling <code>getRGBdefault</code> of this + * class. + * @param bits the number of bits wide used for bit size of pixel values + */ + public ColorModel(int bits) + { + this(bits * 4, // total bits, sRGB, four channels + nArray(bits, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, + Buffers.smallestAppropriateTransferType(bits * 4)); + } + + /** + * Constructs a ColorModel that translates pixel values to + * color/alpha components. + * + * @exception IllegalArgumentException If the length of the bit array is less + * than the number of color or alpha components in this ColorModel, or if the + * transparency is not a valid value, or if the sum of the number of bits in + * bits is less than 1 or if any of the elements in bits is less than 0. + */ + protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace, + boolean hasAlpha, boolean isAlphaPremultiplied, + int transparency, int transferType) + { + int bits_sum = 0; + for (int i = 0; i < bits.length; i++) + { + if (bits [i] < 0) + throw new IllegalArgumentException (); + + bits_sum |= bits [i]; + } + + if ((bits.length < cspace.getNumComponents()) + || (bits_sum < 1)) + throw new IllegalArgumentException (); + + this.pixel_bits = pixel_bits; + this.bits = bits; + this.cspace = cspace; + this.hasAlpha = hasAlpha; + this.isAlphaPremultiplied = isAlphaPremultiplied; + this.transparency = transparency; + this.transferType = transferType; + } + + // This is a hook for ColorConvertOp to create a colormodel with + // a new colorspace + ColorModel cloneColorModel(ColorSpace cspace) + { + Class cls = this.getClass(); + ColorModel cm; + try { + // This constructor will exist. + Constructor ctor = + cls.getConstructor(new Class[]{int.class, int[].class, + ColorSpace.class, boolean.class, + boolean.class, int.class, int.class}); + cm = (ColorModel)ctor. + newInstance(new Object[]{new Integer(pixel_bits), + bits, cspace, Boolean.valueOf(hasAlpha), + Boolean.valueOf(isAlphaPremultiplied), + new Integer(transparency), + new Integer(transferType)}); + } + catch (Exception e) + { + throw new IllegalArgumentException(); + } + return cm; + } + + public void finalize() + { + // Do nothing here. + } + + /** + * Returns the default color model which in Sun's case is an instance + * of <code>DirectColorModel</code>. + */ + public static ColorModel getRGBdefault() + { + return new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000); + } + + public final boolean hasAlpha() + { + return hasAlpha; + } + + public final boolean isAlphaPremultiplied() + { + return isAlphaPremultiplied; + } + + /** + * Get get number of bits wide used for the bit size of pixel values + */ + public int getPixelSize() + { + return pixel_bits; + } + + public int getComponentSize(int componentIdx) + { + return bits[componentIdx]; + } + + public int[] getComponentSize() + { + return bits; + } + + public int getTransparency() + { + return transparency; + } + + public int getNumComponents() + { + return getNumColorComponents() + (hasAlpha ? 1 : 0); + } + + public int getNumColorComponents() + { + return cspace.getNumComponents(); + } + + /** + * Converts pixel value to sRGB and extract red int sample scaled + * to range [0, 255]. + * + * @param pixel pixel value that will be interpreted according to + * the color model, (assumed alpha premultiplied if color model says + * so.) + * + * @return red sample scaled to range [0, 255], from default color + * space sRGB, alpha non-premultiplied. + */ + public abstract int getRed(int pixel); + + /** + * Converts pixel value to sRGB and extract green int sample + * scaled to range [0, 255]. + * + * @see #getRed(int) + */ + public abstract int getGreen(int pixel); + + /** + * Converts pixel value to sRGB and extract blue int sample + * scaled to range [0, 255]. + * + * @see #getRed(int) + */ + public abstract int getBlue(int pixel); + + /** + * Extract alpha int sample from pixel value, scaled to [0, 255]. + * + * @param pixel pixel value that will be interpreted according to + * the color model. + * + * @return alpha sample, scaled to range [0, 255]. + */ + public abstract int getAlpha(int pixel); + + /** + * Converts a pixel int value of the color space of the color + * model to a sRGB pixel int value. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param pixel pixel value that will be interpreted according to + * the color model. + * + * @return a pixel in sRGB color space, encoded in default + * 0xAARRGGBB format. */ + public int getRGB(int pixel) + { + return + ((getAlpha(pixel) & 0xff) << 24) | + (( getRed(pixel) & 0xff) << 16) | + ((getGreen(pixel) & 0xff) << 8) | + (( getBlue(pixel) & 0xff) << 0); + } + + + /** + * In this color model we know that the whole pixel value will + * always be contained within the first element of the pixel + * array. + */ + final int getPixelFromArray(Object inData) { + DataBuffer data = + Buffers.createBufferFromData(transferType, inData, 1); + Object da = Buffers.getData(data); + + return data.getElem(0); + } + + /** + * Converts pixel in the given array to sRGB and extract blue int + * sample scaled to range [0-255]. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param inData array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + */ + public int getRed(Object inData) + { + return getRed(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getGreen(Object inData) + { + return getGreen(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getBlue(Object inData) { + return getBlue(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getAlpha(Object inData) { + return getAlpha(getPixelFromArray(inData)); + } + + /** + * Converts a pixel in the given array of the color space of the + * color model to an sRGB pixel int value. + * + * <p>This method performs the inverse function of + * <code>getDataElements(int rgb, Object pixel)</code>. + * I.e. <code>(rgb == cm.getRGB(cm.getDataElements(rgb, + * null)))</code>. + * + * @param inData array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + * + * @return a pixel in sRGB color space, encoded in default + * 0xAARRGGBB format. + * + * @see #getDataElements(int, Object) + */ + public int getRGB(Object inData) + { + return + ((getAlpha(inData) & 0xff) << 24) | + (( getRed(inData) & 0xff) << 16) | + ((getGreen(inData) & 0xff) << 8) | + (( getBlue(inData) & 0xff) << 0); + } + + /** + * Converts an sRGB pixel int value to an array containing a + * single pixel of the color space of the color model. + * + * <p>This method performs the inverse function of + * <code>getRGB(Object inData)</code>. + * + * Outline of conversion process: + * + * <ol> + * + * <li>Convert rgb to normalized [0.0, 1.0] sRGB values.</li> + * + * <li>Convert to color space components using fromRGB in + * ColorSpace.</li> + * + * <li>If color model has alpha and should be premultiplied, + * multiply color space components with alpha value</li> + * + * <li>Scale the components to the correct number of bits.</li> + * + * <li>Arrange the components in the output array</li> + * + * </ol> + * + * @param rgb The color to be converted to dataElements. A pixel + * in sRGB color space, encoded in default 0xAARRGGBB format, + * assumed not alpha premultiplied. + * + * @param pixel to avoid needless creation of arrays, an array to + * use to return the pixel can be given. If null, a suitable array + * will be created. + * + * @return An array of transferType values representing the color, + * in the color model format. The color model defines whether the + * + * @see #getRGB(Object) + */ + public Object getDataElements(int rgb, Object pixel) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Fills an array with the unnormalized component samples from a + * pixel value. I.e. decompose the pixel, but not perform any + * color conversion. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param pixel pixel value encoded according to the color model. + * + * @return arrays of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are + * according to the color model. Each component sample is stored + * as a separate element in the array. + */ + public int[] getComponents(int pixel, int[] components, int offset) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Fills an array with the unnormalized component samples from an + * array of transferType containing a single pixel. I.e. decompose + * the pixel, but not perform any color conversion. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + * + * @return arrays of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are + * according to the color model. Each component sample is stored + * as a separate element in the array. + */ + public int[] getComponents(Object pixel, int[] components, int offset) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Convert normalized components to unnormalized components. + */ + public int[] getUnnormalizedComponents(float[] normComponents, + int normOffset, + int[] components, + int offset) + { + int numComponents = getNumComponents(); + if (components == null) + { + components = new int[offset + numComponents]; + } + + for (int i=0; i<numComponents; i++) + { + float in = normComponents[normOffset++]; + int out = (int) (in * ((1<<getComponentSize(i)) - 1)); + components[offset++] = out; + } + return components; + } + + /** + * Convert unnormalized components to normalized components. + */ + public float[] getNormalizedComponents(int[] components, + int offset, + float[] normComponents, + int normOffset) + { + int numComponents = getNumComponents(); + if (normComponents == null) + { + normComponents = new float[normOffset + numComponents]; + } + + for (int i=0; i<numComponents; i++) + { + float in = components[offset++]; + float out = in / ((1<<getComponentSize(i)) - 1); + normComponents[normOffset++] = out; + } + return normComponents; + } + + /** + * Convert unnormalized components to normalized components. + * + * @since 1.4 + */ + public float[] getNormalizedComponents (Object pixel, + float[] normComponents, + int normOffset) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Converts the unnormalized component samples from an array to a + * pixel value. I.e. composes the pixel from component samples, but + * does not perform any color conversion or scaling of the samples. + * + * This method performs the inverse function of + * <code>getComponents(int pixel, int[] components, + * int offset)</code>. I.e. + * + * <code>(pixel == cm.getDataElement(cm.getComponents(pixel, null, + * 0), 0))</code>. + * + * This method is overriden in subclasses since this abstract class throws + * UnsupportedOperationException(). + * + * @param components Array of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are according + * to the color model. Each component sample is stored as a separate element + * in the array. + * @param offset Position of the first value of the pixel in components. + * + * @return pixel value encoded according to the color model. + */ + public int getDataElement(int[] components, int offset) + { + // subclasses have to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Converts the normalized component samples from an array to a pixel + * value. I.e. composes the pixel from component samples, but does not + * perform any color conversion or scaling of the samples. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. The method provided by this abstract + * class converts the components to unnormalized form and returns + * getDataElement(int[], int). + * + * @param components Array of normalized component samples of single pixel. + * The scale and multiplication state of the samples are according to the + * color model. Each component sample is stored as a separate element in the + * array. + * @param offset Position of the first value of the pixel in components. + * + * @return pixel value encoded according to the color model. + * @since 1.4 + */ + public int getDataElement (float[] components, int offset) + { + return + getDataElement(getUnnormalizedComponents(components, offset, null, 0), + 0); + } + + public Object getDataElements(int[] components, int offset, Object obj) + { + // subclasses have to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Converts the normalized component samples from an array to an array of + * TransferType values. I.e. composes the pixel from component samples, but + * does not perform any color conversion or scaling of the samples. + * + * If obj is null, a new array of TransferType is allocated and returned. + * Otherwise the results are stored in obj and obj is returned. If obj is + * not long enough, ArrayIndexOutOfBounds is thrown. If obj is not an array + * of primitives, ClassCastException is thrown. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. The method provided by this abstract + * class converts the components to unnormalized form and returns + * getDataElement(int[], int, Object). + * + * @param components Array of normalized component samples of single pixel. + * The scale and multiplication state of the samples are according to the + * color model. Each component sample is stored as a separate element in the + * array. + * @param offset Position of the first value of the pixel in components. + * @param obj Array of TransferType or null. + * + * @return pixel value encoded according to the color model. + * @throws ArrayIndexOutOfBounds + * @throws ClassCastException + * @since 1.4 + */ + public Object getDataElements(float[] components, int offset, Object obj) + { + return + getDataElements(getUnnormalizedComponents(components, offset, null, 0), + 0, obj); + } + + public boolean equals(Object obj) + { + if (!(obj instanceof ColorModel)) return false; + + ColorModel o = (ColorModel) obj; + return + (pixel_bits == o.pixel_bits) && + (transferType == o.transferType) && + (transparency == o.transparency) && + (hasAlpha == o.hasAlpha) && + (isAlphaPremultiplied == o.isAlphaPremultiplied) && + Arrays.equals(bits, o.bits) && + (cspace.equals(o.cspace)); + } + + public final ColorSpace getColorSpace() + { + return cspace; + } + + // Typically overridden + public ColorModel coerceData(WritableRaster raster, + boolean isAlphaPremultiplied) + { + if (this.isAlphaPremultiplied == isAlphaPremultiplied) + return this; + + int w = raster.getWidth(); + int h = raster.getHeight(); + int x = raster.getMinX(); + int y = raster.getMinY(); + int size = w*h; + int numColors = getNumColorComponents(); + int numComponents = getNumComponents(); + int alphaScale = (1<<getComponentSize(numColors)) - 1; + double[] pixels = raster.getPixels(x, y, w, h, (double[]) null); + + for (int i=0; i<size; i++) + { + double alpha = pixels[i*numComponents+numColors]*alphaScale; + for (int c=0; c<numColors; c++) + { + int offset = i*numComponents+c; + if (isAlphaPremultiplied) + pixels[offset] = pixels[offset]/alpha; + else + pixels[offset] = pixels[offset]*alpha; + } + } + + raster.setPixels(0, 0, w, h, pixels); + + // FIXME: what can we return? + return null; + } + + /** + * Checks if the given raster has a compatible data-layout (SampleModel). + * @param raster The Raster to test. + * @return true if raster is compatible. + */ + public boolean isCompatibleRaster(Raster raster) + { + SampleModel sampleModel = raster.getSampleModel(); + return isCompatibleSampleModel(sampleModel); + } + + // Typically overridden + public WritableRaster createCompatibleWritableRaster(int w, int h) + { + return new WritableRaster(createCompatibleSampleModel(w, h), + new Point(0, 0)); + } + + // Typically overridden + public SampleModel createCompatibleSampleModel(int w, int h) + { + throw new UnsupportedOperationException(); + } + + // Typically overridden + public boolean isCompatibleSampleModel(SampleModel sm) + { + return sm.getTransferType() == transferType; + } + + public final int getTransferType () + { + return transferType; + } + + /** + * Subclasses must override this method if it is possible for the + * color model to have an alpha channel. + * + * @return null, as per JDK 1.3 doc. Subclasses will only return + * null if no alpha raster exists. + */ + public WritableRaster getAlphaRaster(WritableRaster raster) + { + return null; + + /* It is a mystery to me why we couldn't use the following code... + + + if (!hasAlpha()) return null; + + SampleModel sm = raster.getSampleModel(); + int[] alphaBand = { sm.getNumBands() - 1 }; + SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); + DataBuffer buffer = raster.getDataBuffer(); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(alphaModel, buffer, origin); + + + ...here, and avoided overriding the method in subclasses, + but the Sun docs state that this method always will return + null, and that overriding is required. Oh, well. + */ + } + + String stringParam() + { + return "pixel_bits=" + pixel_bits + + ", cspace=" + cspace + + ", transferType=" + transferType + + ", transparency=" + transparency + + ", hasAlpha=" + hasAlpha + + ", isAlphaPremultiplied=" + isAlphaPremultiplied; + } + + public String toString() + { + return getClass().getName() + "[" + stringParam() + "]"; + } +} diff --git a/libjava/classpath/java/awt/image/ComponentColorModel.java b/libjava/classpath/java/awt/image/ComponentColorModel.java new file mode 100644 index 00000000000..f56688f9362 --- /dev/null +++ b/libjava/classpath/java/awt/image/ComponentColorModel.java @@ -0,0 +1,391 @@ +/* ComponentColorModel.java -- + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.Point; +import java.awt.color.ColorSpace; + +public class ComponentColorModel extends ColorModel +{ + private static int sum(int[] values) + { + int sum = 0; + for (int i=0; i<values.length; i++) + sum += values[i]; + return sum; + } + + public ComponentColorModel(ColorSpace colorSpace, int[] bits, + boolean hasAlpha, + boolean isAlphaPremultiplied, + int transparency, int transferType) + { + super(sum(bits), bits, colorSpace, hasAlpha, isAlphaPremultiplied, + transparency, transferType); + } + + /** + * Construct a new ComponentColorModel. + * + * This constructor makes all bits of each sample significant, so for a + * transferType of DataBuffer.BYTE, the bits per sample is 8, etc. If + * both hasAlpha and isAlphaPremultiplied are true, color samples are + * assumed to be premultiplied by the alpha component. Transparency may be + * one of OPAQUE, BITMASK, or TRANSLUCENT. + * + * @param colorSpace The colorspace for this color model. + * @param hasAlpha True if there is an alpha component. + * @param isAlphaPremultiplied True if colors are already multiplied by + * alpha. + * @param transparency The type of alpha values. + * @param transferType Data type of pixel sample values. + * @since 1.4 + */ + public ComponentColorModel(ColorSpace colorSpace, + boolean hasAlpha, + boolean isAlphaPremultiplied, + int transparency, int transferType) + { + this(colorSpace, null, hasAlpha, isAlphaPremultiplied, + transparency, transferType); + } + + public int getRed(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getGreen(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getBlue(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getAlpha(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + int shift = 8 - getComponentSize(getNumColorComponents()); + if (shift >= 0) return pixel << shift; + return pixel >> (-shift); + } + + public int getRGB(int pixel) + { + float[] rgb = getRGBFloat(pixel); + int ret = getRGB(rgb); + if (hasAlpha()) ret |= getAlpha(pixel) << 24; + return ret; + } + + + /* Note, it's OK to pass a to large array to toRGB(). Extra + elements are ignored. */ + + private float[] getRGBFloat(int pixel) + { + float[] data = { pixel }; + return cspace.toRGB(data); + } + + private float[] getRGBFloat(Object inData) + { + DataBuffer buffer = + Buffers.createBufferFromData(transferType, inData, + getNumComponents()); + int colors = getNumColorComponents(); + float[] data = new float[colors]; + + // FIXME: unpremultiply data that is premultiplied + for (int i=0; i<colors; i++) + { + float maxValue = (1<<getComponentSize(i))-1; + data[i] = buffer.getElemFloat(i)/maxValue; + } + float[] rgb = cspace.toRGB(data); + return rgb; + } + + public int getRed(Object inData) + { + return (int) getRGBFloat(inData)[0]*255; + } + + public int getGreen(Object inData) + { + return (int) getRGBFloat(inData)[1]*255; + } + + public int getBlue(Object inData) + { + return (int) getRGBFloat(inData)[2]*255; + } + + public int getAlpha(Object inData) + { + DataBuffer buffer = + Buffers.createBufferFromData(transferType, inData, + getNumComponents()); + int shift = 8 - getComponentSize(getNumColorComponents()); + int alpha = buffer.getElem(getNumColorComponents()); + if (shift >= 0) return alpha << shift; + return alpha >> (-shift); + } + + private int getRGB(float[] rgb) + { + /* NOTE: We could cast to byte instead of int here. This would + avoid bits spilling over from one bit field to + another. But, if we assume that floats are in the [0.0, + 1.0] range, this will never happen anyway. */ + + /* Remember to multiply BEFORE casting to int, otherwise, decimal + point data will be lost. */ + int ret = + (((int) (rgb[0]*255F)) << 16) | + (((int) (rgb[1]*255F)) << 8) | + (((int) (rgb[2]*255F)) << 0); + return ret; + } + + /** + * @param inData pixel data of transferType, as returned by the + * getDataElements method in SampleModel. + */ + public int getRGB(Object inData) + { + float[] rgb = getRGBFloat(inData); + int ret = getRGB(rgb); + if (hasAlpha()) ret |= getAlpha(inData) << 24; + return ret; + } + + public Object getDataElements(int rgb, Object pixel) + { + // Convert rgb to [0.0, 1.0] sRGB values. + float[] rgbFloats = { + ((rgb >> 16)&0xff)/255.0F, + ((rgb >> 8)&0xff)/255.0F, + ((rgb >> 0)&0xff)/255.0F + }; + + // Convert from rgb to color space components. + float[] data = cspace.fromRGB(rgbFloats); + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, + getNumComponents()); + int numColors = getNumColorComponents(); + + if (hasAlpha()) + { + float alpha = ((rgb >> 24)&0xff)/255.0F; + + /* If color model has alpha and should be premultiplied, multiply + color space components with alpha value. */ + if (isAlphaPremultiplied()) { + for (int i=0; i<numColors; i++) + data[i] *= alpha; + } + // Scale the alpha sample to the correct number of bits. + alpha *= (1<<(bits[numColors]-1)); + // Arrange the alpha sample in the output array. + buffer.setElemFloat(numColors, alpha); + } + for (int i=0; i<numColors; i++) + { + // Scale the color samples to the correct number of bits. + float value = data[i]*(1<<(bits[i]-1)); + // Arrange the color samples in the output array. + buffer.setElemFloat(i, value); + } + return Buffers.getData(buffer); + } + + public int[] getComponents(int pixel, int[] components, int offset) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + if (components == null) + components = new int[getNumComponents() + offset]; + components[offset] = pixel; + return components; + } + + public int[] getComponents(Object pixel, int[] components, int offset) + { + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, + getNumComponents()); + int numComponents = getNumComponents(); + + if (components == null) + components = new int[numComponents + offset]; + + for (int i=0; i<numComponents; i++) + components[offset++] = buffer.getElem(i); + + return components; + } + + public int getDataElement(int[] components, int offset) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return components[offset]; + } + + public Object getDataElements(int[] components, int offset, Object obj) + { + DataBuffer buffer = Buffers.createBuffer(transferType, obj, + getNumComponents()); + int numComponents = getNumComponents(); + + for (int i=0; i<numComponents; i++) + buffer.setElem(i, components[offset++]); + + return Buffers.getData(buffer); + } + + public ColorModel coerceData(WritableRaster raster, + boolean isAlphaPremultiplied) { + if (this.isAlphaPremultiplied == isAlphaPremultiplied) + return this; + + /* TODO: provide better implementation based on the + assumptions we can make due to the specific type of the + color model. */ + super.coerceData(raster, isAlphaPremultiplied); + + return new ComponentColorModel(cspace, bits, hasAlpha(), + isAlphaPremultiplied, // argument + transparency, transferType); + } + + public boolean isCompatibleRaster(Raster raster) + { + return super.isCompatibleRaster(raster); + // FIXME: Should we test something more here? (Why override?) + } + + public WritableRaster createCompatibleWritableRaster(int w, int h) + { + SampleModel sm = createCompatibleSampleModel(w, h); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(sm, origin); + } + + + /** + * Creates a <code>SampleModel</code> whose arrangement of pixel + * data is compatible to this <code>ColorModel</code>. + * + * @param w the number of pixels in the horizontal direction. + * @param h the number of pixels in the vertical direction. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + int pixelStride, scanlineStride; + int[] bandOffsets; + + pixelStride = getNumComponents(); + scanlineStride = pixelStride * w; + + /* We might be able to re-use the same bandOffsets array among + * multiple calls to this method. However, this optimization does + * not seem worthwile because setting up descriptive data + * structures (such as SampleModels) is neglectible in comparision + * to shuffling around masses of pixel data. + */ + bandOffsets = new int[pixelStride]; + for (int i = 0; i < pixelStride; i++) + bandOffsets[i] = i; + + /* FIXME: Think about whether it would make sense to return the + * possibly more efficient PixelInterleavedSampleModel for other + * transferTypes as well. It seems unlikely that this would break + * any user applications, so the Mauve tests on this method + * might be too restrictive. + */ + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + return new PixelInterleavedSampleModel(transferType, w, h, + pixelStride, + scanlineStride, + bandOffsets); + + default: + return new ComponentSampleModel(transferType, w, h, + pixelStride, + scanlineStride, + bandOffsets); + } + } + + + public boolean isCompatibleSampleModel(SampleModel sm) + { + return + (sm instanceof ComponentSampleModel) && + super.isCompatibleSampleModel(sm); + } + + public WritableRaster getAlphaRaster(WritableRaster raster) + { + if (!hasAlpha()) return null; + + SampleModel sm = raster.getSampleModel(); + int[] alphaBand = { sm.getNumBands() - 1 }; + SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); + DataBuffer buffer = raster.getDataBuffer(); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(alphaModel, buffer, origin); + } + + public boolean equals(Object obj) + { + if (!(obj instanceof ComponentColorModel)) return false; + return super.equals(obj); + } +} diff --git a/libjava/classpath/java/awt/image/ComponentSampleModel.java b/libjava/classpath/java/awt/image/ComponentSampleModel.java new file mode 100644 index 00000000000..953f63c1ea1 --- /dev/null +++ b/libjava/classpath/java/awt/image/ComponentSampleModel.java @@ -0,0 +1,544 @@ +/* Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; + +/* FIXME: This class does not yet support data type TYPE_SHORT */ + +/** + * ComponentSampleModel supports a flexible organization of pixel samples in + * memory, permitting pixel samples to be interleaved by band, by scanline, + * and by pixel. + * + * A DataBuffer for this sample model has K banks of data. Pixels have N + * samples, so there are N bands in the DataBuffer. Each band is completely + * contained in one bank of data, but a bank may contain more than one band. + * Each pixel sample is stored in a single data element. + * + * Within a bank, each band begins at an offset stored in bandOffsets. The + * banks containing the band is given by bankIndices. Within the bank, there + * are three dimensions - band, pixel, and scanline. The dimension ordering + * is controlled by bandOffset, pixelStride, and scanlineStride, which means + * that any combination of interleavings is supported. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class ComponentSampleModel extends SampleModel +{ + protected int[] bandOffsets; + protected int[] bankIndices; + + // FIXME: Should we really shadow the numBands in the superclass? + //protected int numBands; + + /** Used when creating data buffers. */ + protected int numBanks; + + protected int scanlineStride; + + protected int pixelStride; + + private boolean tightPixelPacking = false; + + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int[] bandOffsets) + { + this(dataType, w, h, pixelStride, scanlineStride, + new int[bandOffsets.length], bandOffsets); + } + + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int[] bankIndices, + int[] bandOffsets) + { + super(dataType, w, h, bandOffsets.length); + if ((pixelStride<0) || (scanlineStride<0) || + (bandOffsets.length<1) || + (bandOffsets.length != bankIndices.length)) + throw new IllegalArgumentException(); + + this.bandOffsets = bandOffsets; + this.bankIndices = bankIndices; + + this.numBanks = 0; + for (int b=0; b<bankIndices.length; b++) + this.numBanks = Math.max(this.numBanks, bankIndices[b]+1); + + this.scanlineStride = scanlineStride; + this.pixelStride = pixelStride; + + // See if we can use some speedups + + /* FIXME: May these checks should be reserved for the + PixelInterleavedSampleModel? */ + + if (pixelStride == numBands) + { + tightPixelPacking = true; + for (int b=0; b<numBands; b++) { + if ((bandOffsets[b] != b) || (bankIndices[b] !=0)) + { + tightPixelPacking = false; + break; + } + } + } + } + + public SampleModel createCompatibleSampleModel(int w, int h) + { + return new ComponentSampleModel(dataType, w, h, pixelStride, + scanlineStride, bankIndices, + bandOffsets); + } + + public SampleModel createSubsetSampleModel(int[] bands) + { + int numBands = bands.length; + + int[] bankIndices = new int[numBands]; + int[] bandOffsets = new int[numBands]; + for (int b=0; b<numBands; b++) + { + bankIndices[b] = this.bankIndices[bands[b]]; + bandOffsets[b] = this.bandOffsets[bands[b]]; + } + + return new ComponentSampleModel(dataType, width, height, pixelStride, + scanlineStride, bankIndices, + bandOffsets); + } + + public DataBuffer createDataBuffer() + { + // Maybe this value should be precalculated in the constructor? + int highestOffset = 0; + for (int b=0; b<numBands; b++) + { + highestOffset = Math.max(highestOffset, bandOffsets[b]); + } + int size = pixelStride*(width-1) + scanlineStride*(height-1) + + highestOffset + 1; + + return Buffers.createBuffer(getDataType(), size, numBanks); + } + + public int getOffset(int x, int y) + { + return getOffset(x, y, 0); + } + + public int getOffset(int x, int y, int b) + { + return bandOffsets[b] + pixelStride*x + scanlineStride*y; + } + + public final int[] getSampleSize() + { + int size = DataBuffer.getDataTypeSize(getDataType()); + int[] sizes = new int[numBands]; + + java.util.Arrays.fill(sizes, size); + return sizes; + } + + public final int getSampleSize(int band) + { + return DataBuffer.getDataTypeSize(getDataType()); + } + + public final int[] getBankIndices() + { + return bankIndices; + } + + public final int[] getBandOffsets() + { + return bandOffsets; + } + + public final int getScanlineStride() + { + return scanlineStride; + } + + public final int getPixelStride() + { + return pixelStride; + } + + public final int getNumDataElements() + { + return numBands; + } + + public Object getDataElements(int x, int y, Object obj, DataBuffer data) + { + int xyOffset = pixelStride*x + scanlineStride*y; + + int[] totalBandDataOffsets = new int[numBands]; + + /* Notice that band and bank offsets are different. Band offsets + are managed by the sample model, and bank offsets are managed + by the data buffer. Both must be accounted for. */ + + /* FIXME: For single pixels, it is probably easier to simple + call getElem instead of calculating the bank offset ourself. + + On the other hand, then we need to push the value through + the int type returned by the getElem method. */ + + int[] bankOffsets = data.getOffsets(); + + for (int b=0; b<numBands; b++) + { + totalBandDataOffsets[b] = + bandOffsets[b]+bankOffsets[bankIndices[b]] + xyOffset; + } + + try + { + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + DataBufferByte inByte = (DataBufferByte) data; + byte[] outByte = (byte[]) obj; + if (outByte == null) outByte = new byte[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outByte[b] = inByte.getData(bankIndices[b])[dOffset]; + } + return outByte; + + case DataBuffer.TYPE_USHORT: + DataBufferUShort inUShort = (DataBufferUShort) data; + short[] outUShort = (short[]) obj; + if (outUShort == null) outUShort = new short[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outUShort[b] = inUShort.getData(bankIndices[b])[dOffset]; + } + return outUShort; + + case DataBuffer.TYPE_SHORT: + DataBufferShort inShort = (DataBufferShort) data; + short[] outShort = (short[]) obj; + if (outShort == null) outShort = new short[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outShort[b] = inShort.getData(bankIndices[b])[dOffset]; + } + return outShort; + + case DataBuffer.TYPE_INT: + DataBufferInt inInt = (DataBufferInt) data; + int[] outInt = (int[]) obj; + if (outInt == null) outInt = new int[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outInt[b] = inInt.getData(bankIndices[b])[dOffset]; + } + return outInt; + + case DataBuffer.TYPE_FLOAT: + DataBufferFloat inFloat = (DataBufferFloat) data; + float[] outFloat = (float[]) obj; + if (outFloat == null) outFloat = new float[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outFloat[b] = inFloat.getData(bankIndices[b])[dOffset]; + } + return outFloat; + + case DataBuffer.TYPE_DOUBLE: + DataBufferDouble inDouble = (DataBufferDouble) data; + double[] outDouble = (double[]) obj; + if (outDouble == null) outDouble = new double[numBands]; + + for (int b=0; b<numBands; b++) + { + int dOffset = totalBandDataOffsets[b]; + outDouble[b] = inDouble.getData(bankIndices[b])[dOffset]; + } + return outDouble; + + default: + throw new IllegalStateException("unknown transfer type " + + getTransferType()); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While reading data elements, " + + "x=" + x + ", y=" + y +", " + ", xyOffset=" + xyOffset + + ", data.getSize()=" + data.getSize() + ": " + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + public Object getDataElements(int x, int y, int w, int h, Object obj, + DataBuffer data) + { + if (!tightPixelPacking) + { + return super.getDataElements(x, y, w, h, obj, data); + } + + // using get speedup + + // We can copy whole rows + int rowSize = w*numBands; + int dataSize = rowSize*h; + + DataBuffer transferBuffer = + Buffers.createBuffer(getTransferType(), obj, dataSize); + obj = Buffers.getData(transferBuffer); + + int inOffset = + pixelStride*x + + scanlineStride*y + + data.getOffset(); // Assumes only one band is used + + /* We don't add band offsets since we assume that bands have + offsets 0, 1, 2, ... */ + + // See if we can copy everything in one go + if (scanlineStride == rowSize) + { + // Collapse scan lines: + rowSize *= h; + // We ignore scanlineStride since it won't be of any use + h = 1; + } + + int outOffset = 0; + Object inArray = Buffers.getData(data); + for (int yd = 0; yd<h; yd++) + { + System.arraycopy(inArray, inOffset, obj, outOffset, rowSize); + inOffset += scanlineStride; + outOffset += rowSize; + } + return obj; + } + + public void setDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) + { + if (!tightPixelPacking) + { + super.setDataElements(x, y, w, h, obj, data); + return; + } + + // using set speedup, we can copy whole rows + int rowSize = w*numBands; + int dataSize = rowSize*h; + + DataBuffer transferBuffer = + Buffers.createBufferFromData(getTransferType(), obj, dataSize); + + int[] bankOffsets = data.getOffsets(); + + int outOffset = + pixelStride*x + + scanlineStride*y + + bankOffsets[0]; // same assuptions as in get... + + // See if we can copy everything in one go + if (scanlineStride == rowSize) + { + // Collapse scan lines: + rowSize *= h; + h = 1; + } + + int inOffset = 0; + Object outArray = Buffers.getData(data); + for (int yd = 0; yd<h; yd++) + { + System.arraycopy(obj, inOffset, outArray, outOffset, rowSize); + outOffset += scanlineStride; + inOffset += rowSize; + } + } + + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = pixelStride*x + scanlineStride*y; + if (iArray == null) iArray = new int[numBands]; + for (int b=0; b<numBands; b++) + { + iArray[b] = data.getElem(bankIndices[b], offset+bandOffsets[b]); + } + return iArray; + } + + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int offset = pixelStride*x + scanlineStride*y; + if (iArray == null) iArray = new int[numBands*w*h]; + int outOffset = 0; + for (y=0; y<h; y++) + { + int lineOffset = offset; + for (x=0; x<w; x++) + { + for (int b=0; b<numBands; b++) + { + iArray[outOffset++] = + data.getElem(bankIndices[b], lineOffset+bandOffsets[b]); + } + lineOffset += pixelStride; + } + offset += scanlineStride; + } + return iArray; + } + + public int getSample(int x, int y, int b, DataBuffer data) + { + return data.getElem(bankIndices[b], getOffset(x, y, b)); + } + + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int offset = pixelStride*x + scanlineStride*y; + int[] totalBandDataOffsets = new int[numBands]; + int[] bankOffsets = data.getOffsets(); + for (int b=0; b<numBands; b++) + totalBandDataOffsets[b] = + bandOffsets[b]+bankOffsets[bankIndices[b]] + offset; + + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] in = (byte[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferUShort out = (DataBufferUShort) data; + short[] in = (short[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + case DataBuffer.TYPE_SHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] in = (short[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] in = (int[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + case DataBuffer.TYPE_FLOAT: + { + DataBufferFloat out = (DataBufferFloat) data; + float[] in = (float[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + case DataBuffer.TYPE_DOUBLE: + { + DataBufferDouble out = (DataBufferDouble) data; + double[] in = (double[]) obj; + + for (int b=0; b<numBands; b++) + out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b]; + + return; + } + default: + throw new UnsupportedOperationException("transfer type not " + + "implemented"); + } + } + + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = pixelStride*x + scanlineStride*y; + for (int b=0; b<numBands; b++) + data.setElem(bankIndices[b], offset+bandOffsets[b], iArray[b]); + } + + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + data.setElem(bankIndices[b], getOffset(x, y, b), s); + } +} diff --git a/libjava/classpath/java/awt/image/ConvolveOp.java b/libjava/classpath/java/awt/image/ConvolveOp.java new file mode 100644 index 00000000000..f841c13557f --- /dev/null +++ b/libjava/classpath/java/awt/image/ConvolveOp.java @@ -0,0 +1,337 @@ +/* ConvolveOp.java -- + Copyright (C) 2004 Free Software Foundation -- ConvolveOp + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * Convolution filter. + * + * ConvolveOp convolves the source image with a Kernel to generate a + * destination image. This involves multiplying each pixel and its neighbors + * with elements in the kernel to compute a new pixel. + * + * Each band in a Raster is convolved and copied to the destination Raster. + * + * For BufferedImages, convolution is applied to all components. If the + * source is not premultiplied, the data will be premultiplied before + * convolving. Premultiplication will be undone if the destination is not + * premultiplied. Color conversion will be applied if needed. + * + * @author jlquinn@optonline.net + */ +public class ConvolveOp implements BufferedImageOp, RasterOp +{ + /** Edge pixels are set to 0. */ + public static final int EDGE_ZERO_FILL = 0; + + /** Edge pixels are copied from the source. */ + public static final int EDGE_NO_OP = 1; + + private Kernel kernel; + private int edge; + private RenderingHints hints; + + /** + * Construct a ConvolveOp. + * + * The edge condition specifies that pixels outside the area that can be + * filtered are either set to 0 or copied from the source image. + * + * @param kernel The kernel to convolve with. + * @param edgeCondition Either EDGE_ZERO_FILL or EDGE_NO_OP. + * @param hints Rendering hints for color conversion, or null. + */ + public ConvolveOp(Kernel kernel, + int edgeCondition, + RenderingHints hints) + { + this.kernel = kernel; + edge = edgeCondition; + this.hints = hints; + } + + /** + * Construct a ConvolveOp. + * + * The edge condition defaults to EDGE_ZERO_FILL. + * + * @param kernel The kernel to convolve with. + */ + public ConvolveOp(Kernel kernel) + { + this.kernel = kernel; + edge = EDGE_ZERO_FILL; + hints = null; + } + + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, + * java.awt.image.BufferedImage) + */ + public BufferedImage filter(BufferedImage src, BufferedImage dst) + { + if (src == dst) + throw new IllegalArgumentException(); + + if (dst == null) + dst = createCompatibleDestImage(src, src.getColorModel()); + + // Make sure source image is premultiplied + BufferedImage src1 = src; + if (!src.isPremultiplied) + { + src1 = createCompatibleDestImage(src, src.getColorModel()); + src.copyData(src1.getRaster()); + src1.coerceData(true); + } + + BufferedImage dst1 = dst; + if (!src.getColorModel().equals(dst.getColorModel())) + dst1 = createCompatibleDestImage(src, src.getColorModel()); + + filter(src1.getRaster(), dst1.getRaster()); + + if (dst1 != dst) + { + // Convert between color models. + // TODO Check that premultiplied alpha is handled correctly here. + Graphics2D gg = dst.createGraphics(); + gg.setRenderingHints(hints); + gg.drawImage(dst1, 0, 0, null); + gg.dispose(); + } + + return dst; + } + + /* (non-Javadoc) + * @see + * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, + * java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + // FIXME: set properties to those in src + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isPremultiplied, null); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public RenderingHints getRenderingHints() + { + return hints; + } + + /** + * @return The edge condition. + */ + public int getEdgeCondition() + { + return edge; + } + + /** + * @return The convolution kernel. + */ + public Kernel getKernel() + { + return kernel; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, + * java.awt.image.WritableRaster) + */ + public WritableRaster filter(Raster src, WritableRaster dest) { + if (src.numBands != dest.numBands) + throw new ImagingOpException(null); + if (src == dest) + throw new IllegalArgumentException(); + if (src.getWidth() < kernel.getWidth() || + src.getHeight() < kernel.getHeight()) + throw new ImagingOpException(null); + + if (dest == null) + dest = createCompatibleDestRaster(src); + + // Deal with bottom edge + if (edge == EDGE_ZERO_FILL) + { + float[] zeros = new float[src.getNumBands() * src.getWidth() + * (kernel.getYOrigin() - 1)]; + Arrays.fill(zeros, 0); + dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(), + kernel.getYOrigin() - 1, zeros); + } + else + { + float[] vals = new float[src.getNumBands() * src.getWidth() + * (kernel.getYOrigin() - 1)]; + src.getPixels(src.getMinX(), src.getMinY(), src.getWidth(), + kernel.getYOrigin() - 1, vals); + dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(), + kernel.getYOrigin() - 1, vals); + } + + // Handle main section + float[] kvals = kernel.getKernelData(null); + + float[] tmp = new float[kernel.getWidth() * kernel.getHeight()]; + for (int y = src.getMinY() + kernel.getYOrigin(); + y < src.getMinY() + src.getHeight() - kernel.getYOrigin() / 2; y++) + { + // Handle unfiltered edge pixels at start of line + float[] t1 = new float[(kernel.getXOrigin() - 1) * src.getNumBands()]; + if (edge == EDGE_ZERO_FILL) + Arrays.fill(t1, 0); + else + src.getPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1); + dest.setPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1); + + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + { + // FIXME: This needs a much more efficient implementation + for (int b = 0; b < src.getNumBands(); b++) + { + float v = 0; + src.getSamples(x, y, kernel.getWidth(), kernel.getHeight(), b, tmp); + for (int i=0; i < tmp.length; i++) + v += tmp[i] * kvals[i]; + dest.setSample(x, y, b, v); + } + } + + // Handle unfiltered edge pixels at end of line + float[] t2 = new float[(kernel.getWidth() / 2) * src.getNumBands()]; + if (edge == EDGE_ZERO_FILL) + Arrays.fill(t2, 0); + else + src.getPixels(src.getMinX() + src.getWidth() + - (kernel.getWidth() / 2), + y, kernel.getWidth() / 2, 1, t2); + dest.setPixels(src.getMinX() + src.getWidth() - (kernel.getWidth() / 2), + y, kernel.getWidth() / 2, 1, t2); + } + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++) + { + + } + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++) + { + + } + + // Handle top edge + if (edge == EDGE_ZERO_FILL) + { + float[] zeros = new float[src.getNumBands() * src.getWidth() * + (kernel.getHeight() / 2)]; + Arrays.fill(zeros, 0); + dest.setPixels(src.getMinX(), + src.getHeight() + src.getMinY() - (kernel.getHeight() / 2), + src.getWidth(), kernel.getHeight() / 2, zeros); + } + else + { + float[] vals = new float[src.getNumBands() * src.getWidth() * + (kernel.getHeight() / 2)]; + src.getPixels(src.getMinX(), + src.getHeight() + src.getMinY() + - (kernel.getHeight() / 2), + src.getWidth(), kernel.getHeight() / 2, vals); + dest.setPixels(src.getMinX(), + src.getHeight() + src.getMinY() + - (kernel.getHeight() / 2), + src.getWidth(), kernel.getHeight() / 2, vals); + } + + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /** Return corresponding destination point for source point. + * + * ConvolveOp will return the value of src unchanged. + * @param src The source point. + * @param dst The destination point. + * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, + * java.awt.geom.Point2D) + */ + public Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) return (Point2D)src.clone(); + dst.setLocation(src); + return dst; + } +} diff --git a/libjava/classpath/java/awt/image/CropImageFilter.java b/libjava/classpath/java/awt/image/CropImageFilter.java new file mode 100644 index 00000000000..490f43cd728 --- /dev/null +++ b/libjava/classpath/java/awt/image/CropImageFilter.java @@ -0,0 +1,180 @@ +/* CropImageFilter.java -- Java class for cropping image filter + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Rectangle; +import java.util.Hashtable; + +/** + * Currently this filter does almost nothing and needs to be implemented. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class CropImageFilter extends ImageFilter +{ + int x; + int y; + int width; + int height; + + /** + * Construct a new <code>CropImageFilter</code> instance. + * + * @param x the x-coordinate location of the top-left of the cropped rectangle + * @param y the y-coordinate location of the top-left of the cropped rectangle + * @param width the width of the cropped rectangle + * @param height the height of the cropped rectangle + */ + public CropImageFilter(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * An <code>ImageProducer</code> indicates the size of the image + * being produced using this method. This filter overrides this + * method in order to set the dimentions to the size of the + * cropped rectangle instead of the size of the image. + * + * @param width the width of the image + * @param height the height of the image + */ + public void setDimensions(int width, int height) + { + consumer.setDimensions(this.width, this.height); + } + + /** + * An <code>ImageProducer</code> can set a list of properties + * associated with this image by using this method. + * <br> + * FIXME - What property is set for this class? + * + * @param props the list of properties associated with this image + */ + public void setProperties(Hashtable props) + { + props.put("filters", "CropImageFilter"); + consumer.setProperties(props); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a <code>byte</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + Rectangle filterBounds = new Rectangle(this.x, this.y, + this.width, this.height); + Rectangle pixelBounds = new Rectangle(x, y, w, h); + + if (filterBounds.intersects(pixelBounds)) + { + Rectangle bounds = filterBounds.intersection(pixelBounds); + + byte[] cropped = new byte[bounds.width * bounds.height]; + for (int i = 0; i < bounds.height; i++) + { + int start = (bounds.y - pixelBounds.y + i) * scansize + offset; + + for (int j = 0; j < bounds.width; j++) + cropped[i * bounds.width + j] = pixels[start + bounds.x + j]; + } + + consumer.setPixels(bounds.x, bounds.y, + bounds.width, bounds.height, + model, cropped, 0, bounds.width); + } + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an <code>int</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + Rectangle filterBounds = new Rectangle(this.x, this.y, + this.width, this.height); + Rectangle pixelBounds = new Rectangle(x, y, w, h); + + if (filterBounds.intersects(pixelBounds)) + { + Rectangle bounds = filterBounds.intersection(pixelBounds); + + int[] cropped = new int[bounds.width * bounds.height]; + for (int i = 0; i < bounds.height; i++) + { + int start = (bounds.y - pixelBounds.y + i) * scansize + offset; + + for (int j = 0; j < bounds.width; j++) + cropped[i * bounds.width + j] = pixels[start + bounds.x + j]; + } + + consumer.setPixels(bounds.x, bounds.y, + bounds.width, bounds.height, + model, cropped, 0, bounds.width); + } + } + +} + diff --git a/libjava/classpath/java/awt/image/DataBuffer.java b/libjava/classpath/java/awt/image/DataBuffer.java new file mode 100644 index 00000000000..9e4f714180a --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBuffer.java @@ -0,0 +1,436 @@ +/* Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/** + * Class that manages arrays of data elements. A data buffer consists + * of one or more banks. A bank is a continuous region of data + * elements. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class DataBuffer +{ + /** + * A constant representing a data type that uses <code>byte</code> primitives + * as the storage unit. + */ + public static final int TYPE_BYTE = 0; + + /** + * A constant representing a data type that uses <code>short</code> + * primitives as the storage unit. + */ + public static final int TYPE_USHORT = 1; + + /** + * A constant representing a data type that uses <code>short</code> + * primitives as the storage unit. + */ + public static final int TYPE_SHORT = 2; + + /** + * A constant representing a data type that uses <code>int</code> + * primitives as the storage unit. + */ + public static final int TYPE_INT = 3; + + /** + * A constant representing a data type that uses <code>float</code> + * primitives as the storage unit. + */ + public static final int TYPE_FLOAT = 4; + + /** + * A constant representing a data type that uses <code>double</code> + * primitives as the storage unit. + */ + public static final int TYPE_DOUBLE = 5; + + /** + * A constant representing an undefined data type. + */ + public static final int TYPE_UNDEFINED = 32; + + /** The type of the data elements stored in the data buffer. */ + protected int dataType; + + /** The number of banks in this buffer. */ + protected int banks = 1; + + /** Offset into the default (0'th) bank). */ + protected int offset; // FIXME: Is offsets[0] always mirrored in offset? + + /** The size of the banks. */ + protected int size; + + /** Offset into each bank. */ + protected int[] offsets; + + /** + * Creates a new <code>DataBuffer</code> with the specified data type and + * size. The <code>dataType</code> should be one of the constants + * {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, {@link #TYPE_USHORT}, + * {@link #TYPE_INT}, {@link #TYPE_FLOAT} and {@link #TYPE_DOUBLE}. + * <p> + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + */ + protected DataBuffer(int dataType, int size) + { + this.dataType = dataType; + this.size = size; + } + + /** + * Creates a new <code>DataBuffer</code> with the specified data type, + * size and number of banks. The <code>dataType</code> should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * <p> + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + */ + protected DataBuffer(int dataType, int size, int numBanks) { + this(dataType, size); + banks = numBanks; + offsets = new int[numBanks]; + } + + /** + * Creates a new <code>DataBuffer</code> with the specified data type, + * size and number of banks. An offset (which applies to all banks) is + * also specified. The <code>dataType</code> should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * <p> + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + * @param offset the offset to the first element for all banks. + */ + protected DataBuffer(int dataType, int size, int numBanks, int offset) { + this(dataType, size, numBanks); + + java.util.Arrays.fill(offsets, offset); + + this.offset = offset; + } + + /** + * Creates a new <code>DataBuffer</code> with the specified data type, + * size and number of banks. An offset (which applies to all banks) is + * also specified. The <code>dataType</code> should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * <p> + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + * @param offsets the offsets to the first element for all banks. + * + * @throws ArrayIndexOutOfBoundsException if + * <code>numBanks != offsets.length</code>. + */ + protected DataBuffer(int dataType, int size, int numBanks, int[] offsets) { + this(dataType, size); + if (numBanks != offsets.length) + throw new ArrayIndexOutOfBoundsException(); + + banks = numBanks; + this.offsets = offsets; + + offset = offsets[0]; + } + + /** + * Returns the size (number of bits) of the specified data type. Valid types + * are defined by the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * + * @param dataType the data type. + * @return The number of bits for the specified data type. + * @throws IllegalArgumentException if <code>dataType < 0</code> or + * <code>dataType > TYPE_DOUBLE</code>. + */ + public static int getDataTypeSize(int dataType) { + // Maybe this should be a lookup table instead. + switch (dataType) + { + case TYPE_BYTE: + return 8; + case TYPE_USHORT: + case TYPE_SHORT: + return 16; + case TYPE_INT: + case TYPE_FLOAT: + return 32; + case TYPE_DOUBLE: + return 64; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Returns the type of the data elements in the data buffer. Valid types + * are defined by the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * + * @return The type. + */ + public int getDataType() + { + return dataType; + } + + /** + * Returns the size of the data buffer. + * + * @return The size. + */ + public int getSize() + { + return size; + } + + /** + * Returns the element offset for the first data bank. + * + * @return The element offset. + */ + public int getOffset() + { + return offset; + } + + /** + * Returns the offsets for all the data banks used by this + * <code>DataBuffer</code>. + * + * @return The offsets. + */ + public int[] getOffsets() + { + if (offsets == null) + { + // is this necessary? + offsets = new int[1]; + offsets[0] = offset; + } + return offsets; + } + + /** + * Returns the number of data banks for this <code>DataBuffer</code>. + * @return The number of data banks. + */ + public int getNumBanks() + { + return banks; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return getElem(0, i); + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public abstract int getElem(int bank, int i); + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + setElem(0, i, val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public abstract void setElem(int bank, int i, int val); + + /** + * Returns an element from the first data bank, converted to a + * <code>float</code>. The offset (specified in the constructor) is added + * to <code>i</code> before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public float getElemFloat(int i) + { + return getElem(i); + } + + /** + * Returns an element from a particular data bank, converted to a + * <code>float</code>. The offset (specified in the constructor) is + * added to <code>i</code> before accessing the underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public float getElemFloat(int bank, int i) + { + return getElem(bank, i); + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElemFloat(int i, float val) + { + setElem(i, (int) val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElemFloat(int bank, int i, float val) + { + setElem(bank, i, (int) val); + } + + /** + * Returns an element from the first data bank, converted to a + * <code>double</code>. The offset (specified in the constructor) is added + * to <code>i</code> before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public double getElemDouble(int i) + { + return getElem(i); + } + + /** + * Returns an element from a particular data bank, converted to a + * <code>double</code>. The offset (specified in the constructor) is + * added to <code>i</code> before accessing the underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public double getElemDouble(int bank, int i) + { + return getElem(bank, i); + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElemDouble(int i, double val) + { + setElem(i, (int) val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElemDouble(int bank, int i, double val) + { + setElem(bank, i, (int) val); + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferByte.java b/libjava/classpath/java/awt/image/DataBufferByte.java new file mode 100644 index 00000000000..1113ebb7843 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferByte.java @@ -0,0 +1,245 @@ +/* Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>byte</code> primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferByte extends DataBuffer +{ + private byte[] data; + private byte[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>byte</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferByte(int size) + { + super(TYPE_BYTE, size, 1, 0); + bankData = new byte[1][]; + data = new byte[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>byte</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferByte(int size, int numBanks) + { + super(TYPE_BYTE, size, numBanks); + bankData = new byte[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferByte(byte[] dataArray, int size) + { + super(TYPE_BYTE, size, 1, 0); + bankData = new byte[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferByte(byte[] dataArray, int size, int offset) + { + super(TYPE_BYTE, size, 1, offset); + bankData = new byte[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferByte(byte[][] dataArray, int size) + { + super(TYPE_BYTE, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferByte(byte[][] dataArray, int size, int[] offsets) + { + super(TYPE_BYTE, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public byte[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public byte[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public byte[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset] & 0xff; // get unsigned byte as int + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned byte as int + return bankData[bank][i+offsets[bank]] & 0xff; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (byte) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (byte) val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferDouble.java b/libjava/classpath/java/awt/image/DataBufferDouble.java new file mode 100644 index 00000000000..a8c4b9d0558 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferDouble.java @@ -0,0 +1,288 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>double</code> primitives + * to represent each of its banks. + * + * @since 1.4 + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class DataBufferDouble + extends DataBuffer +{ + private double[] data; + private double[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>double</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferDouble(int size) + { + super(TYPE_DOUBLE, size, 1, 0); + bankData = new double[1][]; + data = new double[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>double</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferDouble(int size, int numBanks) + { + super(TYPE_DOUBLE, size, numBanks); + bankData = new double[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferDouble(double[] dataArray, int size) + { + super(TYPE_DOUBLE, size, 1, 0); + bankData = new double[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferDouble(double[] dataArray, int size, int offset) + { + super(TYPE_DOUBLE, size, 1, offset); + bankData = new double[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferDouble(double[][] dataArray, int size) + { + super(TYPE_DOUBLE, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferDouble(double[][] dataArray, int size, int[] offsets) + { + super(TYPE_DOUBLE, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public double[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public double[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public double[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return (int) data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return (int) bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public float getElemFloat(int i) + { + return (float) data[i+offset]; + } + + public float getElemFloat(int bank, int i) + { + return (float) bankData[bank][i+offsets[bank]]; + } + + public void setElemFloat(int i, float val) + { + data[i+offset] = val; + } + + public void setElemFloat(int bank, int i, float val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public double getElemDouble(int i) + { + return data[i + offset]; + } + + public double getElemDouble(int bank, int i) + { + return bankData[bank][i + offsets[bank]]; + } + + public void setElemDouble(int i, double val) + { + data[i + offset] = val; + } + + public void setElemDouble(int bank, int i, double val) + { + bankData[bank][i + offsets[bank]] = val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferFloat.java b/libjava/classpath/java/awt/image/DataBufferFloat.java new file mode 100644 index 00000000000..9cf8784d78c --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferFloat.java @@ -0,0 +1,286 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>float</code> primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class DataBufferFloat + extends DataBuffer +{ + private float[] data; + private float[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>float</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferFloat(int size) + { + super(TYPE_FLOAT, size, 1, 0); + bankData = new float[1][]; + data = new float[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>float</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferFloat(int size, int numBanks) + { + super(TYPE_FLOAT, size, numBanks); + bankData = new float[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferFloat(float[] dataArray, int size) + { + super(TYPE_FLOAT, size, 1, 0); + bankData = new float[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferFloat(float[] dataArray, int size, int offset) + { + super(TYPE_FLOAT, size, 1, offset); + bankData = new float[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferFloat(float[][] dataArray, int size) + { + super(TYPE_FLOAT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferFloat(float[][] dataArray, int size, int[] offsets) + { + super(TYPE_FLOAT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public float[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public float[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public float[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return (int) data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return (int) bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public float getElemFloat(int i) + { + return data[i+offset]; + } + + public float getElemFloat(int bank, int i) + { + return bankData[bank][i+offsets[bank]]; + } + + public void setElemFloat(int i, float val) + { + data[i+offset] = val; + } + + public void setElemFloat(int bank, int i, float val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public double getElemDouble(int i) + { + return getElemFloat(i); + } + + public double getElemDouble(int bank, int i) + { + return getElemFloat(bank, i); + } + + public void setElemDouble(int i, double val) + { + setElemFloat(i, (float) val); + } + + public void setElemDouble(int bank, int i, double val) + { + setElemFloat(bank, i, (float) val); + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferInt.java b/libjava/classpath/java/awt/image/DataBufferInt.java new file mode 100644 index 00000000000..0aac9404067 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferInt.java @@ -0,0 +1,244 @@ +/* Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>int</code> primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferInt extends DataBuffer +{ + private int[] data; + private int[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>int</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferInt(int size) + { + super(TYPE_INT, size, 1, 0); + bankData = new int[1][]; + data = new int[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>int</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferInt(int size, int numBanks) + { + super(TYPE_INT, size, numBanks); + bankData = new int[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferInt(int[] dataArray, int size) + { + super(TYPE_INT, size, 1, 0); + bankData = new int[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferInt(int[] dataArray, int size, int offset) + { + super(TYPE_INT, size, 1, offset); + bankData = new int[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferInt(int[][] dataArray, int size) + { + super(TYPE_INT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferInt(int[][] dataArray, int size, int[] offsets) + { + super(TYPE_INT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public int[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public int[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public int[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The <code>offset</code> is + * added to the specified index before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The <code>offset</code> + * is added to the specified index before accessing the underlying data + * array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned int as int + return bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferShort.java b/libjava/classpath/java/awt/image/DataBufferShort.java new file mode 100644 index 00000000000..5c67a8d3c3f --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferShort.java @@ -0,0 +1,245 @@ +/* DataBufferShort.java -- + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>short</code> primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferShort extends DataBuffer +{ + private short[] data; + private short[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>short</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferShort(int size) + { + super(TYPE_SHORT, size, 1, 0); + bankData = new short[1][]; + data = new short[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>short</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferShort(int size, int numBanks) + { + super(TYPE_SHORT, size, numBanks); + bankData = new short[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferShort(short[] dataArray, int size) + { + super(TYPE_SHORT, size, 1, 0); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * <p> + * Note: there is no exception when <code>dataArray</code> is + * <code>null</code>, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferShort(short[] dataArray, int size, int offset) + { + super(TYPE_SHORT, size, 1, offset); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferShort(short[][] dataArray, int size) + { + super(TYPE_SHORT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferShort(short[][] dataArray, int size, int[] offsets) + { + super(TYPE_SHORT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public short[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public short[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public short[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (short) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (short) val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferUShort.java b/libjava/classpath/java/awt/image/DataBufferUShort.java new file mode 100644 index 00000000000..981e9e9d2fd --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferUShort.java @@ -0,0 +1,246 @@ +/* DataBufferUShort.java -- + Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of <code>short</code> primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferUShort extends DataBuffer +{ + private short[] data; + private short[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of <code>short</code> elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferUShort(int size) + { + super(TYPE_USHORT, size, 1, 0); + bankData = new short[1][]; + data = new short[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of <code>short</code> elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferUShort(int size, int numBanks) + { + super(TYPE_USHORT, size, numBanks); + bankData = new short[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is null + */ + public DataBufferUShort(short[] dataArray, int size) + { + super(TYPE_USHORT, size, 1, 0); + if (dataArray == null) + throw new NullPointerException(); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + * + * @throws NullPointerException if dataArray is null + */ + public DataBufferUShort(short[] dataArray, int size, int offset) + { + super(TYPE_USHORT, size, 1, offset); + if (dataArray == null) + throw new NullPointerException(); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferUShort(short[][] dataArray, int size) + { + super(TYPE_USHORT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if <code>dataArray</code> is + * <code>null</code>. + */ + public DataBufferUShort(short[][] dataArray, int size, int[] offsets) + { + super(TYPE_USHORT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public short[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public short[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this <code>DataBuffer</code>. + * + * @return The data banks. + */ + public short[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset] & 0xffff; // get unsigned short as int + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to <code>i</code> before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned short as int + return bankData[bank][i+offsets[bank]] & 0xffff; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (short) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to <code>i</code> before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (short) val; + } +} diff --git a/libjava/classpath/java/awt/image/DirectColorModel.java b/libjava/classpath/java/awt/image/DirectColorModel.java new file mode 100644 index 00000000000..c98c3f8ea70 --- /dev/null +++ b/libjava/classpath/java/awt/image/DirectColorModel.java @@ -0,0 +1,420 @@ +/* DirectColorModel.java -- + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.Point; +import java.awt.Transparency; +import java.awt.color.ColorSpace; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author C. Brian Jones (cbj@gnu.org) + * @author Mark Benvenuto (mcb54@columbia.edu) + */ +public class DirectColorModel extends PackedColorModel +{ + /** + * For the color model created with this constructor the pixels + * will have fully opaque alpha components with a value of 255. + * Each mask should describe a fully contiguous set of bits in the + * most likely order of alpha, red, green, blue from the most significant + * byte to the least significant byte. + * + * @param pixelBits the number of bits wide used for bit size of pixel values + * @param rmask the bits describing the red component of a pixel + * @param gmask the bits describing the green component of a pixel + * @param bmask the bits describing the blue component of a pixel + */ + public DirectColorModel(int pixelBits, int rmask, int gmask, int bmask) + { + this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, + rmask, gmask, bmask, 0, + false, // not alpha premultiplied + Buffers.smallestAppropriateTransferType(pixelBits) // find type + ); + } + + /** + * For the color model created with this constructor the pixels + * will have fully opaque alpha components with a value of 255. + * Each mask should describe a fully contiguous set of bits in the + * most likely order of red, green, blue from the most significant + * byte to the least significant byte. + * + * @param pixelBits the number of bits wide used for bit size of pixel values + * @param rmask the bits describing the red component of a pixel + * @param gmask the bits describing the green component of a pixel + * @param bmask the bits describing the blue component of a pixel + * @param amask the bits describing the alpha component of a pixel + */ + public DirectColorModel(int pixelBits, + int rmask, int gmask, int bmask, int amask) + { + this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, + rmask, gmask, bmask, amask, + false, // not alpha premultiplied + Buffers.smallestAppropriateTransferType(pixelBits) // find type + ); + } + + public DirectColorModel(ColorSpace cspace, int pixelBits, + int rmask, int gmask, int bmask, int amask, + boolean isAlphaPremultiplied, + int transferType) + { + super(cspace, pixelBits, + rmask, gmask, bmask, amask, isAlphaPremultiplied, + ((amask == 0) ? Transparency.OPAQUE : Transparency.TRANSLUCENT), + transferType); + } + + public final int getRedMask() + { + return getMask(0); + } + + public final int getGreenMask() + { + return getMask(1); + } + + public final int getBlueMask() + { + return getMask(2); + } + + public final int getAlphaMask() + { + return hasAlpha() ? getMask(3) : 0; + } + + /** + * Get the red component of the given pixel. + * <br> + */ + public final int getRed(int pixel) + { + return extractAndNormalizeSample(pixel, 0); + } + + /** + * Get the green component of the given pixel. + * <br> + */ + public final int getGreen(int pixel) + { + return extractAndNormalizeSample(pixel, 1); + } + + /** + * Get the blue component of the given pixel. + * <br> + */ + public final int getBlue(int pixel) + { + return extractAndNormalizeSample(pixel, 2); + } + + /** + * Get the alpha component of the given pixel. + * <br> + */ + public final int getAlpha(int pixel) + { + if (!hasAlpha()) + return 255; + return extractAndScaleSample(pixel, 3); + } + + private int extractAndNormalizeSample(int pixel, int component) + { + int value = extractAndScaleSample(pixel, component); + if (hasAlpha() && isAlphaPremultiplied()) + value = value*255/getAlpha(pixel); + return value; + } + + private int extractAndScaleSample(int pixel, int component) + { + int field = pixel & getMask(component); + int to8BitShift = + 8 - shifts[component] - getComponentSize(component); + return (to8BitShift>0) ? + (field << to8BitShift) : + (field >>> (-to8BitShift)); + } + + /** + * Get the RGB color value of the given pixel using the default + * RGB color model. + * <br> + * + * @param pixel a pixel value + */ + public final int getRGB(int pixel) + { + /* FIXME: The Sun docs show that this method is overridden, but I + don't see any way to improve on the superclass + implementation. */ + return super.getRGB(pixel); + } + + public int getRed(Object inData) + { + return getRed(getPixelFromArray(inData)); + } + + public int getGreen(Object inData) + { + return getGreen(getPixelFromArray(inData)); + } + + public int getBlue(Object inData) + { + return getBlue(getPixelFromArray(inData)); + } + + public int getAlpha(Object inData) + { + return getAlpha(getPixelFromArray(inData)); + } + + public int getRGB(Object inData) + { + return getRGB(getPixelFromArray(inData)); + } + + /** + * Converts a normalized pixel int value in the sRGB color + * space to an array containing a single pixel of the color space + * of the color model. + * + * <p>This method performs the inverse function of + * <code>getRGB(Object inData)</code>. + * + * @param rgb pixel as a normalized sRGB, 0xAARRGGBB value. + * + * @param pixel to avoid needless creation of arrays, an array to + * use to return the pixel can be given. If null, a suitable array + * will be created. + * + * @return array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + * + * @see #getRGB(Object) + */ + public Object getDataElements(int rgb, Object pixel) + { + // FIXME: handle alpha multiply + + int pixelValue = 0; + int a = 0; + if (hasAlpha()) { + a = (rgb >>> 24) & 0xff; + pixelValue = valueToField(a, 3, 8); + } + + if (hasAlpha() && isAlphaPremultiplied()) + { + int r, g, b; + /* if r=0xff and a=0xff, then resulting + value will be (r*a)>>>8 == 0xfe... This seems wrong. + We should divide by 255 rather than shifting >>>8 after + multiplying. + + Too bad, shifting is probably less expensive. + r = ((rgb >>> 16) & 0xff)*a; + g = ((rgb >>> 8) & 0xff)*a; + b = ((rgb >>> 0) & 0xff)*a; */ + /* The r, g, b values we calculate are 16 bit. This allows + us to avoid discarding the lower 8 bits obtained if + multiplying with the alpha band. */ + + // using 16 bit values + r = ((rgb >>> 8) & 0xff00)*a/255; + g = ((rgb >>> 0) & 0xff00)*a/255; + b = ((rgb << 8) & 0xff00)*a/255; + pixelValue |= + valueToField(r, 0, 16) | // Red + valueToField(g, 1, 16) | // Green + valueToField(b, 2, 16); // Blue + } + else + { + int r, g, b; + // using 8 bit values + r = (rgb >>> 16) & 0xff; + g = (rgb >>> 8) & 0xff; + b = (rgb >>> 0) & 0xff; + + pixelValue |= + valueToField(r, 0, 8) | // Red + valueToField(g, 1, 8) | // Green + valueToField(b, 2, 8); // Blue + } + + /* In this color model, the whole pixel fits in the first element + of the array. */ + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 1); + buffer.setElem(0, pixelValue); + return Buffers.getData(buffer); + } + + /** + * Converts a value to the correct field bits based on the + * information derived from the field masks. + * + * @param highBit the position of the most significant bit in the + * val parameter. + */ + private int valueToField(int val, int component, int highBit) + { + int toFieldShift = + getComponentSize(component) + shifts[component] - highBit; + int ret = (toFieldShift>0) ? + (val << toFieldShift) : + (val >>> (-toFieldShift)); + return ret & getMask(component); + } + + /** + * Converts a 16 bit value to the correct field bits based on the + * information derived from the field masks. + */ + private int value16ToField(int val, int component) + { + int toFieldShift = getComponentSize(component) + shifts[component] - 16; + return (toFieldShift>0) ? + (val << toFieldShift) : + (val >>> (-toFieldShift)); + } + + /** + * Fills an array with the unnormalized component samples from a + * pixel value. I.e. decompose the pixel, but not perform any + * color conversion. + */ + public final int[] getComponents(int pixel, int[] components, int offset) + { + int numComponents = getNumComponents(); + if (components == null) components = new int[offset + numComponents]; + + for (int b=0; b<numComponents; b++) + components[offset++] = (pixel&getMask(b)) >>> shifts[b]; + + return components; + } + + public final int[] getComponents(Object pixel, int[] components, + int offset) + { + return getComponents(getPixelFromArray(pixel), components, offset); + } + + public final WritableRaster createCompatibleWritableRaster(int w, int h) + { + SampleModel sm = createCompatibleSampleModel(w, h); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(sm, origin); + } + + public int getDataElement(int[] components, int offset) + { + int numComponents = getNumComponents(); + int pixelValue = 0; + + for (int c=0; c<numComponents; c++) + pixelValue |= (components[offset++] << shifts[c]) & getMask(c); + + return pixelValue; + } + + public Object getDataElements(int[] components, int offset, Object obj) + { + /* In this color model, the whole pixel fits in the first element + of the array. */ + int pixelValue = getDataElement(components, offset); + + DataBuffer buffer = Buffers.createBuffer(transferType, obj, 1); + buffer.setElem(0, pixelValue); + return Buffers.getData(buffer); + } + + public final ColorModel coerceData (WritableRaster raster, + boolean isAlphaPremultiplied) + { + if (this.isAlphaPremultiplied == isAlphaPremultiplied) + return this; + + /* TODO: provide better implementation based on the + assumptions we can make due to the specific type of the + color model. */ + super.coerceData(raster, isAlphaPremultiplied); + + return new ComponentColorModel(cspace, bits, hasAlpha(), + isAlphaPremultiplied, // argument + transparency, transferType); + } + + public boolean isCompatibleRaster(Raster raster) + { + /* FIXME: the Sun docs say this method is overridden here, + but I don't see any way to improve upon the implementation + in ColorModel. */ + return super.isCompatibleRaster(raster); + } + + String stringParam() + { + return super.stringParam() + + ", redMask=" + Integer.toHexString(getRedMask()) + + ", greenMask=" + Integer.toHexString(getGreenMask()) + + ", blueMask=" + Integer.toHexString(getBlueMask()) + + ", alphaMask=" + Integer.toHexString(getAlphaMask()); + } + + public String toString() + { + /* FIXME: Again, docs say override, but how do we improve upon the + superclass implementation? */ + return super.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/FilteredImageSource.java b/libjava/classpath/java/awt/image/FilteredImageSource.java new file mode 100644 index 00000000000..8893e86a625 --- /dev/null +++ b/libjava/classpath/java/awt/image/FilteredImageSource.java @@ -0,0 +1,125 @@ +/* FilteredImageSource.java -- Java class for providing image data + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; + +/** + * + * @see ImageConsumer + * @author C. Brian Jones (cbj@gnu.org) + */ +public class FilteredImageSource implements ImageProducer +{ + ImageProducer ip; + ImageFilter filter; + Hashtable consumers = new Hashtable(); + + /** + * The given filter is applied to the given image producer + * to create a new image producer. + */ + public FilteredImageSource(ImageProducer ip, ImageFilter filter) { + this.ip = ip; + this.filter = filter; + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code>. + */ + public synchronized void addConsumer(ImageConsumer ic) { + if (consumers.containsKey(ic)) + return; + + ImageFilter f = filter.getFilterInstance(ic); + consumers.put(ic, f); + ip.addConsumer(f); + } + + /** + * Used to determine if the given <code>ImageConsumer</code> is + * already registered with this <code>ImageProducer</code>. + */ + public synchronized boolean isConsumer(ImageConsumer ic) { + ImageFilter f = (ImageFilter)consumers.get(ic); + if (f != null) + return ip.isConsumer(f); + return false; + } + + /** + * Used to remove an <code>ImageConsumer</code> from the list of + * registered consumers for this <code>ImageProducer</code>. + */ + public synchronized void removeConsumer(ImageConsumer ic) { + ImageFilter f = (ImageFilter)consumers.remove(ic); + if (f != null) + ip.removeConsumer(f); + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then immediately start + * reconstruction of the image data to be delivered to all + * registered consumers. + */ + public void startProduction(ImageConsumer ic) { + ImageFilter f; + if (!(consumers.containsKey(ic))) { + f = filter.getFilterInstance(ic); + consumers.put(ic, f); + ip.addConsumer(f); + } else { + f = (ImageFilter)consumers.get( ic ); + } + ip.startProduction(f); + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then request that this producer + * resend the image data in the order top-down, left-right. + */ + public void requestTopDownLeftRightResend(ImageConsumer ic) { + ImageFilter f = (ImageFilter)consumers.get(ic); + ip.requestTopDownLeftRightResend(f); + } +} + diff --git a/libjava/classpath/java/awt/image/ImageConsumer.java b/libjava/classpath/java/awt/image/ImageConsumer.java new file mode 100644 index 00000000000..e1834c3978f --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageConsumer.java @@ -0,0 +1,216 @@ +/* ImageConsumer.java -- Java interface for image consumption + Copyright (C) 1999, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; + +/** + * An object implementing the <code>ImageProducer</code> interface can + * use objects implementing this interface to deliver the image data. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public interface ImageConsumer +{ + /** + * The pixel order may be random. This should be + * the default assumption of the <code>ImageConsumer</code>. + * + * @see #setHints + */ + int RANDOMPIXELORDER = 1; + + /** + * The pixel order is top-down, left-right. + * + * @see #setHints + */ + int TOPDOWNLEFTRIGHT = 2; + + /** + * The pixel order is in multiples of complete scanlines. + * + * @see #setHints + */ + int COMPLETESCANLINES = 4; + + /** + * The pixels will be delivered in a single pass. There is at + * most one call to <code>setPixels</code> for any single pixel. + * + * @see #setHints + * @see #setPixels + */ + int SINGLEPASS = 8; + + /** + * The pixels will be delivered with multiple calls to + * <code>setPixels</code>. The image contains a single frame + * which ends when <code>imageComplete</code> is called with the + * <code>STATICIMAGEDONE</code> flag. If the image is constantly + * changing such as with video then the end of each frame is + * marked by a similar call to <code>imageComplete</code> with the + * <code>SINGLEFRAMEDONE</code> flag. + * + * @see #setHints + * @see #imageComplete + */ + int SINGLEFRAME = 16; + + /** + * Indicates an error occurred while producing an image. + * + * @see #imageComplete + */ + int IMAGEERROR = 1; + + /** + * A single frame is complete but more will follow. + * + * @see #imageComplete + */ + int SINGLEFRAMEDONE = 2; + + /** + * The image is complete and no more pixels or frames will follow. + * + * @see #imageComplete + */ + int STATICIMAGEDONE = 3; + + /** + * Production of the image has been aborted. + * + * @see #imageComplete + */ + int IMAGEABORTED = 4; + + /** + * An <code>ImageProducer</code> indicates the size of the image + * being produced using this method. + * + * @param width the width of the image + * @param height the height of the image + */ + void setDimensions(int width, int height); + + /** + * An <code>ImageProducer</code> can set a list of properties + * associated with this image by using this method. + * + * @param props the list of properties associated with this image + */ + void setProperties(Hashtable props); + + /** + * This <code>ColorModel</code> should indicate the model used by + * the majority of calls to <code>setPixels</code>. Each call to + * <code>setPixels</code> could however indicate a different + * <code>ColorModel</code>. + * + * @param model the color model to be used most often by setPixels + * @see ColorModel + */ + void setColorModel(ColorModel model); + + /** + * The <code>ImageProducer</code> should call this method with a + * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, + * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, + * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>. + * + * @param flags a bit mask of hints + */ + void setHints(int flags); + + /** + * Deliver a subset of an ImageProducer's pixels to this ImageConsumer. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the block of + * pixels being delivered to this ImageConsumer. They are + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel block's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize); + + /** + * Deliver a subset of an ImageProducer's pixels to this ImageConsumer. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the rectangular + * region of pixels being delivered to this ImageConsumer, + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel region's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize); + + /** + * The <code>ImageProducer</code> calls this method to indicate a + * single frame or the entire image is complete. The method is + * also used to indicate an error in loading or producing the + * image. + * + * @param status the status of image production, represented by a + * bitwise OR of ImageConsumer flags + */ + void imageComplete(int status); +} diff --git a/libjava/classpath/java/awt/image/ImageFilter.java b/libjava/classpath/java/awt/image/ImageFilter.java new file mode 100644 index 00000000000..9940a2b1b91 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageFilter.java @@ -0,0 +1,221 @@ +/* ImageFilter.java -- Java class for filtering images + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; + +/** + * The <code>ImageFilter</code> class is a base class which can be + * extended to provide different types of filters for an image. By + * default this class does nothing to an image passing through it. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class ImageFilter implements ImageConsumer, Cloneable +{ + /** + * The consumer this filter is filtering an image data stream for. + * It is initialized in the method <code>getFilterInstance</code>. + */ + protected ImageConsumer consumer = null; + + /** + * The <code>ImageConsumer</code> can use this method to request + * the pixels be delivered in top-down, left-right order. + * <br> + * The filter can respond in three different ways. + * <ul> + * <li>The default behavior is to forward the request to the + * <code>ImageProducer</code> + * using the method <code>requestTopDownLeftRightResend</code> + * and using the filter as the consumer.</li> + * <li>The filter has the pixels and can retransmit them in the + * top-down, left-right order.</li> + * <li>The filter can do nothing when this method is called.</li> + * </ul> + */ + public void resendTopDownLeftRight(ImageProducer ip) + { + ip.requestTopDownLeftRightResend(this); + } + + /** + * By default, returns a shallow copy of the object created by + * <code>Object.clone()</code> + * + * @see java.lang.Object#clone () + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + // This should never happen as this class implements the + // Cloneable interface. + throw new InternalError (); + } + } + + /** + * This is the only method which can set the + * <code>ImageConsumer</code> for this filter. By default a clone + * of this filter with the appropriate consumer set is returned. + * + * @see #clone () + */ + public ImageFilter getFilterInstance(ImageConsumer ic) + { + if ( ic == null ) + throw new IllegalArgumentException("null argument for ImageFilter.getFilterInstance(ImageConsumer)"); + + consumer = ic; + ImageFilter f = (ImageFilter)clone(); + consumer = null; + return f; + } + + /** + * An <code>ImageProducer</code> indicates the size of the image + * being produced using this method. A filter can override this + * method to intercept these calls from the producer in order to + * change either the width or the height before in turn calling + * the consumer's <code>setDimensions</code> method. + * + * @param width the width of the image + * @param height the height of the image + */ + public void setDimensions(int width, int height) + { + consumer.setDimensions(width, height); + } + + /** + * An <code>ImageProducer</code> can set a list of properties + * associated with this image by using this method. + * + * @param props the list of properties associated with this image + */ + public void setProperties(Hashtable props) + { + props.put("filters", "ImageFilter"); + consumer.setProperties(props); + } + + /** + * Override this method to process calls to this method from the + * <code>ImageProducer</code>. By default the <code>setColorModel</code> + * method of the consumer is called with the specified <code>model</code>. + * + * @param model the color model to be used most often by setPixels + * @see ColorModel */ + public void setColorModel(ColorModel model) + { + consumer.setColorModel(model); + } + + /** + * The <code>ImageProducer</code> should call this method with a + * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, + * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, + * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code> from the + * <code>ImageConsumer</code> interface. + * + * @param flags a bit mask of hints + * @see ImageConsumer + */ + public void setHints(int flags) + { + consumer.setHints(flags); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a <code>byte</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an <code>int</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + + /** + * The <code>ImageProducer</code> calls this method to indicate a + * single frame or the entire image is complete. The method is + * also used to indicate an error in loading or producing the + * image. + */ + public void imageComplete(int status) + { + consumer.imageComplete(status); + } +} + diff --git a/libjava/classpath/java/awt/image/ImageObserver.java b/libjava/classpath/java/awt/image/ImageObserver.java new file mode 100644 index 00000000000..36dd013f4e9 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageObserver.java @@ -0,0 +1,129 @@ +/* ImageObserver.java -- Java interface for asynchronous updates to an image + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Image; + +/** + * An object implementing the <code>ImageObserver</code> interface can + * receive updates on image construction from an + * <code>ImageProducer</code> asynchronously. + * + * @see ImageProducer + * @author C. Brian Jones (cbj@gnu.org) + */ +public interface ImageObserver +{ + /** + * The width of the image has been provided as the + * <code>width</code> argument to <code>imageUpdate</code>. + * + * @see #imageUpdate + */ + int WIDTH = 1; + + /** + * The height of the image has been provided as the + * <code>height</code> argument to <code>imageUpdate</code>. + * + * @see #imageUpdate + */ + int HEIGHT = 2; + + /** + * The properties of the image have been provided. + * + * @see #imageUpdate + * @see java.awt.Image#getProperty (java.lang.String, java.awt.image.ImageObserver) + */ + int PROPERTIES = 4; + + /** + * More pixels are now available for drawing a scaled variation of + * the image. + * + * @see #imageUpdate + */ + int SOMEBITS = 8; + + /** + * All the pixels needed to draw a complete frame of a multi-frame + * image are available. + * + * @see #imageUpdate + */ + int FRAMEBITS = 16; + + /** + * An image with a single frame, a static image, is complete. + * + * @see #imageUpdate + */ + int ALLBITS = 32; + + /** + * An error was encountered while producing the image. + * + * @see #imageUpdate + */ + int ERROR = 64; + + /** + * Production of the image was aborted. + * + * @see #imageUpdate + */ + int ABORT = 128; + + /** + * This is a callback method for an asynchronous image producer to + * provide updates on the production of the image as it happens. + * + * @param image the image the update refers to + * @param flags a bit mask indicating what is provided with this update + * @param x the x coordinate of the image + * @param y the y coordinate of the image + * @param width the width of the image + * @param height the height of the image + * + * @see java.awt.Image + */ + boolean imageUpdate(Image image, int flags, int x, + int y, int width, int height); +} diff --git a/libjava/classpath/java/awt/image/ImageProducer.java b/libjava/classpath/java/awt/image/ImageProducer.java new file mode 100644 index 00000000000..49846685a3e --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageProducer.java @@ -0,0 +1,85 @@ +/* ImageProducer.java -- Java interface for image production + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * An object implementing the <code>ImageProducer</code> interface can + * produce data for images. Each image has a corresponding + * <code>ImageProducer</code> which is needed for things such as + * resizing the image. + * + * @see ImageConsumer + * @author C. Brian Jones (cbj@gnu.org) + */ +public interface ImageProducer +{ + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code>. + */ + void addConsumer(ImageConsumer ic); + + /** + * Used to determine if the given <code>ImageConsumer</code> is + * already registered with this <code>ImageProducer</code>. + */ + boolean isConsumer(ImageConsumer ic); + + /** + * Used to remove an <code>ImageConsumer</code> from the list of + * registered consumers for this <code>ImageProducer</code>. + */ + void removeConsumer(ImageConsumer ic); + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then immediately start + * reconstruction of the image data to be delivered to all + * registered consumers. + */ + void startProduction(ImageConsumer ic); + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then request that this producer + * resend the image data in the order top-down, left-right. + */ + void requestTopDownLeftRightResend(ImageConsumer ic); +} + diff --git a/libjava/classpath/java/awt/image/ImagingOpException.java b/libjava/classpath/java/awt/image/ImagingOpException.java new file mode 100644 index 00000000000..ca40e9ed365 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImagingOpException.java @@ -0,0 +1,66 @@ +/* ImagingOpException.java -- indicates an imaging filter failure + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This exception is thrown when <code>BufferedImageOp</code> or + * <code>RasterOp</code> filters cannot process an image. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see BufferedImageOp + * @see RasterOp + * @status updated to 1.4 + */ +public class ImagingOpException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8026288481846276658L; + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public ImagingOpException(String message) + { + super(message); + } +} // class ImagingOpException diff --git a/libjava/classpath/java/awt/image/IndexColorModel.java b/libjava/classpath/java/awt/image/IndexColorModel.java new file mode 100644 index 00000000000..299b4dc0d86 --- /dev/null +++ b/libjava/classpath/java/awt/image/IndexColorModel.java @@ -0,0 +1,697 @@ +/* IndexColorModel.java -- Java class for interpreting Pixel objects + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.color.ColorSpace; +import java.math.BigInteger; + +/** + * Color model similar to pseudo visual in X11. + * <br><br> + * This color model maps linear pixel values to actual RGB and alpha colors. + * Thus, pixel values are indexes into the color map. Each color component is + * an 8-bit unsigned value. + * <br><br> + * The <code>IndexColorModel</code> supports a map of valid pixels, allowing + * the representation of holes in the the color map. The valid map is + * represented as a {@link BigInteger} where each bit indicates the validity + * of the map entry with the same index. + * <br><br> + * Colors can have alpha components for transparency support. If alpha + * component values aren't given, color values are opaque. The model also + * supports a reserved pixel value to represent completely transparent colors, + * no matter what the actual color component values are. + * <br><br> + * <code>IndexColorModel</code> supports anywhere from 1 to 16 bit index + * values. The allowed transfer types are {@link DataBuffer#TYPE_BYTE} and + * {@link DataBuffer#TYPE_USHORT}. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class IndexColorModel extends ColorModel +{ + private int map_size; + private boolean opaque; // no alpha, but doesn't account for trans + private int trans = -1; + private int[] rgb; + private BigInteger validBits = BigInteger.ZERO; + + /** + * Creates a new indexed color model for <code>size</code> color elements + * with no alpha component. Each array must contain at least + * <code>size</code> elements. For each array, the i-th color is described + * by reds[i], greens[i] and blues[i]. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors. + * @param size the number of colors in the color map. + * @param reds the red component of all colors. + * @param greens the green component of all colors. + * @param blues the blue component of all colors. + * + * @throws IllegalArgumentException if <code>bits</code> < 1 or + * <code>bits</code> > 16. + * @throws NullPointerException if any of the arrays is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues) + { + this(bits, size, reds, greens, blues, (byte[]) null); + } + + /** + * Creates a new indexed color model for <code>size</code> color elements. + * Each array must contain at least <code>size</code> elements. For each + * array, the i-th color is described by reds[i], greens[i] and blues[i]. + * All the colors are opaque except for the transparent color. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors + * @param size the number of colors in the color map + * @param reds the red component of all colors + * @param greens the green component of all colors + * @param blues the blue component of all colors + * @param trans the index of the transparent color (use -1 for no + * transparent color). + * + * @throws IllegalArgumentException if <code>bits</code> < 1 or + * <code>bits</code> > 16. + * @throws NullPointerException if any of the arrays is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues, int trans) + { + super(bits, nArray(8, (0 <= trans && trans < size) ? 4 : 3), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + (0 <= trans && trans < size), // hasAlpha + false, OPAQUE, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + if (0 <= trans && trans < size) { + this.trans = trans; + transparency = BITMASK; + } + rgb = new int[size]; + for (int i = 0; i < size; i++) + { + rgb[i] = (0xff000000 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Creates a new indexed color model for <code>size</code> color elements + * including alpha. Each array must contain at least <code>size</code> + * elements. For each array, the i-th color is described + * by reds[i], greens[i], blues[i] and alphas[i]. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors. + * @param size the number of colors in the color map. + * @param reds the red component of all colors. + * @param greens the green component of all colors. + * @param blues the blue component of all colors. + * @param alphas the alpha component of all colors (<code>null</code> + * permitted). + * + * @throws IllegalArgumentException if <code>bits</code> < 1 or + * <code>bits</code> > 16. + * @throws NullPointerException if <code>reds</code>, <code>greens</code> or + * <code>blues</code> is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>size</code> is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues, byte[] alphas) + { + super(bits, nArray(8, (alphas == null ? 3 : 4)), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + (alphas != null), false, TRANSLUCENT, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = (alphas == null); + + rgb = new int[size]; + if (alphas == null) + { + for (int i = 0; i < size; i++) + { + rgb[i] = (0xff000000 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + transparency = OPAQUE; + } + else + { + byte alphaZero = (byte) 0x00; + byte alphaOne = (byte) 0xFF; + for (int i = 0; i < size; i++) + { + alphaZero = (byte) (alphaZero | alphas[i]); + alphaOne = (byte) (alphaOne & alphas[i]); + rgb[i] = ((alphas[i] & 0xff) << 24 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + if ((alphaZero == (byte) 0x00) || (alphaOne == (byte) 0xFF)) + transparency = BITMASK; + else + transparency = TRANSLUCENT; + } + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Creates a new indexed color model using the color components in + * <code>cmap</code>. If <code>hasAlpha</code> is <code>true</code> then + * <code>cmap</code> contains an alpha component after each of the red, green + * and blue components. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors + * @param size the number of colors in the color map + * @param cmap packed color components + * @param start the offset of the first color component in <code>cmap</code> + * @param hasAlpha <code>cmap</code> has alpha values + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws NullPointerException if <code>cmap</code> is <code>null</code>. + */ + public IndexColorModel(int bits, int size, byte[] cmap, int start, + boolean hasAlpha) + { + this(bits, size, cmap, start, hasAlpha, -1); + } + + /** + * Construct an IndexColorModel from an array of red, green, blue, and + * optional alpha components. The component values are interleaved as RGB(A). + * + * @param bits the number of bits needed to represent <code>size</code> + * colors + * @param size the number of colors in the color map + * @param cmap interleaved color components + * @param start the offset of the first color component in <code>cmap</code> + * @param hasAlpha <code>cmap</code> has alpha values + * @param trans the index of the transparent color + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws NullPointerException if <code>cmap</code> is <code>null</code>. + */ + public IndexColorModel(int bits, int size, byte[] cmap, int start, + boolean hasAlpha, int trans) + { + super(bits, nArray(8, hasAlpha || (0 <= trans && trans < size) ? 4 : 3), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + hasAlpha || (0 <= trans && trans < size), false, OPAQUE, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = !hasAlpha; + if (0 <= trans && trans < size) + this.trans = trans; + + rgb = new int[size]; + if (hasAlpha) + { + int alpha; + int alphaZero = 0x00; // use to detect all zeros + int alphaOne = 0xff; // use to detect all ones + for (int i = 0; i < size; i++) { + alpha = cmap[4 * i + 3 + start] & 0xff; + alphaZero = alphaZero | alpha; + alphaOne = alphaOne & alpha; + rgb[i] = + ( alpha << 24 + // red + | ((cmap[4 * i + start] & 0xff) << 16) + // green + | ((cmap[4 * i + 1 + start] & 0xff) << 8) + // blue + | (cmap[4 * i + 2 + start] & 0xff)); + } + if (alphaZero == 0) + transparency = BITMASK; + else if (alphaOne == 255) + transparency = (trans != -1 ? BITMASK : OPAQUE); + else + transparency = TRANSLUCENT; + } + else + { + for (int i = 0; i < size; i++) + rgb[i] = (0xff000000 + // red + | ((cmap[3 * i + start] & 0xff) << 16) + // green + | ((cmap[3 * i + 1 + start] & 0xff) << 8) + // blue + | (cmap[3 * i + 2 + start] & 0xff)); + if (trans != -1) + transparency = BITMASK; + } + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Construct an IndexColorModel from an array of <code>size</code> packed + * colors. Each int element contains 8-bit red, green, blue, and optional + * alpha values packed in order. If hasAlpha is false, then all the colors + * are opaque except for the transparent color. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors + * @param size the number of colors in the color map + * @param cmap packed color components + * @param start the offset of the first color component in <code>cmap</code> + * @param hasAlpha <code>cmap</code> has alpha values + * @param trans the index of the transparent color + * @param transferType {@link DataBuffer#TYPE_BYTE} or + {@link DataBuffer#TYPE_USHORT}. + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws IllegalArgumentException if <code>transferType</code> is something + * other than {@link DataBuffer#TYPE_BYTE} or + * {@link DataBuffer#TYPE_USHORT}. + */ + public IndexColorModel(int bits, int size, int[] cmap, int start, + boolean hasAlpha, int trans, int transferType) + { + super(bits, + nArray(8, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, transferType); + if (transferType != DataBuffer.TYPE_BYTE + && transferType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException(); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = !hasAlpha; + if (0 <= trans && trans < size) + this.trans = trans; + + rgb = new int[size]; + if (!hasAlpha) + for (int i = 0; i < size; i++) + rgb[i] = cmap[i + start] | 0xff000000; + else + System.arraycopy(cmap, start, rgb, 0, size); + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Construct an IndexColorModel using a colormap with holes. + * <br><br> + * The IndexColorModel is built from the array of ints defining the + * colormap. Each element contains red, green, blue, and alpha + * components. The ColorSpace is sRGB. The transparency value is + * automatically determined. + * <br><br> + * This constructor permits indicating which colormap entries are valid, + * using the validBits argument. Each entry in cmap is valid if the + * corresponding bit in validBits is set. + * + * @param bits the number of bits needed to represent <code>size</code> + * colors. + * @param size the number of colors in the color map. + * @param cmap packed color components. + * @param start the offset of the first color component in <code>cmap</code>. + * @param transferType {@link DataBuffer#TYPE_BYTE} or + * {@link DataBuffer#TYPE_USHORT}. + * @param validBits a map of the valid entries in <code>cmap</code>. + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws IllegalArgumentException if transferType is something other than + * {@link DataBuffer#TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT}. + */ + public IndexColorModel(int bits, int size, int[] cmap, int start, + int transferType, BigInteger validBits) + { + super(bits, // total bits, sRGB, four channels + nArray(8, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, transferType); + if (transferType != DataBuffer.TYPE_BYTE + && transferType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException(); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = false; + this.trans = -1; + this.validBits = validBits; + + rgb = new int[size]; + if (!hasAlpha) + for (int i = 0; i < size; i++) + rgb[i] = cmap[i + start] | 0xff000000; + else + System.arraycopy(cmap, start, rgb, 0, size); + } + + /** + * Returns the size of the color lookup table. + * + * @return The size of the color lookup table. + */ + public final int getMapSize() + { + return map_size; + } + + /** + * Get the index of the transparent color in this color model. + * + * @return The index of the color that is considered transparent, or -1 if + * there is no transparent color. + */ + public final int getTransparentPixel() + { + return trans; + } + + /** + * Fills the supplied array with the red component of each color in the + * lookup table. + * + * @param r an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if <code>r</code> is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>r</code> has less + * than {@link #getMapSize()} elements. + */ + public final void getReds(byte[] r) + { + int i; + for (i = 0; i < map_size; i++) + r[i] = (byte) ((0x00FF0000 & rgb[i]) >> 16); + } + + /** + * Fills the supplied array with the green component of each color in the + * lookup table. + * + * @param g an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if <code>g</code> is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>g</code> has less + * than {@link #getMapSize()} elements. + */ + public final void getGreens(byte[] g) + { + int i; + for (i = 0; i < map_size; i++) + g[i] = (byte) ((0x0000FF00 & rgb[i]) >> 8); + } + + /** + * Fills the supplied array with the blue component of each color in the + * lookup table. + * + * @param b an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if <code>b</code> is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>b</code> has less + * than {@link #getMapSize()} elements. + */ + public final void getBlues(byte[] b) + { + int i; + for (i = 0; i < map_size; i++) + b[i] = (byte) (0x000000FF & rgb[i]); + } + + /** + * Fills the supplied array with the alpha component of each color in the + * lookup table. If the model has a transparent pixel specified, the alpha + * for that pixel will be 0. + * + * @param a an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * @throws ArrayIndexOutOfBoundsException if <code>a</code> has less + * than {@link #getMapSize()} elements. + */ + public final void getAlphas(byte[] a) + { + int i; + for (i = 0; i < map_size; i++) + if (i == trans) + a[i] = (byte) 0; + else + a[i] = (byte) ((0xFF000000 & rgb[i]) >> 24); + } + + /** + * Returns the red component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The red component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative. + */ + public final int getRed(int pixel) + { + if (pixel < map_size) + return (0x00FF0000 & rgb[pixel]) >> 16; + + return 0; + } + + /** + * Returns the green component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The green component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative. + */ + public final int getGreen(int pixel) + { + if (pixel < map_size) + return (0x0000FF00 & rgb[pixel]) >> 8; + + return 0; + } + + /** + * Returns the blue component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The blue component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative. + */ + public final int getBlue(int pixel) + { + if (pixel < map_size) + return 0x000000FF & rgb[pixel]; + + return 0; + } + + /** + * Returns the alpha component of the color in the lookup table for the + * given pixel value. If no alpha channel was specified when the color model + * was created, then 255 is returned for all pixels except the transparent + * pixel (if one is defined - see {@link #getTransparentPixel()}) which + * returns an alpha of 0. + * + * @param pixel the pixel lookup value. + * + * @return The alpha component of the color in the lookup table (in the + * range 0 to 255). + * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative. + */ + public final int getAlpha(int pixel) + { + if (opaque && pixel != trans) + return 255; + if ((pixel == trans && trans != -1) || pixel >= map_size) + return 0; + + return (0xFF000000 & rgb[pixel]) >> 24; + } + + /** + * Get the RGB color value of the given pixel using the default + * RGB color model. + * + * @param pixel the pixel lookup value. + * @return The RGB color value. + * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is negative. + */ + public final int getRGB(int pixel) + { + if (pixel >= 0 && pixel < map_size) + return rgb[pixel]; + + return 0; + } + + /** + * Get the RGB color values of all pixels in the map using the default + * RGB color model. + * + * @param rgb The destination array. + */ + public final void getRGBs(int[] rgb) + { + System.arraycopy(this.rgb, 0, rgb, 0, map_size); + } + + /** + * Return <code>true</code> if the lookup table contains valid data for + * <code>pixel</code>, and <code>false</code> otherwise. + * + * @param pixel the pixel value used to index the color lookup table. + * @return <code>true</code> if <code>pixel</code> is valid, + * <code>false</code> otherwise. + */ + public boolean isValid(int pixel) + { + if (pixel >= 0) + return validBits.testBit(pixel); + return false; + } + + /** + * Return <code>true</code> if all pixels are valid, <code>false</code> + * otherwise. + * + * @return <code>true</code> if all pixels are valid, <code>false</code> + * otherwise. + */ + public boolean isValid() + { + // Generate a bigint with 1's for every pixel + BigInteger allbits = new BigInteger("0"); + allbits = allbits.setBit(map_size); + allbits = allbits.subtract(new BigInteger("1")); + return allbits.equals(validBits); + } + + /** + * Returns a binary value ({@link BigInteger}) where each bit represents an + * entry in the color lookup table. If the bit is on, the entry is valid. + * + * @return The binary value. + */ + public BigInteger getValidPixels() + { + return validBits; + } + + /** + * Construct a {@link BufferedImage} with rgb pixel values from a + * {@link Raster}. + * + * Constructs a new BufferedImage in which each pixel is an RGBA int from + * a Raster with index-valued pixels. If this model has no alpha component + * or transparent pixel, the type of the new BufferedImage is TYPE_INT_RGB. + * Otherwise the type is TYPE_INT_ARGB. If forceARGB is true, the type is + * forced to be TYPE_INT_ARGB no matter what. + * + * @param raster The source of pixel values. + * @param forceARGB True if type must be TYPE_INT_ARGB. + * @return New BufferedImage with RBGA int pixel values. + */ + public BufferedImage convertToIntDiscrete(Raster raster, boolean forceARGB) + { + int type = forceARGB ? BufferedImage.TYPE_INT_ARGB + : ((opaque && trans == -1) ? BufferedImage.TYPE_INT_RGB : + BufferedImage.TYPE_INT_ARGB); + + // FIXME: assuming that raster has only 1 band since pixels are supposed + // to be int indexes. + // FIXME: it would likely be more efficient to fetch a complete array, + // but it would take much more memory. + // FIXME: I'm not sure if transparent pixels or alpha values need special + // handling here. + BufferedImage im = new BufferedImage(raster.width, raster.height, type); + for (int x = raster.minX; x < raster.width + raster.minX; x++) + for (int y = raster.minY; y < raster.height + raster.minY; y++) + im.setRGB(x, y, rgb[raster.getSample(x, y, 0)]); + + return im; + } +} diff --git a/libjava/classpath/java/awt/image/Kernel.java b/libjava/classpath/java/awt/image/Kernel.java new file mode 100644 index 00000000000..f7c29c3cde9 --- /dev/null +++ b/libjava/classpath/java/awt/image/Kernel.java @@ -0,0 +1,143 @@ +/* Kernel.java -- Java class for an image processing kernel + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * Kernel represents an image processing kernel. It gets used to hold + * convolution filters among other purposes. It stores an array of float + * values representing a 2-dimensional array in row-major order. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public class Kernel implements Cloneable +{ + private final int width; + private final int height; + private final float[] data; + + /** + * Creates a new <code>Kernel</code> instance. + * + * @param width The 2D width of data. + * @param height The 2D height of data. + * @param data The source data array. + * @exception IllegalArgumentException if width * height < data.length. + */ + public Kernel(int width, int height, float[] data) + throws IllegalArgumentException + { + this.width = width; + this.height = height; + if (data.length < width * height || width < 0 || height < 0) + throw new IllegalArgumentException(); + this.data = new float[width * height]; + System.arraycopy(data, 0, this.data, 0, width * height); + } + + /** + * Return the X origin: (width - 1) / 2 + */ + public final int getXOrigin() + { + return (width - 1) / 2; + } + + /** + * Return the Y origin: (height - 1) / 2 + */ + public final int getYOrigin() + { + return (height - 1) / 2; + } + + /** + * @return The kernel width. + */ + public final int getWidth() + { + return width; + } + + /** + * @return The kernel height. + */ + public final int getHeight() + { + return height; + } + + /** + * Return the kernel data. + * + * If data is null, allocates a new array and returns it. Otherwise, the + * kernel values are copied into data. + * + * @param data Array to copy values into, or null. + * @return The array with copied values. + * @exception IllegalArgumentException if data != null and too small. + */ + public final float[] getKernelData(float[] data) + throws IllegalArgumentException + { + if (data == null) + return (float[])this.data.clone(); + + if (data.length < this.data.length) + throw new IllegalArgumentException(); + + System.arraycopy(this.data, 0, data, 0, this.data.length); + return data; + } + + /** + * @return a clone of this Kernel. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} diff --git a/libjava/classpath/java/awt/image/LookupOp.java b/libjava/classpath/java/awt/image/LookupOp.java new file mode 100644 index 00000000000..f131daabae3 --- /dev/null +++ b/libjava/classpath/java/awt/image/LookupOp.java @@ -0,0 +1,252 @@ +/* LookupOp.java -- Filter that converts each pixel using a lookup table. + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * LookupOp is a filter that converts each pixel using a lookup table. + * + * For filtering Rasters, the lookup table must have either one component + * that is applied to all bands, or one component for every band in the + * Rasters. + * + * For BufferedImages, the lookup table may apply to both color and alpha + * components. If the lookup table contains one component, or if there are + * the same number of components as color components in the source, the table + * applies to all color components. Otherwise the table applies to all + * components including alpha. Alpha premultiplication is ignored during the + * lookup filtering. + * + * After filtering, if color conversion is necessary, the conversion happens, + * taking alpha premultiplication into account. + * + * @author jlquinn + */ +public class LookupOp implements BufferedImageOp, RasterOp +{ + private LookupTable lut; + private RenderingHints hints; + + /** Construct a new LookupOp. + * + * @param lookup LookupTable to use. + * @param hints Rendering hints (can be null). + */ + public LookupOp(LookupTable lookup, RenderingHints hints) + { + lut = lookup; + this.hints = hints; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage) + */ + public BufferedImage filter(BufferedImage src, BufferedImage dst) + { + if (src.getColorModel() instanceof IndexColorModel) + throw new IllegalArgumentException("LookupOp.filter: IndexColorModel " + + "not allowed"); + if (dst == null) + dst = createCompatibleDestImage(src, src.getColorModel()); + + // Set up for potential colormodel mismatch + BufferedImage tgt; + if (dst.getColorModel().equals(src.getColorModel())) + tgt = dst; + else + tgt = createCompatibleDestImage(src, src.getColorModel()); + + Raster sr = src.getRaster(); + WritableRaster dr = tgt.getRaster(); + + if (src.getColorModel().hasAlpha() && + (lut.getNumComponents() == 1 || + lut.getNumComponents() == src.getColorModel().getNumColorComponents())) + { + // Need to ignore alpha for lookup + int[] dbuf = new int[src.getColorModel().getNumComponents()]; + int tmpBands = src.getColorModel().getNumColorComponents(); + int[] tmp = new int[tmpBands]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + { + // Filter only color components, but also copy alpha + sr.getPixel(x, y, dbuf); + System.arraycopy(dbuf, 0, tmp, 0, tmpBands); + dr.setPixel(x, y, lut.lookupPixel(tmp, dbuf)); + } + } + else if (lut.getNumComponents() != 1 + && + lut.getNumComponents() != src.getColorModel().getNumComponents()) + throw new IllegalArgumentException("LookupOp.filter: " + + "Incompatible lookup " + + "table and source image"); + + // No alpha to ignore + int[] dbuf = new int[src.getColorModel().getNumComponents()]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf)); + + if (tgt != dst) + { + // Convert between color models. + // TODO Check that premultiplied alpha is handled correctly here. + Graphics2D gg = dst.createGraphics(); + gg.setRenderingHints(hints); + gg.drawImage(tgt, 0, 0, null); + gg.dispose(); + } + + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + // FIXME: set properties to those in src + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isPremultiplied, null); + } + + /** Return corresponding destination point for source point. + * + * LookupOp will return the value of src unchanged. + * @param src The source point. + * @param dst The destination point. + * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + */ + public Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) + return (Point2D) src.clone(); + + dst.setLocation(src); + return dst; + } + + /** Return the LookupTable for this op. */ + public LookupTable getTable() + { + return lut; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public RenderingHints getRenderingHints() + { + return hints; + } + + /** Filter a raster through a lookup table. + * + * Applies the lookup table for this Rasterop to each pixel of src and + * puts the results in dest. If dest is null, a new Raster is created and + * returned. + * + * @param src The source raster. + * @param dest The destination raster. + * @return The WritableRaster with the filtered pixels. + * @throws IllegalArgumentException if lookup table has more than one + * component but not the same as src and dest. + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + */ + public WritableRaster filter(Raster src, WritableRaster dest) + { + if (dest == null) + // Allocate a raster if needed + dest = createCompatibleDestRaster(src); + else + if (src.getNumBands() != dest.getNumBands()) + throw new IllegalArgumentException(); + + if (lut.getNumComponents() != 1 + && lut.getNumComponents() != src.getNumBands()) + throw new IllegalArgumentException(); + + + // Allocate pixel storage. + int[] tmp = new int[src.getNumBands()]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dest.setPixel(x, y, lut.lookupPixel(src.getPixel(x, y, tmp), tmp)); + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + +} diff --git a/libjava/classpath/java/awt/image/LookupTable.java b/libjava/classpath/java/awt/image/LookupTable.java new file mode 100644 index 00000000000..f814b8e090b --- /dev/null +++ b/libjava/classpath/java/awt/image/LookupTable.java @@ -0,0 +1,109 @@ +/* LookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * LookupTable represents translation arrays for pixel values. It wraps one + * or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @see ByteLookupTable + * @see ShortLookupTable + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public abstract class LookupTable +{ + // Not protected since that's part of the public API. + int offset; + int numComponents; + + /** + * Creates a new <code>LookupTable</code> instance. + * + * If numComponents is 1, the same translation table is used for all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param numComponents Number of image components. + * @exception IllegalArgumentException if offset < 0 or numComponents < 1. + */ + protected LookupTable(int offset, int numComponents) + throws IllegalArgumentException + { + if (offset < 0 || numComponents < 1) + throw new IllegalArgumentException(); + this.offset = offset; + this.numComponents = numComponents; + } + + /** Return the number of components. */ + public int getNumComponents() + { + return numComponents; + } + + /** Return the offset. */ + public int getOffset() + { + return offset; + } + + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dest Destination array for values, or null. + * @return Translated values for the pixel. + */ + public abstract int[] lookupPixel(int[] src, int[] dest); +} diff --git a/libjava/classpath/java/awt/image/MemoryImageSource.java b/libjava/classpath/java/awt/image/MemoryImageSource.java new file mode 100644 index 00000000000..c27e0bf7317 --- /dev/null +++ b/libjava/classpath/java/awt/image/MemoryImageSource.java @@ -0,0 +1,373 @@ +/* MemoryImageSource.java -- Java class for providing image data + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; +import java.util.Vector; + +public class MemoryImageSource implements ImageProducer +{ + private boolean animated = false; + private boolean fullbuffers = false; + private int[] pixeli; + private int width; + private int height; + private int offset; + private int scansize; + private byte[] pixelb; + private ColorModel cm; + private Hashtable props = new Hashtable(); + private Vector consumers = new Vector(); + + /** + * Construct an image producer that reads image data from a byte + * array. + * + * @param w width of image + * @param h height of image + * @param cm the color model used to represent pixel values + * @param pix a byte array of pixel values + * @param off the offset into the array at which the first pixel is stored + * @param scan the number of array elements that represents a single pixel row + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off, + int scan) + { + this(w, h, cm, pix, off, scan, null); + } + + /** + * Constructs an ImageProducer from memory + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off, + int scan, Hashtable props) + { + width = w; + height = h; + this.cm = cm; + offset = off; + scansize = scan; + this.props = props; + int max = ((scansize > width) ? scansize : width); + pixelb = pix; + } + + /** + * Construct an image producer that reads image data from an + * integer array. + * + * @param w width of image + * @param h height of image + * @param cm the color model used to represent pixel values + * @param pix an integer array of pixel values + * @param off the offset into the array at which the first pixel is stored + * @param scan the number of array elements that represents a single pixel row + */ + public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, + int scan) + { + this(w, h, cm, pix, off, scan, null); + } + + /** + Constructs an ImageProducer from memory + */ + public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, + int scan, Hashtable props) + { + width = w; + height = h; + this.cm = cm; + offset = off; + scansize = scan; + this.props = props; + int max = ((scansize > width) ? scansize : width); + pixeli = pix; + } + + /** + * Constructs an ImageProducer from memory using the default RGB ColorModel + */ + public MemoryImageSource(int w, int h, int[] pix, int off, int scan, + Hashtable props) + { + this(w, h, ColorModel.getRGBdefault(), pix, off, scan, props); + } + + /** + * Constructs an ImageProducer from memory using the default RGB ColorModel + */ + public MemoryImageSource(int w, int h, int[] pix, int off, int scan) + { + this(w, h, ColorModel.getRGBdefault(), pix, off, scan, null); + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code>. + */ + public synchronized void addConsumer(ImageConsumer ic) + { + if (consumers.contains(ic)) + return; + + consumers.addElement(ic); + } + + /** + * Used to determine if the given <code>ImageConsumer</code> is + * already registered with this <code>ImageProducer</code>. + */ + public synchronized boolean isConsumer(ImageConsumer ic) + { + if (consumers.contains(ic)) + return true; + return false; + } + + /** + * Used to remove an <code>ImageConsumer</code> from the list of + * registered consumers for this <code>ImageProducer</code>. + */ + public synchronized void removeConsumer(ImageConsumer ic) + { + consumers.removeElement(ic); + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then immediately start + * reconstruction of the image data to be delivered to all + * registered consumers. + */ + public void startProduction(ImageConsumer ic) + { + if (! (consumers.contains(ic))) + consumers.addElement(ic); + + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + sendPicture(ic); + if (animated) + ic.imageComplete(ImageConsumer.SINGLEFRAME); + else + ic.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + + /** + * Used to register an <code>ImageConsumer</code> with this + * <code>ImageProducer</code> and then request that this producer + * resend the image data in the order top-down, left-right. + */ + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + /** + * Changes a flag to indicate whether this MemoryImageSource supports + * animations. + * + * @param animated A flag indicating whether this class supports animations + */ + public synchronized void setAnimated(boolean animated) + { + this.animated = animated; + } + + /** + * A flag to indicate whether or not to send full buffer updates when + * sending animation. If this flag is set then full buffers are sent + * in the newPixels methods instead of just regions. + * + * @param fullbuffers - a flag indicating whether to send the full buffers + */ + public synchronized void setFullBufferUpdates(boolean fullbuffers) + { + this.fullbuffers = fullbuffers; + } + + /** + * Send an animation frame to the image consumers. + */ + public void newPixels() + { + if (animated == true) + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + sendPicture(ic); + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + + private void sendPicture(ImageConsumer ic) + { + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + ic.setDimensions(width, height); + ic.setColorModel(cm); + if (pixeli != null) + ic.setPixels(0, 0, width, height, cm, pixeli, offset, scansize); + else + ic.setPixels(0, 0, width, height, cm, pixelb, offset, scansize); + } + + /** + * Send an animation frame to the image consumers containing the specified + * pixels unless setFullBufferUpdates is set. + */ + public synchronized void newPixels(int x, int y, int w, int h) + { + if (animated == true) + { + if (fullbuffers) + newPixels(); + else + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + if (pixeli != null) + { + int[] pixelbuf = new int[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixeli, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + else + { + byte[] pixelbuf = new byte[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixelb, row * scansize + x + offset, + pixelbuf, 0, w * h); + + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + } + + /** + * Send an animation frame to the image consumers containing the specified + * pixels unless setFullBufferUpdates is set. + * + * If framenotify is set then a notification is sent when the frame + * is sent otherwise no status is sent. + */ + public synchronized void newPixels(int x, int y, int w, int h, + boolean framenotify) + { + if (animated == true) + { + if (fullbuffers) + newPixels(); + else + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + if (pixeli != null) + { + int[] pixelbuf = new int[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixeli, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + else + { + byte[] pixelbuf = new byte[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixelb, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + if (framenotify == true) + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + } + + public synchronized void newPixels(byte[] newpix, ColorModel newmodel, + int offset, int scansize) + { + pixeli = null; + pixelb = newpix; + cm = newmodel; + this.offset = offset; + this.scansize = scansize; + if (animated == true) + newPixels(); + } + + public synchronized void newPixels(int[] newpix, ColorModel newmodel, + int offset, int scansize) + { + pixelb = null; + pixeli = newpix; + cm = newmodel; + this.offset = offset; + this.scansize = scansize; + if (animated == true) + newPixels(); + } +} diff --git a/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java new file mode 100644 index 00000000000..18a6e555205 --- /dev/null +++ b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java @@ -0,0 +1,388 @@ +/* Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; + +/** + * MultiPixelPackedSampleModel provides a single band model that supports + * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit + * per data element. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class MultiPixelPackedSampleModel extends SampleModel +{ + private int scanlineStride; + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + private int dataBitOffset; + private int elemBits; + private int numberOfBits; + private int numElems; + + public MultiPixelPackedSampleModel(int dataType, int w, int h, + int numberOfBits) + { + this(dataType, w, h, numberOfBits, 0, 0); + } + + public MultiPixelPackedSampleModel(int dataType, int w, int h, + int numberOfBits, int scanlineStride, + int dataBitOffset) + { + super(dataType, w, h, 1); + + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + elemBits = 8; + break; + case DataBuffer.TYPE_USHORT: + elemBits = 16; + break; + case DataBuffer.TYPE_INT: + elemBits = 32; + break; + default: + throw new IllegalArgumentException("MultiPixelPackedSampleModel" + + " unsupported dataType"); + } + + this.dataBitOffset = dataBitOffset; + + this.numberOfBits = numberOfBits; + if (numberOfBits > elemBits) + throw new RasterFormatException("MultiPixelPackedSampleModel pixel size" + + " larger than dataType"); + switch (numberOfBits) + { + case 1: case 2: case 4: case 8: case 16: case 32: break; + default: + throw new RasterFormatException("MultiPixelPackedSampleModel pixel" + + " size not 2^n bits"); + } + numElems = elemBits / numberOfBits; + + // Compute scan line large enough for w pixels. + if (scanlineStride == 0) + scanlineStride = ((dataBitOffset + w * numberOfBits) / elemBits); + this.scanlineStride = scanlineStride; + + + sampleSize = new int[1]; + sampleSize[0] = numberOfBits; + + bitMasks = new int[numElems]; + bitOffsets = new int[numElems]; + for (int i=0; i < numElems; i++) + { + bitOffsets[numElems - i- 1] = numberOfBits * i; + bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) << + bitOffsets[numElems - i - 1]; + } + } + + public SampleModel createCompatibleSampleModel(int w, int h) + { + /* FIXME: We can avoid recalculation of bit offsets and sample + sizes here by passing these from the current instance to a + special private constructor. */ + return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits); + } + + + /** + * Creates a DataBuffer for holding pixel data in the format and + * layout described by this SampleModel. The returned buffer will + * consist of one single bank. + */ + public DataBuffer createDataBuffer() + { + int size; + + // FIXME: The comment refers to SinglePixelPackedSampleModel. See if the + // same can be done for MultiPixelPackedSampleModel. + // We can save (scanlineStride - width) pixels at the very end of + // the buffer. The Sun reference implementation (J2SE 1.3.1 and + // 1.4.1_01) seems to do this; tested with Mauve test code. + size = scanlineStride * height; + + return Buffers.createBuffer(getDataType(), size); + } + + + public int getNumDataElements() + { + return 1; + } + + public int[] getSampleSize() + { + return sampleSize; + } + + public int getSampleSize(int band) + { + return sampleSize[0]; + } + + public int getOffset(int x, int y) + { + return scanlineStride * y + ((dataBitOffset + x*numberOfBits) / elemBits); + } + + public int getBitOffset(int x) + { + return (dataBitOffset + x*numberOfBits) % elemBits; + } + + public int getDataBitOffset() + { + return dataBitOffset; + } + + public int getScanlineStride() + { + return scanlineStride; + } + + public int getPixelBitStride() + { + return numberOfBits; + } + + + public SampleModel createSubsetSampleModel(int[] bands) + { + int numBands = bands.length; + if (numBands != 1) + throw new RasterFormatException("MultiPixelPackedSampleModel only" + + " supports one band"); + + return new MultiPixelPackedSampleModel(dataType, width, height, + numberOfBits, scanlineStride, + dataBitOffset); + } + + /** + * Extract one pixel and return in an array of transfer type. + * + * Extracts the pixel at x, y from data and stores into the 0th index of the + * array obj, since there is only one band. If obj is null, a new array of + * getTransferType() is created. + * + * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param obj The primitive array to store the pixels into or null to force creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + * @see java.awt.image.SampleModel#getDataElements(int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, + DataBuffer data) + { + int pixel = getSample(x, y, 0, data); + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + if (obj == null) obj = new byte[1]; + ((byte[])obj)[0] = (byte)pixel; + return obj; + case DataBuffer.TYPE_USHORT: + if (obj == null) obj = new short[1]; + ((short[])obj)[0] = (short)pixel; + return obj; + case DataBuffer.TYPE_INT: + if (obj == null) obj = new int[1]; + ((int[])obj)[0] = pixel; + return obj; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) iArray = new int[1]; + iArray[0] = getSample(x, y, 0, data); + + return iArray; + } + + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int offset = getOffset(x, y); + if (iArray == null) iArray = new int[w*h]; + int outOffset = 0; + for (y=0; y<h; y++) + { + int lineOffset = offset; + for (x=0; x<w;) + { + int samples = data.getElem(lineOffset++); + for (int b=0; b<numElems && x<w; b++) + { + iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; + x++; + } + } + offset += scanlineStride; + } + return iArray; + } + + public int getSample(int x, int y, int b, DataBuffer data) + { + int pos = + ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; + int offset = getOffset(x, y); + int samples = data.getElem(offset); + return (samples & bitMasks[pos]) >>> bitOffsets[pos]; + } + + /** + * Set the pixel at x, y to the value in the first element of the primitive + * array obj. + * + * @param x The x-coordinate of the data elements in <code>obj</code>. + * @param y The y-coordinate of the data elements in <code>obj</code>. + * @param obj The primitive array containing the data elements to set. + * @param data The DataBuffer to store the data elements into. + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int transferType = getTransferType(); + if (getTransferType() != data.getDataType()) + { + throw new IllegalArgumentException("transfer type ("+ + getTransferType()+"), "+ + "does not match data "+ + "buffer type (" + + data.getDataType() + + ")."); + } + + int offset = getOffset(x, y); + + try + { + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] in = (byte[]) obj; + out.getData()[offset] = in[0]; + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferUShort out = (DataBufferUShort) data; + short[] in = (short[]) obj; + out.getData()[offset] = in[0]; + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] in = (int[]) obj; + out.getData()[offset] = in[0]; + return; + } + default: + throw new ClassCastException("Unsupported data type"); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While writing data elements" + + ", x="+x+", y="+y+ + ", width="+width+", height="+height+ + ", scanlineStride="+scanlineStride+ + ", offset="+offset+ + ", data.getSize()="+data.getSize()+ + ", data.getOffset()="+data.getOffset()+ + ": " + + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + setSample(x, y, 0, iArray[0], data); + } + + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + int bitpos = + ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; + int offset = getOffset(x, y); + + s = s << bitOffsets[bitpos]; + s = s & bitMasks[bitpos]; + + int sample = data.getElem(offset); + sample |= s; + data.setElem(offset, sample); + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i=0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/PackedColorModel.java b/libjava/classpath/java/awt/image/PackedColorModel.java new file mode 100644 index 00000000000..894e6e66fda --- /dev/null +++ b/libjava/classpath/java/awt/image/PackedColorModel.java @@ -0,0 +1,192 @@ +/* Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.BitMaskExtent; + +import java.awt.Point; +import java.awt.color.ColorSpace; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class PackedColorModel extends ColorModel +{ + private int masks[]; + + /* Package accessibility, the DirectColorModel needs this array */ + int shifts[]; + + public PackedColorModel(ColorSpace cspace, int pixelBits, + int[] colorMaskArray, int alphaMask, + boolean isAlphaPremultiplied, + int transparency, + int transferType) + { + super(pixelBits, calcBitsPerComponent(colorMaskArray, alphaMask), + cspace, (alphaMask != 0), isAlphaPremultiplied, transparency, + transferType); + initMasks(colorMaskArray, alphaMask); + if ((pixelBits<1) || (pixelBits>32)) { + throw new IllegalArgumentException("pixels per bits must be " + + "in the range [1, 32]"); + } + } + + private static int[] calcBitsPerComponent(int[] colorMaskArray, + int alphaMask) + { + int numComponents = colorMaskArray.length; + if (alphaMask != 0) numComponents++; + + int[] bitsPerComponent = new int[numComponents]; + + BitMaskExtent extent = new BitMaskExtent(); + for (int b=0; b<colorMaskArray.length; b++) + { + extent.setMask(colorMaskArray[b]); + bitsPerComponent[b] = extent.bitWidth; + } + if (alphaMask != 0) + { + extent.setMask(alphaMask); + bitsPerComponent[numComponents-1] = extent.bitWidth; + } + return bitsPerComponent; + } + + /** Initializes the masks. + * + * @return an array containing the number of bits per color + * component. + */ + private void initMasks(int[] colorMaskArray, int alphaMask) + { + int numComponents = colorMaskArray.length; + if (alphaMask == 0) + { + masks = colorMaskArray; + } + else + { + masks = new int[numComponents+1]; + System.arraycopy(colorMaskArray, 0, + masks, 0, + numComponents); + masks[numComponents++] = alphaMask; + } + + shifts = new int[numComponents]; + + // Bit field handling have been moved to a utility class + BitMaskExtent extent = new BitMaskExtent(); + for (int b=0; b<numComponents; b++) + { + extent.setMask(masks[b]); + shifts[b] = extent.leastSignificantBit; + } + } + + public PackedColorModel(ColorSpace cspace, int pixelBits, + int rmask, int gmask, int bmask, + int amask, boolean isAlphaPremultiplied, + int transparency, + int transferType) + { + this(cspace, pixelBits, makeColorMaskArray(rmask, gmask, bmask), + amask, isAlphaPremultiplied, transparency, transferType); + } + + /* TODO: If there is a alpha mask, it is inefficient to create a + color mask array that will be discarded when the alpha mask is + appended. We should probably create a private constructor that + takes a complete array of masks (color+alpha) as an + argument. */ + + private static int[] makeColorMaskArray(int rmask, int gmask, int bmask) + { + int[] colorMaskArray = { rmask, gmask, bmask }; + return colorMaskArray; + } + + public final int getMask(int index) + { + return masks[index]; + } + + public final int[] getMasks() + { + return masks; + } + + public SampleModel createCompatibleSampleModel(int w, int h) + { + return new SinglePixelPackedSampleModel(transferType, w, h, masks); + } + + public boolean isCompatibleSampleModel(SampleModel sm) + { + if (!super.isCompatibleSampleModel(sm)) return false; + if (!(sm instanceof SinglePixelPackedSampleModel)) return false; + + SinglePixelPackedSampleModel sppsm = + (SinglePixelPackedSampleModel) sm; + return java.util.Arrays.equals(sppsm.getBitMasks(), masks); + } + + public WritableRaster getAlphaRaster(WritableRaster raster) { + if (!hasAlpha()) return null; + + SampleModel sm = raster.getSampleModel(); + int[] alphaBand = { sm.getNumBands() - 1 }; + SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); + DataBuffer buffer = raster.getDataBuffer(); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(alphaModel, buffer, origin); + } + + public boolean equals(Object obj) + { + if (!super.equals(obj)) return false; + if (!(obj instanceof PackedColorModel)) return false; + + PackedColorModel other = (PackedColorModel) obj; + + return java.util.Arrays.equals(masks, other.masks); + } +} diff --git a/libjava/classpath/java/awt/image/PixelGrabber.java b/libjava/classpath/java/awt/image/PixelGrabber.java new file mode 100644 index 00000000000..b576dbfad98 --- /dev/null +++ b/libjava/classpath/java/awt/image/PixelGrabber.java @@ -0,0 +1,618 @@ +/* PixelGrabber.java -- retrieve a subset of an image's data + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Image; +import java.util.Hashtable; + +/** + * PixelGrabber is an ImageConsumer that extracts a rectangular region + * of pixels from an Image. + */ +public class PixelGrabber implements ImageConsumer +{ + int x, y, offset; + int width = -1; + int height = -1; + int scansize = -1; + boolean forceRGB = true; + + ColorModel model = ColorModel.getRGBdefault(); + int hints; + Hashtable props; + + int int_pixel_buffer[]; + boolean ints_delivered = false; + byte byte_pixel_buffer[]; + boolean bytes_delivered = false; + + ImageProducer ip; + int observerStatus; + int consumerStatus; + + private Thread grabberThread; + boolean grabbing = false; + + /** + * Construct a PixelGrabber that will retrieve RGB data from a given + * Image. + * + * The RGB data will be retrieved from a rectangular region + * <code>(x, y, w, h)</code> within the image. The data will be + * stored in the provided <code>pix</code> array, which must have + * been initialized to a size of at least <code>w * h</code>. The + * data for a pixel (m, n) in the grab rectangle will be stored at + * <code>pix[(n - y) * scansize + (m - x) + off]</code>. + * + * @param img the Image from which to grab pixels + * @param x the x coordinate, relative to <code>img</code>'s + * top-left corner, of the grab rectangle's top-left pixel + * @param y the y coordinate, relative to <code>img</code>'s + * top-left corner, of the grab rectangle's top-left pixel + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param pix the array in which to store grabbed RGB pixel data + * @param off the offset into the <code>pix</code> array at which to + * start storing RGB data + * @param scansize a set of <code>scansize</code> consecutive + * elements in the <code>pix</code> array represents one row of + * pixels in the grab rectangle + */ + public PixelGrabber(Image img, int x, int y, int w, int h, + int pix[], int off, int scansize) + { + this (img.getSource(), x, y, w, h, pix, off, scansize); + } + + /** + * Construct a PixelGrabber that will retrieve RGB data from a given + * ImageProducer. + * + * The RGB data will be retrieved from a rectangular region + * <code>(x, y, w, h)</code> within the image produced by + * <code>ip</code>. The data will be stored in the provided + * <code>pix</code> array, which must have been initialized to a + * size of at least <code>w * h</code>. The data for a pixel (m, n) + * in the grab rectangle will be stored at + * <code>pix[(n - y) * scansize + (m - x) + off]</code>. + * + * @param ip the ImageProducer from which to grab pixels + * @param x the x coordinate of the grab rectangle's top-left pixel, + * specified relative to the top-left corner of the image produced + * by <code>ip</code> + * @param y the y coordinate of the grab rectangle's top-left pixel, + * specified relative to the top-left corner of the image produced + * by <code>ip</code> + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param pix the array in which to store grabbed RGB pixel data + * @param off the offset into the <code>pix</code> array at which to + * start storing RGB data + * @param scansize a set of <code>scansize</code> consecutive + * elements in the <code>pix</code> array represents one row of + * pixels in the grab rectangle + */ + public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, + int pix[], int off, int scansize) + { + this.ip = ip; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.offset = off; + this.scansize = scansize; + + int_pixel_buffer = pix; + // Initialize the byte array in case ip sends us byte-formatted + // pixel data. + byte_pixel_buffer = new byte[pix.length * 4]; + } + + /** + * Construct a PixelGrabber that will retrieve data from a given + * Image. + * + * The RGB data will be retrieved from a rectangular region + * <code>(x, y, w, h)</code> within the image. The data will be + * stored in an internal array which can be accessed by calling + * <code>getPixels</code>. The data for a pixel (m, n) in the grab + * rectangle will be stored in the returned array at index + * <code>(n - y) * scansize + (m - x) + off</code>. + * If forceRGB is false, then the returned data will be not be + * converted to RGB from its format in <code>img</code>. + * + * If <code>w</code> is negative, the width of the grab region will + * be from x to the right edge of the image. Likewise, if + * <code>h</code> is negative, the height of the grab region will be + * from y to the bottom edge of the image. + * + * @param img the Image from which to grab pixels + * @param x the x coordinate, relative to <code>img</code>'s + * top-left corner, of the grab rectangle's top-left pixel + * @param y the y coordinate, relative to <code>img</code>'s + * top-left corner, of the grab rectangle's top-left pixel + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param forceRGB true to force conversion of the rectangular + * region's pixel data to RGB + */ + public PixelGrabber(Image img, + int x, int y, + int w, int h, + boolean forceRGB) + { + this.ip = img.getSource(); + this.x = x; + this.y = y; + width = w; + height = h; + // If width or height is negative, postpone pixel buffer + // initialization until setDimensions is called back by ip. + if (width >= 0 && height >= 0) + { + int_pixel_buffer = new int[width * height]; + byte_pixel_buffer = new byte[width * height]; + } + this.forceRGB = forceRGB; + } + + /** + * Start grabbing pixels. + * + * Spawns an image production thread that calls back to this + * PixelGrabber's ImageConsumer methods. + */ + public synchronized void startGrabbing() + { + // Make sure we're not already grabbing. + if (grabbing == false) + { + grabbing = true; + grabberThread = new Thread () + { + public void run () + { + ip.startProduction (PixelGrabber.this); + } + }; + grabberThread.start (); + } + } + + /** + * Abort pixel grabbing. + */ + public synchronized void abortGrabbing() + { + if (grabbing) + { + // Interrupt the grabbing thread. + Thread moribund = grabberThread; + grabberThread = null; + moribund.interrupt(); + + imageComplete (ImageConsumer.IMAGEABORTED); + } + } + + /** + * Have our Image or ImageProducer start sending us pixels via our + * ImageConsumer methods and wait for all pixels in the grab + * rectangle to be delivered. + * + * @return true if successful, false on abort or error + * + * @throws InterruptedException if interrupted by another thread. + */ + public synchronized boolean grabPixels() throws InterruptedException + { + return grabPixels(0); + } + + /** + * grabPixels's behavior depends on the value of <code>ms</code>. + * + * If ms < 0, return true if all pixels from the source image have + * been delivered, false otherwise. Do not wait. + * + * If ms >= 0 then we request that our Image or ImageProducer start + * delivering pixels to us via our ImageConsumer methods. + * + * If ms > 0, wait at most <code>ms</code> milliseconds for + * delivery of all pixels within the grab rectangle. + * + * If ms == 0, wait until all pixels have been delivered. + * + * @return true if all pixels from the source image have been + * delivered, false otherwise + * + * @throws InterruptedException if this thread is interrupted while + * we are waiting for pixels to be delivered + */ + public synchronized boolean grabPixels(long ms) throws InterruptedException + { + if (ms < 0) + return ((observerStatus & (ImageObserver.FRAMEBITS + | ImageObserver.ALLBITS)) != 0); + + // Spawn a new ImageProducer thread to send us the image data via + // our ImageConsumer methods. + startGrabbing(); + + if (ms > 0) + { + long stop_time = System.currentTimeMillis() + ms; + long time_remaining; + while (grabbing) + { + time_remaining = stop_time - System.currentTimeMillis(); + if (time_remaining <= 0) + break; + wait (time_remaining); + } + abortGrabbing (); + } + else + wait (); + + // If consumerStatus is non-zero then the image is done loading or + // an error has occurred. + if (consumerStatus != 0) + return setObserverStatus (); + + return ((observerStatus & (ImageObserver.FRAMEBITS + | ImageObserver.ALLBITS)) != 0); + } + + // Set observer status flags based on the current consumer status + // flags. Return true if the consumer flags indicate that the + // image was loaded successfully, or false otherwise. + private synchronized boolean setObserverStatus () + { + boolean retval = false; + + if ((consumerStatus & IMAGEERROR) != 0) + observerStatus |= ImageObserver.ERROR; + + if ((consumerStatus & IMAGEABORTED) != 0) + observerStatus |= ImageObserver.ABORT; + + if ((consumerStatus & STATICIMAGEDONE) != 0) + { + observerStatus |= ImageObserver.ALLBITS; + retval = true; + } + + if ((consumerStatus & SINGLEFRAMEDONE) != 0) + { + observerStatus |= ImageObserver.FRAMEBITS; + retval = true; + } + + return retval; + } + + /** + * @return the status of the pixel grabbing thread, represented by a + * bitwise OR of ImageObserver flags + */ + public synchronized int getStatus() + { + return observerStatus; + } + + /** + * @return the width of the grab rectangle in pixels, or a negative + * number if the ImageProducer has not yet called our setDimensions + * method + */ + public synchronized int getWidth() + { + return width; + } + + /** + * @return the height of the grab rectangle in pixels, or a negative + * number if the ImageProducer has not yet called our setDimensions + * method + */ + public synchronized int getHeight() + { + return height; + } + + /** + * @return a byte array of pixel data if ImageProducer delivered + * pixel data using the byte[] variant of setPixels, or an int array + * otherwise + */ + public synchronized Object getPixels() + { + if (ints_delivered) + return int_pixel_buffer; + else if (bytes_delivered) + return byte_pixel_buffer; + else + return null; + } + + /** + * @return the ColorModel currently being used for the majority of + * pixel data conversions + */ + public synchronized ColorModel getColorModel() + { + return model; + } + + /** + * Our <code>ImageProducer</code> calls this method to indicate the + * size of the image being produced. + * + * setDimensions is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param width the width of the image + * @param height the height of the image + */ + public synchronized void setDimensions(int width, int height) + { + // Our width wasn't set when we were constructed. Set our width + // so that the grab region includes all pixels from x to the right + // edge of the source image. + if (this.width < 0) + this.width = width - x; + + // Our height wasn't set when we were constructed. Set our height + // so that the grab region includes all pixels from y to the + // bottom edge of the source image. + if (this.height < 0) + this.height = height - y; + + if (scansize < 0) + scansize = this.width; + + if (int_pixel_buffer == null) + int_pixel_buffer = new int[this.width * this.height]; + + if (byte_pixel_buffer == null) + byte_pixel_buffer = new byte[this.width * this.height]; + } + + /** + * Our <code>ImageProducer</code> may call this method to send us a + * list of its image's properties. + * + * setProperties is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param props a list of properties associated with the image being + * produced + */ + public synchronized void setProperties(Hashtable props) + { + this.props = props; + } + + /** + * Our ImageProducer will call <code>setColorModel</code> to + * indicate the model used by the majority of calls to + * <code>setPixels</code>. Each call to <code>setPixels</code> + * could however indicate a different <code>ColorModel</code>. + * + * setColorModel is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param model the color model to be used most often by setPixels + * + * @see ColorModel + */ + public synchronized void setColorModel(ColorModel model) + { + this.model = model; + } + + /** + * Our <code>ImageProducer</code> may call this method with a + * bit mask of hints from any of <code>RANDOMPIXELORDER</code>, + * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>, + * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>. + * + * setHints is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param flags a bit mask of hints + */ + public synchronized void setHints(int flags) + { + hints = flags; + } + + /** + * Our ImageProducer calls setPixels to deliver a subset of its + * pixels. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the rectangular + * region of pixels being delivered to this ImageConsumer, + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel region's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + public synchronized void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, + int offset, int scansize) + { + ColorModel currentModel; + if (model != null) + currentModel = model; + else + currentModel = this.model; + + for(int yp = y; yp < (y + h); yp++) + { + for(int xp = x; xp < (x + w); xp++) + { + // Check if the coordinates (xp, yp) are within the + // pixel block that we are grabbing. + if(xp >= this.x + && yp >= this.y + && xp < (this.x + this.width) + && yp < (this.y + this.height)) + { + int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; + int p = (yp - y) * scansize + (xp - x) + offset; + if (forceRGB) + { + ints_delivered = true; + + int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF); + } + else + { + bytes_delivered = true; + + byte_pixel_buffer[i] = pixels[p]; + } + } + } + } + } + + /** + * Our ImageProducer calls setPixels to deliver a subset of its + * pixels. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the rectangular + * region of pixels being delivered to this ImageConsumer, + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel region's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + public synchronized void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, + int offset, int scansize) + { + ColorModel currentModel; + if (model != null) + currentModel = model; + else + currentModel = this.model; + + ints_delivered = true; + + for(int yp = y; yp < (y + h); yp++) + { + for(int xp = x; xp < (x + w); xp++) + { + // Check if the coordinates (xp, yp) are within the + // pixel block that we are grabbing. + if(xp >= this.x + && yp >= this.y + && xp < (this.x + this.width) + && yp < (this.y + this.height)) + { + int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; + int p = (yp - y) * scansize + (xp - x) + offset; + if (forceRGB) + int_pixel_buffer[i] = currentModel.getRGB (pixels[p]); + else + int_pixel_buffer[i] = pixels[p]; + } + } + } + } + + /** + * Our <code>ImageProducer</code> calls this method to inform us + * that a single frame or the entire image is complete. The method + * is also used to inform us of an error in loading or producing the + * image. + * + * @param status the status of image production, represented by a + * bitwise OR of ImageConsumer flags + */ + public synchronized void imageComplete(int status) + { + consumerStatus = status; + setObserverStatus (); + grabbing = false; + ip.removeConsumer (this); + + notifyAll (); + } + + /** + * @return the return value of getStatus + * + * @specnote The newer getStatus should be used in place of status. + */ + public synchronized int status() + { + return getStatus(); + } +} diff --git a/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java b/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java new file mode 100644 index 00000000000..4c5c436edfc --- /dev/null +++ b/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java @@ -0,0 +1,98 @@ +/* PixelInterleavedSampleModel.java + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + + +/** + * A <code>SampleModel</code> that uses exactly one element of the + * raster’s {@link DataBuffer} per pixel, holds all bands in a + * single bank, and stores band data in pixel-interleaved manner. + * + * @since 1.2 + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class PixelInterleavedSampleModel + extends ComponentSampleModel +{ + public PixelInterleavedSampleModel(int dataType, int width, int height, + int pixelStride, int scanlineStride, + int[] bandOffsets) + { + super(dataType, width, height, pixelStride, scanlineStride, + bandOffsets); + } + + + /** + * Creates a new <code>SampleModel</code> that is like this one, but + * uses the specified width and height. + * + * @param width the number of pixels in the horizontal direction. + * + * @param height the number of pixels in the vertical direction. + */ + public SampleModel createCompatibleSampleModel(int width, int height) + { + return new PixelInterleavedSampleModel(dataType, width, height, + pixelStride, scanlineStride, + bandOffsets); + } + + + /** + * Creates a new <code>SampleModel</code> that is like this one, but + * uses only a subset of its bands. + * + * @param bands an array whose elements indicate which bands shall + * be part of the subset. For example, <code>[0, 2, 3]</code> would + * create a SampleModel containing bands #0, #2 and #3. + */ + public SampleModel createSubsetSampleModel(int[] bands) + { + int[] subOffsets; + + subOffsets = new int[bands.length]; + for (int i = 0; i < bands.length; i++) + subOffsets[i] = bandOffsets[bands[i]]; + + return new PixelInterleavedSampleModel(dataType, width, height, + pixelStride, scanlineStride, + subOffsets); + } +} diff --git a/libjava/classpath/java/awt/image/RGBImageFilter.java b/libjava/classpath/java/awt/image/RGBImageFilter.java new file mode 100644 index 00000000000..f7b39b97b28 --- /dev/null +++ b/libjava/classpath/java/awt/image/RGBImageFilter.java @@ -0,0 +1,267 @@ +/* RGBImageFilter.java -- Java class for filtering Pixels by RGB values + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * A filter designed to filter images in the default RGBColorModel regardless of + * the ImageProducer's ColorModel. + * + * @author Mark Benvenuto (mcb54@columbia.edu) + */ +public abstract class RGBImageFilter extends ImageFilter +{ + protected ColorModel origmodel; + + protected ColorModel newmodel; + + /** + Specifies whether to apply the filter to the index entries of the + IndexColorModel. Subclasses should set this to true if the filter + does not depend on the pixel's coordinate. + */ + protected boolean canFilterIndexColorModel = false; + + /** + Construct new RGBImageFilter. + */ + public RGBImageFilter() + { + } + + /** + * Sets the ColorModel used to filter with. If the specified ColorModel is IndexColorModel + * and canFilterIndexColorModel is true, we subsitute the ColorModel for a filtered one + * here and in setPixels whenever the original one appears. Otherwise overrides the default + * ColorModel of ImageProducer and specifies the default RGBColorModel + * + * @param model the color model to be used most often by setPixels + * @see ColorModel */ + public void setColorModel(ColorModel model) + { + origmodel = model; + newmodel = model; + + if( ( model instanceof IndexColorModel) && canFilterIndexColorModel ) { + newmodel = filterIndexColorModel( (IndexColorModel) model ); + consumer.setColorModel(newmodel); + } + else { + consumer.setColorModel(ColorModel.getRGBdefault()); + } + } + + /** + Registers a new ColorModel to subsitute for the old ColorModel when + setPixels encounters the a pixel with the old ColorModel. The pixel + remains unchanged except for a new ColorModel. + + @param oldcm the old ColorModel + @param newcm the new ColorModel + */ + public void substituteColorModel(ColorModel oldcm, + ColorModel newcm) + { + origmodel = oldcm; + newmodel = newcm; + } + + /** + Filters an IndexColorModel through the filterRGB function. Uses + coordinates of -1 to indicate its filtering an index and not a pixel. + + @param icm an IndexColorModel to filter + */ + public IndexColorModel filterIndexColorModel(IndexColorModel icm) + { + int len = icm.getMapSize(), rgb; + byte reds[] = new byte[len], greens[] = new byte[len], blues[] = new byte[len], alphas[] = new byte[len]; + + icm.getAlphas( alphas ); + icm.getReds( reds ); + icm.getGreens( greens ); + icm.getBlues( blues ); + + for( int i = 0; i < len; i++ ) + { + rgb = filterRGB( -1, -1, makeColor ( alphas[i], reds[i], greens[i], blues[i] ) ); + alphas[i] = (byte)(( 0xff000000 & rgb ) >> 24); + reds[i] = (byte)(( 0xff0000 & rgb ) >> 16); + greens[i] = (byte)(( 0xff00 & rgb ) >> 8); + blues[i] = (byte)(0xff & rgb); + } + return new IndexColorModel( icm.getPixelSize(), len, reds, greens, blues, alphas ); + } + + private int makeColor( byte a, byte r, byte g, byte b ) + { + return ( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b ); + } + + /** + This functions filters a set of RGB pixels through filterRGB. + + @param x the x coordinate of the rectangle + @param y the y coordinate of the rectangle + @param w the width of the rectangle + @param h the height of the rectangle + @param pixels the array of pixel values + @param offset the index of the first pixels in the <code>pixels</code> array + @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void filterRGBPixels(int x, int y, int w, int h, int[] pixels, + int offset, int scansize) + { + for (int yp = 0; yp < h; yp++) + { + for (int xp = 0; xp < w; xp++) + { + pixels[offset + xp] = filterRGB(xp + x, yp + y, pixels[offset + xp]); + } + offset += scansize; + } + } + + + /** + * If the ColorModel is the same ColorModel which as already converted + * then it converts it the converted ColorModel. Otherwise it passes the + * array of pixels through filterRGBpixels. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, + int offset, int scansize) + { + if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel) + { + consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize); + } + else + { + int intPixels[] = + convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize ); + filterRGBPixels( x, y, w, h, intPixels, offset, scansize ); + consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), intPixels, offset, scansize); + } + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an <code>int</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, + int offset, int scansize) + { + if(model == origmodel && (model instanceof IndexColorModel) && canFilterIndexColorModel) + { + consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize); + } + else + { + //FIXME: Store the filtered pixels in a separate temporary buffer? + convertColorModelToDefault( x, y, w, h, model, pixels, offset, scansize ); + filterRGBPixels( x, y, w, h, pixels, offset, scansize ); + consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset, scansize); + } + } + + private int[] convertColorModelToDefault(int x, int y, int w, int h, + ColorModel model, byte pixels[], + int offset, int scansize) + { + int intPixels[] = new int[pixels.length]; + for (int i = 0; i < pixels.length; i++) + intPixels[i] = makeColorbyDefaultCM(model, pixels[i]); + return intPixels; + } + + private void convertColorModelToDefault(int x, int y, int w, int h, + ColorModel model, int pixels[], + int offset, int scansize) + { + for (int i = 0; i < pixels.length; i++) + pixels[i] = makeColorbyDefaultCM(model, pixels[i]); + } + + private int makeColorbyDefaultCM(ColorModel model, byte rgb) + { + return makeColor( model.getAlpha( rgb ) * 4, model.getRed( rgb ) * 4, model.getGreen( rgb ) * 4, model.getBlue( rgb ) * 4 ); + } + + private int makeColorbyDefaultCM(ColorModel model, int rgb) + { + return makeColor( model.getAlpha( rgb ), model.getRed( rgb ), model.getGreen( rgb ), model.getBlue( rgb ) ); + } + + private int makeColor( int a, int r, int g, int b ) + { + return (int)( 0xff000000 & (a << 24) | 0xff0000 & (r << 16) | 0xff00 & (g << 8) | 0xff & b ); + } + + + /** + Filters a single pixel from the default ColorModel. + + @param x x-coordinate + @param y y-coordinate + @param rgb color + */ + public abstract int filterRGB(int x, + int y, + int rgb); +} diff --git a/libjava/classpath/java/awt/image/Raster.java b/libjava/classpath/java/awt/image/Raster.java new file mode 100644 index 00000000000..4af958a17c7 --- /dev/null +++ b/libjava/classpath/java/awt/image/Raster.java @@ -0,0 +1,546 @@ +/* Copyright (C) 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Point; +import java.awt.Rectangle; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class Raster +{ + protected SampleModel sampleModel; + protected DataBuffer dataBuffer; + protected int minX; + protected int minY; + protected int width; + protected int height; + protected int sampleModelTranslateX; + protected int sampleModelTranslateY; + protected int numBands; + protected int numDataElements; + protected Raster parent; + + protected Raster(SampleModel sampleModel, Point origin) + { + this(sampleModel, sampleModel.createDataBuffer(), origin); + } + + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, + Point origin) + { + this(sampleModel, dataBuffer, + new Rectangle(origin.x, origin.y, + sampleModel.getWidth(), sampleModel.getHeight()), + origin, null); + } + + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, Raster parent) + { + this.sampleModel = sampleModel; + this.dataBuffer = dataBuffer; + this.minX = aRegion.x; + this.minY = aRegion.y; + this.width = aRegion.width; + this.height = aRegion.height; + + // If sampleModelTranslate is null, use (0,0). Methods such as + // Raster.createRaster are specified to allow for a null argument. + if (sampleModelTranslate != null) + { + this.sampleModelTranslateX = sampleModelTranslate.x; + this.sampleModelTranslateY = sampleModelTranslate.y; + } + + this.numBands = sampleModel.getNumBands(); + this.numDataElements = sampleModel.getNumDataElements(); + this.parent = parent; + } + + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, + int bands, + Point location) + { + int[] bandOffsets = new int[bands]; + // TODO: Maybe not generate this every time. + for (int b=0; b<bands; b++) bandOffsets[b] = b; + + int scanlineStride = bands*w; + return createInterleavedRaster(dataType, w, h, scanlineStride, bands, + bandOffsets, location); + } + + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, + int scanlineStride, + int pixelStride, + int[] bandOffsets, + Point location) + { + SampleModel sm = new ComponentSampleModel(dataType, + w, h, + pixelStride, + scanlineStride, + bandOffsets); + return createWritableRaster(sm, location); + } + + public static WritableRaster createBandedRaster(int dataType, + int w, int h, int bands, + Point location) + { + SampleModel sm = new BandedSampleModel(dataType, w, h, bands); + return createWritableRaster(sm, location); + } + + public static WritableRaster createBandedRaster(int dataType, + int w, int h, + int scanlineStride, + int[] bankIndices, + int[] bandOffsets, + Point location) + { + SampleModel sm = new BandedSampleModel(dataType, w, h, scanlineStride, + bankIndices, bandOffsets); + return createWritableRaster(sm, location); + } + + public static WritableRaster createPackedRaster(int dataType, + int w, int h, + int[] bandMasks, + Point location) + { + SampleModel sm = new SinglePixelPackedSampleModel(dataType, + w, h, + bandMasks); + return createWritableRaster(sm, location); + } + + public static WritableRaster createPackedRaster(int dataType, + int w, int h, + int bands, int bitsPerBand, + Point location) + { + if (bands <= 0 || (bands * bitsPerBand > getTypeBits(dataType))) + throw new IllegalArgumentException(); + + SampleModel sm; + + if (bands == 1) + sm = new MultiPixelPackedSampleModel(dataType, w, h, bitsPerBand); + else + { + int[] bandMasks = new int[bands]; + int mask = 0x1; + for (int bits = bitsPerBand; --bits != 0;) + mask = (mask << 1) | 0x1; + for (int i = 0; i < bands; i++) + { + bandMasks[i] = mask; + mask <<= bitsPerBand; + } + + sm = new SinglePixelPackedSampleModel(dataType, w, h, bandMasks); + } + return createWritableRaster(sm, location); + } + + public static WritableRaster + createInterleavedRaster(DataBuffer dataBuffer, int w, int h, + int scanlineStride, int pixelStride, + int[] bandOffsets, Point location) + { + SampleModel sm = new ComponentSampleModel(dataBuffer.getDataType(), + w, h, + scanlineStride, + pixelStride, + bandOffsets); + return createWritableRaster(sm, dataBuffer, location); + } + + public static + WritableRaster createBandedRaster(DataBuffer dataBuffer, + int w, int h, + int scanlineStride, + int[] bankIndices, + int[] bandOffsets, + Point location) + { + SampleModel sm = new BandedSampleModel(dataBuffer.getDataType(), + w, h, scanlineStride, + bankIndices, bandOffsets); + return createWritableRaster(sm, dataBuffer, location); + } + + public static WritableRaster + createPackedRaster(DataBuffer dataBuffer, + int w, int h, + int scanlineStride, + int[] bandMasks, + Point location) + { + SampleModel sm = + new SinglePixelPackedSampleModel(dataBuffer.getDataType(), + w, h, + scanlineStride, + bandMasks); + return createWritableRaster(sm, dataBuffer, location); + } + + public static WritableRaster + createPackedRaster(DataBuffer dataBuffer, + int w, int h, + int bitsPerPixel, + Point location) + { + SampleModel sm = + new MultiPixelPackedSampleModel(dataBuffer.getDataType(), + w, h, + bitsPerPixel); + return createWritableRaster(sm, dataBuffer, location); + } + + public static Raster createRaster(SampleModel sm, DataBuffer db, + Point location) + { + return new Raster(sm, db, location); + } + + public static WritableRaster createWritableRaster(SampleModel sm, + Point location) + { + return new WritableRaster(sm, location); + } + + public static WritableRaster createWritableRaster(SampleModel sm, + DataBuffer db, + Point location) + { + return new WritableRaster(sm, db, location); + } + + public Raster getParent() + { + return parent; + } + + public final int getSampleModelTranslateX() + { + return sampleModelTranslateX; + } + + public final int getSampleModelTranslateY() + { + return sampleModelTranslateY; + } + + public WritableRaster createCompatibleWritableRaster() + { + return new WritableRaster(getSampleModel(), new Point(minX, minY)); + } + + public WritableRaster createCompatibleWritableRaster(int w, int h) + { + return createCompatibleWritableRaster(minX, minY, w, h); + } + + public WritableRaster createCompatibleWritableRaster(Rectangle rect) + { + return createCompatibleWritableRaster(rect.x, rect.y, + rect.width, rect.height); + } + + public WritableRaster createCompatibleWritableRaster(int x, int y, + int w, int h) + { + SampleModel sm = getSampleModel().createCompatibleSampleModel(w, h); + return new WritableRaster(sm, sm.createDataBuffer(), + new Point(x, y)); + } + + public Raster createTranslatedChild(int childMinX, int childMinY) { + int tcx = sampleModelTranslateX - minX + childMinX; + int tcy = sampleModelTranslateY - minY + childMinY; + + return new Raster(sampleModel, dataBuffer, + new Rectangle(childMinX, childMinY, + width, height), + new Point(tcx, tcy), + this); + } + + public Raster createChild(int parentX, int parentY, int width, + int height, int childMinX, int childMinY, + int[] bandList) + { + /* FIXME: Throw RasterFormatException if child bounds extends + beyond the bounds of this raster. */ + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + /* + data origin + / + +------------------------- + |\. __ parent trans + | \`. + | \ `. parent origin + | \ `. / + | /\ +-------- - - + |trans\ /<\-- deltaTrans + |child +-+-\---- - - + | /|`| \__ parent [x, y] + |child | |`. \ + |origin| : `.\ + | | / `\ + | : / + + | child [x, y] + + parent_xy - parent_trans = child_xy - child_trans + + child_trans = parent_trans + child_xy - parent_xy + */ + + return new Raster(sm, dataBuffer, + new Rectangle(childMinX, childMinY, + width, height), + new Point(sampleModelTranslateX+childMinX-parentX, + sampleModelTranslateY+childMinY-parentY), + this); + } + + public Rectangle getBounds() + { + return new Rectangle(minX, minY, width, height); + } + + public final int getMinX() + { + return minX; + } + + public final int getMinY() + { + return minY; + } + + public final int getWidth() + { + return width; + } + + public final int getHeight() + { + return height; + } + + public final int getNumBands() + { + return numBands; + } + + public final int getNumDataElements() + { + return numDataElements; + } + + public final int getTransferType() + { + return sampleModel.getTransferType(); + } + + public DataBuffer getDataBuffer() + { + return dataBuffer; + } + + public SampleModel getSampleModel() + { + return sampleModel; + } + + public Object getDataElements(int x, int y, Object outData) + { + return sampleModel.getDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + outData, dataBuffer); + } + + public Object getDataElements(int x, int y, int w, int h, + Object outData) + { + return sampleModel.getDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, outData, dataBuffer); + } + + public int[] getPixel(int x, int y, int[] iArray) + { + return sampleModel.getPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + iArray, dataBuffer); + } + + public float[] getPixel(int x, int y, float[] fArray) + { + return sampleModel.getPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + fArray, dataBuffer); + } + + public double[] getPixel(int x, int y, double[] dArray) + { + return sampleModel.getPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + dArray, dataBuffer); + } + + public int[] getPixels(int x, int y, int w, int h, int[] iArray) + { + return sampleModel.getPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, iArray, dataBuffer); + } + + public float[] getPixels(int x, int y, int w, int h, + float[] fArray) + { + return sampleModel.getPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, fArray, dataBuffer); + } + + public double[] getPixels(int x, int y, int w, int h, + double[] dArray) + { + return sampleModel.getPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, dArray, dataBuffer); + } + + public int getSample(int x, int y, int b) + { + return sampleModel.getSample(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, dataBuffer); + } + + public float getSampleFloat(int x, int y, int b) + { + return sampleModel.getSampleFloat(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, dataBuffer); + } + + public double getSampleDouble(int x, int y, int b) + { + return sampleModel.getSampleDouble(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, dataBuffer); + } + + public int[] getSamples(int x, int y, int w, int h, int b, + int[] iArray) + { + return sampleModel.getSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, iArray, dataBuffer); + } + + public float[] getSamples(int x, int y, int w, int h, int b, + float[] fArray) + { + return sampleModel.getSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, fArray, dataBuffer); + } + + public double[] getSamples(int x, int y, int w, int h, int b, + double[] dArray) + { + return sampleModel.getSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, dArray, dataBuffer); + } + + /** + * Create a String representing the stat of this Raster. + * @return A String representing the stat of this Raster. + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + + result.append(getClass().getName()); + result.append("[("); + result.append(minX).append(",").append(minY).append("), "); + result.append(width).append(" x ").append(height).append(","); + result.append(sampleModel).append(","); + result.append(dataBuffer); + result.append("]"); + + return result.toString(); + } + + // Map from datatype to bits + private static int getTypeBits(int dataType) + { + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + return 8; + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_SHORT: + return 16; + case DataBuffer.TYPE_INT: + case DataBuffer.TYPE_FLOAT: + return 32; + case DataBuffer.TYPE_DOUBLE: + return 64; + default: + return 0; + } + } +} diff --git a/libjava/classpath/java/awt/image/RasterFormatException.java b/libjava/classpath/java/awt/image/RasterFormatException.java new file mode 100644 index 00000000000..582c2ae5ab2 --- /dev/null +++ b/libjava/classpath/java/awt/image/RasterFormatException.java @@ -0,0 +1,65 @@ +/* RasterFormatException.java -- indicates invalid layout in Raster + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This exception is thrown when there is invalid layout information in + * <code>Raster</code> + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Raster + * @status updated to 1.4 + */ +public class RasterFormatException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 96598996116164315L; + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public RasterFormatException(String message) + { + super(message); + } +} // class RasterFormatException diff --git a/libjava/classpath/java/awt/image/RasterOp.java b/libjava/classpath/java/awt/image/RasterOp.java new file mode 100644 index 00000000000..e081ca3d2ad --- /dev/null +++ b/libjava/classpath/java/awt/image/RasterOp.java @@ -0,0 +1,57 @@ +/* RasterOp.java -- + Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +public interface RasterOp +{ + WritableRaster filter(Raster src, WritableRaster dest); + + Rectangle2D getBounds2D(Raster src); + + WritableRaster createCompatibleDestRaster(Raster src); + + Point2D getPoint2D(Point2D srcPoint, Point2D destPoint); + + RenderingHints getRenderingHints(); +} + diff --git a/libjava/classpath/java/awt/image/RenderedImage.java b/libjava/classpath/java/awt/image/RenderedImage.java new file mode 100644 index 00000000000..b35f8602129 --- /dev/null +++ b/libjava/classpath/java/awt/image/RenderedImage.java @@ -0,0 +1,70 @@ +/* RenderedImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Rectangle; +import java.util.Vector; + +/** + * NEEDS DOCUMENTATION + */ +public interface RenderedImage +{ + Vector getSources(); + Object getProperty(String name); + String[] getPropertyNames(); + ColorModel getColorModel(); + SampleModel getSampleModel(); + int getWidth(); + int getHeight(); + int getMinX(); + int getMinY(); + int getNumXTiles(); + int getNumYTiles(); + int getMinTileX(); + int getMinTileY(); + int getTileWidth(); + int getTileHeight(); + int getTileGridXOffset(); + int getTileGridYOffset(); + Raster getTile(int x, int y); + Raster getData(); + Raster getData(Rectangle r); + WritableRaster copyData(WritableRaster raster); +} // interface RenderedImage diff --git a/libjava/classpath/java/awt/image/ReplicateScaleFilter.java b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java new file mode 100644 index 00000000000..3841e49d538 --- /dev/null +++ b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java @@ -0,0 +1,244 @@ +/* ReplicateScaleFilter.java -- Java class for filtering images + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; + +/** + * This filter should be used for fast scaling of images where the result + * does not need to ensure straight lines are still straight, etc. The + * exact method is not defined by Sun but some sort of fast Box filter should + * probably be correct. + * <br> + * Currently this filter does nothing and needs to be implemented. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class ReplicateScaleFilter extends ImageFilter +{ + public ReplicateScaleFilter(int width, int height) { + destHeight = height; + destWidth = width; + } + + /** + * The height of the destination image. + */ + protected int destHeight; + + /** + * The width of the destination image. + */ + protected int destWidth; + + /** + * The height of the source image. + */ + protected int srcHeight; + + /** + * The width of the source image. + */ + protected int srcWidth; + + /** + * + */ + protected int srcrows[]; + + /** + * + */ + protected int srccols[]; + + /** + * + */ + protected Object outpixbuf; + + /** + * An <code>ImageProducer</code> indicates the size of the image + * being produced using this method. A filter can override this + * method to intercept these calls from the producer in order to + * change either the width or the height before in turn calling + * the consumer's <code>setDimensions</code> method. + * + * @param width the width of the image + * @param height the height of the image + */ + public void setDimensions(int width, int height) + { + srcWidth = width; + srcHeight = height; + + /* If either destHeight or destWidth is < 0, the image should + maintain its original aspect ratio. When both are < 0, + just maintain the original width and height. */ + if (destWidth < 0 && destHeight < 0) + { + destWidth = width; + destHeight = height; + } + else if (destWidth < 0) + { + destWidth = (int) (width * ((double) destHeight / srcHeight)); + } + else if (destHeight < 0) + { + destHeight = (int) (height * ((double) destWidth / srcWidth)); + } + + consumer.setDimensions(destWidth, destHeight); + } + + /** + * An <code>ImageProducer</code> can set a list of properties + * associated with this image by using this method. + * + * @param props the list of properties associated with this image + */ + public void setProperties(Hashtable props) + { + props.put("filters", "ReplicateScaleFilter"); + consumer.setProperties(props); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a <code>byte</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + double rx = ((double) srcWidth) / destWidth; + double ry = ((double) srcHeight) / destHeight; + + int destScansize = (int) Math.round(scansize / rx); + + byte[] destPixels = replicatePixels(x, y, w, h, + model, pixels, offset, scansize, + rx, ry, destScansize); + + consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry), + (int) Math.ceil(w/rx), (int) Math.ceil(h/ry), + model, destPixels, 0, destScansize); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an <code>int</code> at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the <code>ColorModel</code> used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the <code>pixels</code> array + * @param scansize the width to use in extracting pixels from the <code>pixels</code> array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + double rx = ((double) srcWidth) / destWidth; + double ry = ((double) srcHeight) / destHeight; + + int destScansize = (int) Math.round(scansize / rx); + + int[] destPixels = replicatePixels(x, y, w, h, + model, pixels, offset, scansize, + rx, ry, destScansize); + + consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry), + (int) Math.ceil(w/rx), (int) Math.ceil(h/ry), + model, destPixels, 0, destScansize); + } + + private byte[] replicatePixels(int srcx, int srcy, int srcw, int srch, + ColorModel model, byte[] srcPixels, + int srcOffset, int srcScansize, + double rx, double ry, int destScansize) + { + byte[] destPixels = + new byte[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)]; + + int a, b; + for (int i = 0; i < destPixels.length; i++) + { + a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize; + b = (int) ((i % destScansize) * rx); + if ((a + b + srcOffset) < srcPixels.length) + destPixels[i] = srcPixels[a + b + srcOffset]; + } + + return destPixels; + } + + private int[] replicatePixels(int srcx, int srcy, int srcw, int srch, + ColorModel model, int[] srcPixels, + int srcOffset, int srcScansize, + double rx, double ry, int destScansize) + { + int[] destPixels = + new int[(int) Math.ceil(srcw/rx) * (int) Math.ceil(srch/ry)]; + + int a, b; + for (int i = 0; i < destPixels.length; i++) + { + a = (int) ((int) ( ((double) i) / destScansize) * ry) * srcScansize; + b = (int) ((i % destScansize) * rx); + if ((a + b + srcOffset) < srcPixels.length) + destPixels[i] = srcPixels[a + b + srcOffset]; + } + + return destPixels; + } +} + diff --git a/libjava/classpath/java/awt/image/RescaleOp.java b/libjava/classpath/java/awt/image/RescaleOp.java new file mode 100644 index 00000000000..35b42f79ccd --- /dev/null +++ b/libjava/classpath/java/awt/image/RescaleOp.java @@ -0,0 +1,218 @@ +/* Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class RescaleOp implements BufferedImageOp, RasterOp +{ + private float[] scale; + private float[] offsets; + private RenderingHints hints = null; + + public RescaleOp(float[] scaleFactors, + float[] offsets, + RenderingHints hints) + { + this.scale = scaleFactors; + this.offsets = offsets; + this.hints = hints; + } + + public RescaleOp(float scaleFactor, + float offset, + RenderingHints hints) + { + scale = new float[]{ scaleFactor }; + offsets = new float[]{offset}; + this.hints = hints; + } + + public final float[] getScaleFactors(float[] scaleFactors) + { + if (scaleFactors == null) + scaleFactors = new float[scale.length]; + System.arraycopy(scale, 0, scaleFactors, 0, scale.length); + return scaleFactors; + } + + public final float[] getOffsets(float[] offsets) + { + if (offsets == null) + offsets = new float[this.offsets.length]; + System.arraycopy(this.offsets, 0, offsets, 0, this.offsets.length); + return offsets; + } + + public final int getNumFactors() + { + return scale.length; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getRenderingHints() + */ + public RenderingHints getRenderingHints() + { + return hints; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, java.awt.image.BufferedImage) + */ + public final BufferedImage filter(BufferedImage src, BufferedImage dst) + { + // TODO Make sure premultiplied alpha is handled correctly. + // TODO See that color conversion is handled. + // TODO figure out how to use rendering hints. + if (scale.length != offsets.length) + throw new IllegalArgumentException(); + + ColorModel scm = src.getColorModel(); + if (dst == null) dst = createCompatibleDestImage(src, null); + + WritableRaster wsrc = src.getRaster(); + WritableRaster wdst = dst.getRaster(); + + // Share constant across colors except alpha + if (scale.length == 1 || scale.length == scm.getNumColorComponents()) + { + // Construct a raster that doesn't include an alpha band. + int[] subbands = new int[scm.getNumColorComponents()]; + for (int i=0; i < subbands.length; i++) subbands[i] = i; + wsrc = + wsrc.createWritableChild(wsrc.minX, wsrc.minY, wsrc.width, wsrc.height, + wsrc.minX, wsrc.minY, subbands); + } + // else all color bands + + filter(wsrc, wdst); + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + if (dest == null) dest = src.createCompatibleWritableRaster(); + + // Required sanity checks + if (src.numBands != dest.numBands || scale.length != offsets.length) + throw new IllegalArgumentException(); + if (scale.length != 1 && scale.length != src.numBands) + throw new IllegalArgumentException(); + + // Create scaling arrays if needed + float[] lscale = scale; + float[] loff = offsets; + if (scale.length == 1) + { + lscale = new float[src.numBands]; + Arrays.fill(lscale, scale[0]); + loff = new float[src.numBands]; + Arrays.fill(loff, offsets[0]); + } + + // TODO The efficiency here can be improved for various data storage + // patterns, aka SampleModels. + float[] pixel = new float[src.numBands]; + for (int y = src.minY; y < src.height + src.minY; y++) + for (int x = src.minX; x < src.width + src.minX; x++) + { + src.getPixel(x, y, pixel); + for (int b = 0; b < src.numBands; b++) + pixel[b] = pixel[b] * lscale[b] + loff[b]; + dest.setPixel(x, y, pixel); + } + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + if (dstCM == null) dstCM = src.getColorModel(); + WritableRaster wr = src.getRaster().createCompatibleWritableRaster(); + BufferedImage image + = new BufferedImage(dstCM, wr, src.isPremultiplied, null); + return image; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public final Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + */ + public final Point2D getPoint2D(Point2D src, Point2D dst) { + if (dst == null) dst = (Point2D) src.clone(); + else dst.setLocation(src); + return dst; + } + +} diff --git a/libjava/classpath/java/awt/image/SampleModel.java b/libjava/classpath/java/awt/image/SampleModel.java new file mode 100644 index 00000000000..257e30a5bd5 --- /dev/null +++ b/libjava/classpath/java/awt/image/SampleModel.java @@ -0,0 +1,477 @@ +/* Copyright (C) 2000, 2001, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class SampleModel +{ + /** Width of image described. */ + protected int width; + + /** Height of image described. */ + protected int height; + + /** Number of bands in the image described. */ + protected int numBands; + + /** + * The DataBuffer type that is used to store the data of the image + * described. + */ + protected int dataType; + + public SampleModel(int dataType, int w, int h, int numBands) + { + if ((w <= 0) || (h <= 0)) + throw new IllegalArgumentException((w <= 0 ? " width<=0" : " width is ok") + +(h <= 0 ? " height<=0" : " height is ok")); + + // FIXME: How can an int be greater than Integer.MAX_VALUE? + // FIXME: How do we identify an unsupported data type? + + this.dataType = dataType; + this.width = w; + this.height = h; + this.numBands = numBands; + } + + public final int getWidth() + { + return width; + } + + public final int getHeight() + { + return height; + } + + public final int getNumBands() + { + return numBands; + } + + public abstract int getNumDataElements(); + + public final int getDataType() + { + return dataType; + } + + public int getTransferType() + { + // FIXME: Is this a reasonable default implementation? + return dataType; + } + + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) iArray = new int[numBands]; + for (int b=0; b<numBands; b++) iArray[b] = getSample(x, y, b, data); + return iArray; + } + + /** + * + * This method is provided as a faster alternative to getPixel(), + * that can be used when there is no need to decode the pixel into + * separate sample values. + * + * @param obj An array to return the pixel data in. If null, an + * array of the right type and size will be created. + * + * @return A single pixel as an array object of a primitive type, + * based on the transfer type. Eg. if transfer type is + * DataBuffer.TYPE_USHORT, then a short[] object is returned. + */ + public abstract Object getDataElements(int x, int y, Object obj, + DataBuffer data); + + + public Object getDataElements(int x, int y, int w, int h, Object obj, + DataBuffer data) + { + int size = w*h; + int numDataElements = getNumDataElements(); + int dataSize = numDataElements*size; + + if (obj == null) + { + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + obj = new byte[dataSize]; + break; + case DataBuffer.TYPE_USHORT: + obj = new short[dataSize]; + break; + case DataBuffer.TYPE_INT: + obj = new int[dataSize]; + break; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + Object pixelData = null; + int outOffset = 0; + for (int yy = y; yy<(y+h); yy++) + { + for (int xx = x; xx<(x+w); xx++) + { + pixelData = getDataElements(xx, yy, pixelData, data); + System.arraycopy(pixelData, 0, obj, outOffset, + numDataElements); + outOffset += numDataElements; + } + } + return obj; + } + + public abstract void setDataElements(int x, int y, Object obj, + DataBuffer data); + + public void setDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) + { + int size = w*h; + int numDataElements = getNumDataElements(); + int dataSize = numDataElements*size; + + Object pixelData; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + pixelData = new byte[numDataElements]; + break; + case DataBuffer.TYPE_USHORT: + pixelData = new short[numDataElements]; + break; + case DataBuffer.TYPE_INT: + pixelData = new int[numDataElements]; + break; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + int inOffset = 0; + + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + System.arraycopy(obj, inOffset, pixelData, 0, + numDataElements); + setDataElements(xx, yy, pixelData, data); + inOffset += numDataElements; + } + } + } + + public float[] getPixel(int x, int y, float[] fArray, DataBuffer data) + { + if (fArray == null) fArray = new float[numBands]; + + for (int b=0; b<numBands; b++) + { + fArray[b] = getSampleFloat(x, y, b, data); + } + return fArray; + } + + public double[] getPixel(int x, int y, double[] dArray, DataBuffer data) { + if (dArray == null) dArray = new double[numBands]; + for (int b=0; b<numBands; b++) + { + dArray[b] = getSampleDouble(x, y, b, data); + } + return dArray; + } + + /* FIXME: Should it return a banded or pixel interleaved array of + samples? (Assume interleaved.) */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int size = w*h; + int outOffset = 0; + int[] pixel = null; + if (iArray == null) iArray = new int[w*h*numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, iArray, outOffset, numBands); + outOffset += numBands; + } + } + return iArray; + } + + /* FIXME: Should it return a banded or pixel interleaved array of + samples? (Assume interleaved.) */ + public float[] getPixels(int x, int y, int w, int h, float[] fArray, + DataBuffer data) + { + int size = w*h; + int outOffset = 0; + float[] pixel = null; + if (fArray == null) fArray = new float[w*h*numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, fArray, outOffset, numBands); + outOffset += numBands; + } + } + return fArray; + } + + /* FIXME: Should it return a banded or pixel interleaved array of + samples? (Assume interleaved.) */ + public double[] getPixels(int x, int y, int w, int h, double[] dArray, + DataBuffer data) + { + int size = w*h; + int outOffset = 0; + double[] pixel = null; + if (dArray == null) dArray = new double[w*h*numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, dArray, outOffset, numBands); + outOffset += numBands; + } + } + return dArray; + } + + public abstract int getSample(int x, int y, int b, DataBuffer data); + + public float getSampleFloat(int x, int y, int b, DataBuffer data) + { + return getSample(x, y, b, data); + } + + public double getSampleDouble(int x, int y, int b, DataBuffer data) + { + return getSampleFloat(x, y, b, data); + } + + public int[] getSamples(int x, int y, int w, int h, int b, + int[] iArray, DataBuffer data) + { + int size = w*h; + int outOffset = 0; + if (iArray == null) iArray = new int[size]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + iArray[outOffset++] = getSample(xx, yy, b, data); + } + } + return iArray; + } + + public float[] getSamples(int x, int y, int w, int h, int b, + float[] fArray, DataBuffer data) + { + int size = w*h; + int outOffset = 0; + if (fArray == null) fArray = new float[size]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + fArray[outOffset++] = getSampleFloat(xx, yy, b, data); + } + } + return fArray; + } + + public double[] getSamples(int x, int y, int w, int h, int b, + double[] dArray, DataBuffer data) + { + int size = w*h; + int outOffset = 0; + if (dArray == null) dArray = new double[size]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + dArray[outOffset++] = getSampleDouble(xx, yy, b, data); + } + } + return dArray; + } + + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + for (int b=0; b<numBands; b++) setSample(x, y, b, iArray[b], data); + } + + public void setPixel(int x, int y, float[] fArray, DataBuffer data) + { + for (int b=0; b<numBands; b++) setSample(x, y, b, fArray[b], data); + } + + public void setPixel(int x, int y, double[] dArray, DataBuffer data) + { + for (int b=0; b<numBands; b++) setSample(x, y, b, dArray[b], data); + } + + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + int[] pixel = new int[numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + System.arraycopy(iArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + public void setPixels(int x, int y, int w, int h, float[] fArray, + DataBuffer data) + { + int inOffset = 0; + float[] pixel = new float[numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + System.arraycopy(fArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + public void setPixels(int x, int y, int w, int h, double[] dArray, + DataBuffer data) + { + int inOffset = 0; + double[] pixel = new double[numBands]; + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + System.arraycopy(dArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + public abstract void setSample(int x, int y, int b, int s, + DataBuffer data); + + public void setSample(int x, int y, int b, float s, + DataBuffer data) + { + setSample(x, y, b, (int) s, data); + } + + public void setSample(int x, int y, int b, double s, + DataBuffer data) + { + setSample(x, y, b, (float) s, data); + } + + public void setSamples(int x, int y, int w, int h, int b, + int[] iArray, DataBuffer data) + { + int size = w*h; + int inOffset = 0; + for (int yy=y; yy<(y+h); yy++) + for (int xx=x; xx<(x+w); xx++) + setSample(xx, yy, b, iArray[inOffset++], data); + } + + public void setSamples(int x, int y, int w, int h, int b, + float[] fArray, DataBuffer data) + { + int size = w*h; + int inOffset = 0; + for (int yy=y; yy<(y+h); yy++) + for (int xx=x; xx<(x+w); xx++) + setSample(xx, yy, b, fArray[inOffset++], data); + + } + + public void setSamples(int x, int y, int w, int h, int b, + double[] dArray, DataBuffer data) { + int size = w*h; + int inOffset = 0; + for (int yy=y; yy<(y+h); yy++) + for (int xx=x; xx<(x+w); xx++) + setSample(xx, yy, b, dArray[inOffset++], data); + } + + public abstract SampleModel createCompatibleSampleModel(int w, int h); + + /** + * Return a SampleModel with a subset of the bands in this model. + * + * Selects bands.length bands from this sample model. The bands chosen + * are specified in the indices of bands[]. This also permits permuting + * the bands as well as taking a subset. Thus, giving an array with + * 1, 2, 3, ..., numbands, will give an identical sample model. + * + * @param bands Array with band indices to include. + * @return A new sample model + */ + public abstract SampleModel createSubsetSampleModel(int[] bands); + + public abstract DataBuffer createDataBuffer(); + + public abstract int[] getSampleSize(); + + public abstract int getSampleSize(int band); +} diff --git a/libjava/classpath/java/awt/image/ShortLookupTable.java b/libjava/classpath/java/awt/image/ShortLookupTable.java new file mode 100644 index 00000000000..5915a7939a3 --- /dev/null +++ b/libjava/classpath/java/awt/image/ShortLookupTable.java @@ -0,0 +1,162 @@ +/* ShortLookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * ShortLookupTable represents translation arrays for pixel values. It wraps + * one or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public class ShortLookupTable extends LookupTable +{ + // Array of translation tables. + private short data[][]; + + /** + * Creates a new <code>ShortLookupTable</code> instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * tables. If data.length is one, the same table is applied to all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param data Array of lookup tables. + * @exception IllegalArgumentException if offset < 0 or data.length < 1. + */ + public ShortLookupTable(int offset, short[][] data) + throws IllegalArgumentException + { + super(offset, data.length); + this.data = data; + } + + /** + * Creates a new <code>ShortLookupTable</code> instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * table. The same table is applied to all pixel components. + * + * @param offset Offset to be subtracted. + * @param data Lookup table for all components. + * @exception IllegalArgumentException if offset < 0. + */ + public ShortLookupTable(int offset, short[] data) + throws IllegalArgumentException + { + super(offset, 1); + this.data = new short[][] {data}; + } + + /** Return the lookup tables. */ + public final short[][] getTable() + { + return data; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public int[] lookupPixel(int[] src, int[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new int[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][src[i] - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][src[i] - offset]; + + return dst; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public short[] lookupPixel(short[] src, short[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new short[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][((int)src[i]) - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][((int)src[i]) - offset]; + + return dst; + + } +} diff --git a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java new file mode 100644 index 00000000000..6ccce753bd3 --- /dev/null +++ b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java @@ -0,0 +1,449 @@ +/* Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.BitMaskExtent; +import gnu.java.awt.Buffers; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class SinglePixelPackedSampleModel extends SampleModel +{ + private int scanlineStride; + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int[] bitMasks) + { + this(dataType, w, h, w, bitMasks); + } + + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int scanlineStride, int[] bitMasks) + { + super(dataType, w, h, bitMasks.length); + + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + break; + default: + throw new IllegalArgumentException("SinglePixelPackedSampleModel unsupported dataType"); + } + + this.scanlineStride = scanlineStride; + this.bitMasks = bitMasks; + + bitOffsets = new int[numBands]; + sampleSize = new int[numBands]; + + BitMaskExtent extent = new BitMaskExtent(); + for (int b=0; b<numBands; b++) + { + extent.setMask(bitMasks[b]); + sampleSize[b] = extent.bitWidth; + bitOffsets[b] = extent.leastSignificantBit; + } + } + + public int getNumDataElements() + { + return 1; + } + + public SampleModel createCompatibleSampleModel(int w, int h) + { + /* FIXME: We can avoid recalculation of bit offsets and sample + sizes here by passing these from the current instance to a + special private constructor. */ + return new SinglePixelPackedSampleModel(dataType, w, h, bitMasks); + } + + + /** + * Creates a DataBuffer for holding pixel data in the format and + * layout described by this SampleModel. The returned buffer will + * consist of one single bank. + */ + public DataBuffer createDataBuffer() + { + int size; + + // We can save (scanlineStride - width) pixels at the very end of + // the buffer. The Sun reference implementation (J2SE 1.3.1 and + // 1.4.1_01) seems to do this; tested with Mauve test code. + size = scanlineStride * (height - 1) + width; + + return Buffers.createBuffer(getDataType(), size); + } + + + public int[] getSampleSize() + { + return sampleSize; + } + + public int getSampleSize(int band) + { + return sampleSize[band]; + } + + public int getOffset(int x, int y) + { + return scanlineStride*y + x; + } + + public int[] getBitOffsets() + { + return bitOffsets; + } + + public int[] getBitMasks() + { + return bitMasks; + } + + public int getScanlineStride() + { + return scanlineStride; + } + + public SampleModel createSubsetSampleModel(int[] bands) + { + // FIXME: Is this the right way to interpret bands? + + int numBands = bands.length; + + int[] bitMasks = new int[numBands]; + + for (int b=0; b<numBands; b++) + bitMasks[b] = this.bitMasks[bands[b]]; + + return new SinglePixelPackedSampleModel(dataType, width, height, + scanlineStride, bitMasks); + } + + public Object getDataElements(int x, int y, Object obj, + DataBuffer data) + { + int offset = scanlineStride*y + x + data.getOffset(); + + return Buffers.getData(data, offset, obj, + 0, // destination offset, + 1 // length + ); + } + + /** + * This is a more efficient implementation of the default implementation in the super + * class. + * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. + * @param w The width of the pixel rectangle to store in <code>obj</code>. + * @param h The height of the pixel rectangle to store in <code>obj</code>. + * @param obj The primitive array to store the pixels into or null to force creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public Object getDataElements(int x, int y, int w, int h, Object obj, + DataBuffer data) + { + int size = w*h; + int dataSize = size; + Object pixelData = null; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + pixelData = ((DataBufferByte) data).getData(); + if (obj == null) obj = new byte[dataSize]; + break; + case DataBuffer.TYPE_USHORT: + pixelData = ((DataBufferUShort) data).getData(); + if (obj == null) obj = new short[dataSize]; + break; + case DataBuffer.TYPE_INT: + pixelData = ((DataBufferInt) data).getData(); + if (obj == null) obj = new int[dataSize]; + break; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + if(x==0 && scanlineStride == w) + { + // The full width need to be copied therefore we can copy in one shot. + System.arraycopy(pixelData, scanlineStride*y + data.getOffset(), obj, 0, size); + } + else + { + // Since we do not need the full width we need to copy line by line. + int outOffset = 0; + int dataOffset = scanlineStride*y + x + data.getOffset(); + for (int yy = y; yy<(y+h); yy++) + { + System.arraycopy(pixelData, dataOffset, obj, outOffset, w); + dataOffset += scanlineStride; + outOffset += w; + } + } + return obj; + } + + + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = scanlineStride*y + x; + if (iArray == null) iArray = new int[numBands]; + int samples = data.getElem(offset); + + for (int b=0; b<numBands; b++) + iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b]; + + return iArray; + } + + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int offset = scanlineStride*y + x; + if (iArray == null) iArray = new int[numBands*w*h]; + int outOffset = 0; + for (y=0; y<h; y++) + { + int lineOffset = offset; + for (x=0; x<w; x++) + { + int samples = data.getElem(lineOffset++); + for (int b=0; b<numBands; b++) + iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; + } + offset += scanlineStride; + } + return iArray; + } + + public int getSample(int x, int y, int b, DataBuffer data) + { + int offset = scanlineStride*y + x; + int samples = data.getElem(offset); + return (samples & bitMasks[b]) >>> bitOffsets[b]; + } + + /** + * This method implements a more efficient way to set data elements than the default + * implementation of the super class. It sets the data elements line by line instead + * of pixel by pixel. + * @param x The x-coordinate of the data elements in <code>obj</code>. + * @param y The y-coordinate of the data elements in <code>obj</code>. + * @param w The width of the data elements in <code>obj</code>. + * @param h The height of the data elements in <code>obj</code>. + * @param obj The primitive array containing the data elements to set. + * @param data The DataBuffer to store the data elements into. + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public void setDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) + { + + Object pixelData; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + pixelData = ((DataBufferByte) data).getData(); + break; + case DataBuffer.TYPE_USHORT: + pixelData = ((DataBufferUShort) data).getData(); + break; + case DataBuffer.TYPE_INT: + pixelData = ((DataBufferInt) data).getData(); + break; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + + int inOffset = 0; + int dataOffset = scanlineStride*y + x + data.getOffset(); + for (int yy=y; yy<(y+h); yy++) + { + System.arraycopy(obj,inOffset,pixelData,dataOffset,w); + dataOffset += scanlineStride; + inOffset += w; + } + } + + + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int offset = scanlineStride*y + x + data.getOffset(); + + int transferType = getTransferType(); + if (getTransferType() != data.getDataType()) + { + throw new IllegalArgumentException("transfer type ("+ + getTransferType()+"), "+ + "does not match data "+ + "buffer type (" + + data.getDataType() + + ")."); + } + + try + { + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] in = (byte[]) obj; + out.getData()[offset] = in[0]; + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferUShort out = (DataBufferUShort) data; + short[] in = (short[]) obj; + out.getData()[offset] = in[0]; + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] in = (int[]) obj; + out.getData()[offset] = in[0]; + return; + } + // FIXME: Fill in the other possible types. + default: + throw new InternalError(); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While writing data elements" + + ", x="+x+", y="+y+ + ", width="+width+", height="+height+ + ", scanlineStride="+scanlineStride+ + ", offset="+offset+ + ", data.getSize()="+data.getSize()+ + ", data.getOffset()="+data.getOffset()+ + ": " + + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = scanlineStride*y + x; + + int samples = 0; + for (int b=0; b<numBands; b++) + samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b]; + + data.setElem(offset, samples); + } + + /** + * This method implements a more efficient way to set pixels than the default + * implementation of the super class. It copies the pixel components directly + * from the input array instead of creating a intermediate buffer. + * @param x The x-coordinate of the pixel rectangle in <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle in <code>obj</code>. + * @param w The width of the pixel rectangle in <code>obj</code>. + * @param h The height of the pixel rectangle in <code>obj</code>. + * @param iArray The primitive array containing the pixels to set. + * @param data The DataBuffer to store the pixels into. + * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], java.awt.image.DataBuffer) + */ + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + int[] pixel = new int[numBands]; + for (int yy=y; yy<(y+h); yy++) + { + int offset = scanlineStride*yy + x; + for (int xx=x; xx<(x+w); xx++) + { + int samples = 0; + for (int b=0; b<numBands; b++) + samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b]; + data.setElem(0, offset, samples); + inOffset += numBands; + offset += 1; + } + } + } + + + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + int offset = scanlineStride*y + x; + int samples = data.getElem(offset); + int bitMask = bitMasks[b]; + samples &= ~bitMask; + samples |= (s << bitOffsets[b]) & bitMask; + data.setElem(offset, samples); + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + StringBuffer result = new StringBuffer(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i=0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/TileObserver.java b/libjava/classpath/java/awt/image/TileObserver.java new file mode 100644 index 00000000000..99aafbce81c --- /dev/null +++ b/libjava/classpath/java/awt/image/TileObserver.java @@ -0,0 +1,47 @@ +/* TileObserver.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * NEEDS DOCUMENTATION + */ +public interface TileObserver +{ + void tileUpdate(WritableRenderedImage src, int x, int y, boolean writable); +} // interface TileObserver diff --git a/libjava/classpath/java/awt/image/VolatileImage.java b/libjava/classpath/java/awt/image/VolatileImage.java new file mode 100644 index 00000000000..308654162ae --- /dev/null +++ b/libjava/classpath/java/awt/image/VolatileImage.java @@ -0,0 +1,253 @@ +/* VolatileImage.java -- a hardware-accelerated image buffer + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Transparency; +import java.awt.ImageCapabilities; + +/** + * VolatileImage represents a hardware-accelerated graphics buffer. + * The native graphics system may free or damage the resources + * occupied by a VolatileImage at any time. As such, one must + * frequently check the "validity" of the image buffer's resources. + * + * A volatile image's "validity" depends on multiple factors. Its + * resources may have become unavailble in which case you must + * reallocate them. If you move the image from one output device to + * another, you may need to recreate the image's resources if the new + * output device's capabilities don't match the old one's. Finally, + * if the contents of the image's buffer have been damaged you must + * re-render the image. + * + * VolatileImages should always be created using either + * Component.createVolatileImage or + * GraphicsConfiguration.createCompatibleVolatileImage. + */ +public abstract class VolatileImage extends Image + implements Transparency +{ + /** + * One of validate's possible return values. Indicates that the + * image buffer matches its graphics configuration's capabilities + * and that its resources are initialized and ready to be drawn + * into. Also implies that any existing image rendered to the + * buffer is intact and need not be re-rendered. + */ + public static final int IMAGE_OK = 0; + + /** + * One of validate's possible return values. Indicates that the + * image buffer has been restored, meaning that it is valid and + * ready-to-use but that its previous contents have been lost. This + * return value implies that the image needs to be re-rendered. + */ + public static final int IMAGE_RESTORED = 1; + + /** + * One of validate's possible return values. Indicates that the + * image buffer type is unsupported by the current graphics + * configuration. The graphics configuration may have changed, for + * example if the image moved from one output device to another. + * This return value implies that the image buffer's resources + * should be re-acquired. + */ + public static final int IMAGE_INCOMPATIBLE = 2; + + /** + * This image's transparency type. One of Transparency.BITMASK, + * Transparency.OPAQUE or Transparency.TRANSLUCENT. + * + * @since 1.5 + */ + protected int transparency; + + /** + * Default constructor. VolatileImages should not be created + * directly. Rather, you should use Component.createVolatileImage + * or GraphicsConfiguration.createCompatibleVolatileImage. + */ + public VolatileImage() + { + } + + /** + * Returns an image representing the current state of the volatile + * image buffer. The returned image is static meaning that it is + * not updated after being created. It is a snapshot of the + * volatile image buffer at the time getSnapshot is called. + * + * This method, which reads pixels from the volatile image buffer, + * may be less-performant than methods that write pixels since + * VolatileImages are typically optimized for writing. + * + * @return a BufferedImage representing this VolatileImage + */ + public abstract BufferedImage getSnapshot(); + + /** + * Returns the width of this image buffer. + * + * @return the width of this VolatileImage + */ + public abstract int getWidth(); + + /** + * Returns the height of this image buffer. + * + * @return the height of this VolatileImage + */ + public abstract int getHeight(); + + /** + * Calling this method is equivalent to calling + * getSnapshot().getSource(). The ImageProducer produces pixels + * from the BufferedImage snapshot and not from the VolatileImage + * itself. Thus, changes to the VolatileImage that occur after this + * ImageProducer has been retrieved will not be reflected in the + * produced pixels. + * + * This method, which reads pixels from the volatile image buffer, + * may be less-performant than methods that write pixels since + * VolatileImages are typically optimized for writing. + * + * @return an ImageProducer for a static BufferedImage snapshot of + * this image buffer + */ + public ImageProducer getSource() + { + return getSnapshot().getSource(); + } + + /** + * Releases the system resources taken by this image. + */ + public void flush() + { + } + + /** + * Returns a Graphics2D object that can be used to draw onto this + * image. This method is present for backwards-compatibility. It + * simply returns the result of createGraphics. + * + * @return a Graphics2D object that can be used to draw onto this + * image + */ + public Graphics getGraphics() + { + return createGraphics(); + } + + /** + * Returns a Graphics2D object that can be used to draw onto this + * image. + * + * @return a Graphics2D object that can be used to draw onto this + * image + */ + public abstract Graphics2D createGraphics(); + + /** + * Validates and restores this image. If the image buffer has + * become unavailable for further use since the last call to + * validate, validate will allocate a new image buffer. The image + * is also "validated" against the GraphicsConfiguration parameter. + * + * "Validating" the image means checking that the capabilities it + * requires of the output device are indeed supported by the given + * output device. If the image's characteristics, which can be + * highly output device-specific, are not supported by the graphics + * configuration, then IMAGE_INCOMPATIBLE will be returned. This + * can happen, for example, if this image was created on one output + * device, then validated against a different output device with + * different capabilities. Calling validate with a NULL gc argument + * causes validate to skip the validation test. + * + * @param gc graphics configuration against which to validate or + * NULL + * + * @return a code indicating the result of validation. One of: + * <ul> + * <li><code>IMAGE_OK</code> if the image did not need to be + * validated and didn't need to be restored</li> + * <li><code>IMAGE_RESTORED</code> if the image may need to be + * re-rendered.</li> + * <li><code>IMAGE_INCOMPATIBLE</code> if this image's + * requirements are not fulfilled by the graphics configuration + * parameter. This implies that you need to create a new + * VolatileImage for the different GraphicsConfiguration or + * Component. This return value implies nothing about whether the + * image is valid or needs to be re-rendered.</li> + * </ul> + */ + public abstract int validate(GraphicsConfiguration gc); + + /** + * Returns true if the contents of the image buffer have been + * damaged or if the image buffer's resources have been reclaimed by + * the graphics system. You should call this method after a series + * of rendering operations to or from the image, to see if the image + * buffer needs to be revalidated or the image re-rendered. + * + * @return true if the validate should be called, false otherwise + */ + public abstract boolean contentsLost(); + + /** + * Returns the capabilities of this image buffer. + * + * @return the capabilities of this image buffer + */ + public abstract ImageCapabilities getCapabilities(); + + /** + * Returns the transparency type of this image. + * + * @return Transparency.OPAQUE, Transparency.BITMASK or + * Transparency.TRANSLUCENT + */ + public int getTransparency() + { + return transparency; + } +} diff --git a/libjava/classpath/java/awt/image/WritableRaster.java b/libjava/classpath/java/awt/image/WritableRaster.java new file mode 100644 index 00000000000..2e5462fd92e --- /dev/null +++ b/libjava/classpath/java/awt/image/WritableRaster.java @@ -0,0 +1,265 @@ +/* Copyright (C) 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Point; +import java.awt.Rectangle; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class WritableRaster extends Raster +{ + protected WritableRaster(SampleModel sampleModel, Point origin) + { + this(sampleModel, sampleModel.createDataBuffer(), origin); + } + + protected WritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, Point origin) + { + this(sampleModel, dataBuffer, + new Rectangle(origin != null ? origin.x : 0, + origin != null ? origin.y : 0, + sampleModel.getWidth(), sampleModel.getHeight()), + origin, + null); + } + + protected WritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, + WritableRaster parent) + { + super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, + parent); + } + + public WritableRaster getWritableParent() + { + return (WritableRaster) getParent(); + } + + public WritableRaster createWritableTranslatedChild(int childMinX, + int childMinY) + { + // This mirrors the code from the super class + int tcx = sampleModelTranslateX - minX + childMinX; + int tcy = sampleModelTranslateY - minY + childMinY; + + return new WritableRaster(sampleModel, dataBuffer, + new Rectangle(childMinX, childMinY, + width, height), + new Point(tcx, tcy), + this); + } + + public WritableRaster createWritableChild(int parentX, + int parentY, + int w, int h, + int childMinX, + int childMinY, + int[] bandList) + { + // This mirrors the code from the super class + + // FIXME: Throw RasterFormatException if child bounds extends + // beyond the bounds of this raster. + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + return new + WritableRaster(sm, dataBuffer, + new Rectangle(childMinX, childMinY, + w, h), + new Point(sampleModelTranslateX+childMinX-parentX, + sampleModelTranslateY+childMinY-parentY), + this); + } + + public void setDataElements(int x, int y, Object inData) + { + sampleModel.setDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + inData, dataBuffer); + } + + public void setDataElements(int x, int y, Raster inRaster) + { + Object dataElements = getDataElements(0, 0, + inRaster.getWidth(), + inRaster.getHeight(), + null); + setDataElements(x, y, dataElements); + } + + public void setDataElements(int x, int y, int w, int h, + Object inData) + { + sampleModel.setDataElements(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, inData, dataBuffer); + } + + public void setRect(Raster srcRaster) + { + setRect(0, 0, srcRaster); + } + + public void setRect(int dx, int dy, Raster srcRaster) + { + Rectangle targetUnclipped = new Rectangle(srcRaster.getMinX()+dx, + srcRaster.getMinY()+dy, + srcRaster.getWidth(), + srcRaster.getHeight()); + + Rectangle target = getBounds().intersection(targetUnclipped); + + if (target.isEmpty()) return; + + int sx = target.x - dx; + int sy = target.y - dy; + + // FIXME: Do tests on rasters and use get/set data instead. + + /* The JDK documentation seems to imply this implementation. + (the trucation of higher bits), but an implementation using + get/setDataElements would be more efficient. None of the + implementations would do anything sensible when the sample + models don't match. + + But this is probably not the place to consider such + optimizations.*/ + + int[] pixels = srcRaster.getPixels(sx, sy, + target.width, target.height, + (int[]) null); + + setPixels(target.x, target.y, target.width, target.height, pixels); + } + + public void setPixel(int x, int y, int[] iArray) + { + sampleModel.setPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + iArray, dataBuffer); + } + + public void setPixel(int x, int y, float[] fArray) + { + sampleModel.setPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + fArray, dataBuffer); + } + + public void setPixel(int x, int y, double[] dArray) + { + sampleModel.setPixel(x-sampleModelTranslateX, + y-sampleModelTranslateY, + dArray, dataBuffer); + } + + public void setPixels(int x, int y, int w, int h, int[] iArray) + { + sampleModel.setPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, iArray, dataBuffer); + } + + public void setPixels(int x, int y, int w, int h, float[] fArray) + { + sampleModel.setPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, fArray, dataBuffer); + } + + public void setPixels(int x, int y, int w, int h, double[] dArray) + { + sampleModel.setPixels(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, dArray, dataBuffer); + } + + public void setSample(int x, int y, int b, int s) + { + sampleModel.setSample(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, s, dataBuffer); + } + + public void setSample(int x, int y, int b, float s) + { + sampleModel.setSample(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, s, dataBuffer); + } + + public void setSample(int x, int y, int b, double s) + { + sampleModel.setSample(x-sampleModelTranslateX, + y-sampleModelTranslateY, + b, s, dataBuffer); + } + + public void setSamples(int x, int y, int w, int h, int b, + int[] iArray) + { + sampleModel.setSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, iArray, dataBuffer); + } + + public void setSamples(int x, int y, int w, int h, int b, + float[] fArray) + { + sampleModel.setSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, fArray, dataBuffer); + } + + public void setSamples(int x, int y, int w, int h, int b, + double[] dArray) + { + sampleModel.setSamples(x-sampleModelTranslateX, + y-sampleModelTranslateY, + w, h, b, dArray, dataBuffer); + } +} diff --git a/libjava/classpath/java/awt/image/WritableRenderedImage.java b/libjava/classpath/java/awt/image/WritableRenderedImage.java new file mode 100644 index 00000000000..4ed9f101371 --- /dev/null +++ b/libjava/classpath/java/awt/image/WritableRenderedImage.java @@ -0,0 +1,56 @@ +/* WritableRenderedImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Point; + +/** + * NEEDS DOCUMENTATION + */ +public interface WritableRenderedImage extends RenderedImage +{ + void addTileObserver(TileObserver to); + void removeTileObserver(TileObserver to); + WritableRaster getWritableTile(int x, int y); + void releaseWritableTile(int x, int y); + boolean isTileWritable(int x, int y); + Point[] getWritableTileIndices(); + boolean hasTileWriters(); + void setData(Raster r); +} // interface WritableRenderedImage diff --git a/libjava/classpath/java/awt/image/package.html b/libjava/classpath/java/awt/image/package.html new file mode 100644 index 00000000000..50fa99d138f --- /dev/null +++ b/libjava/classpath/java/awt/image/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.image package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.image</title></head> + +<body> +<p>Image consumers, producers and filters.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java b/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java new file mode 100644 index 00000000000..729d857ac89 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java @@ -0,0 +1,56 @@ +/* ContextualRenderedImageFactory.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; + +/** + * STUBBED + */ +public interface ContextualRenderedImageFactory extends RenderedImageFactory +{ + RenderContext mapRenderContext(int i, RenderContext context, + ParameterBlock block, RenderableImage image); + RenderedImage create(RenderContext context, ParameterBlock block); + Rectangle2D getBounds2D(ParameterBlock block); + Object getProperty(ParameterBlock block, String name); + String[] getPropertyNames(); + boolean isDynamic(); +} // interface ContextualRenderedImageFactory diff --git a/libjava/classpath/java/awt/image/renderable/ParameterBlock.java b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java new file mode 100644 index 00000000000..879d3c4fb90 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java @@ -0,0 +1,308 @@ +/* ParameterBlock.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.image.RenderedImage; +import java.io.Serializable; +import java.util.Vector; + +public class ParameterBlock implements Cloneable, Serializable +{ + private static final long serialVersionUID = -7577115551785240750L; + protected Vector sources; + protected Vector parameters; + + public ParameterBlock() + { + this(new Vector(), new Vector()); + } + + public ParameterBlock(Vector sources) + { + this(sources, new Vector()); + } + + public ParameterBlock(Vector sources, Vector parameters) + { + this.sources = sources; + this.parameters = parameters; + } + + public Object shallowClone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // impossible + } + } + + public Object clone() + { + ParameterBlock pb = (ParameterBlock) shallowClone(); + if (sources != null) + pb.sources = (Vector) sources.clone(); + if (parameters != null) + pb.parameters = (Vector) parameters.clone(); + return pb; + } + + public ParameterBlock addSource(Object source) + { + sources.add(source); + return this; + } + + public Object getSource(int index) + { + return sources.get(index); + } + + public ParameterBlock setSource(Object source, int index) + { + sources.ensureCapacity(index); + sources.set(index, source); + return this; + } + + public RenderedImage getRenderedSource(int index) + { + return (RenderedImage) sources.get(index); + } + + public RenderableImage getRenderableSource(int index) + { + return (RenderableImage) sources.get(index); + } + + public int getNumSources() + { + return sources.size(); + } + + public Vector getSources() + { + return sources; + } + + public void setSources(Vector sources) + { + this.sources = sources; + } + + public void removeSources() + { + if (sources != null) + sources.clear(); + } + + public int getNumParameters() + { + return parameters.size(); + } + + public Vector getParameters() + { + return parameters; + } + + public void setParameters(Vector parameters) + { + this.parameters = parameters; + } + + public void removeParameters() + { + if (parameters != null) + parameters.clear(); + } + + public ParameterBlock add(Object o) + { + parameters.add(o); + return this; + } + + public ParameterBlock add(byte b) + { + return add(new Byte(b)); + } + + public ParameterBlock add(char c) + { + return add(new Character(c)); + } + + public ParameterBlock add(short s) + { + return add(new Short(s)); + } + + public ParameterBlock add(int i) + { + return add(new Integer(i)); + } + + public ParameterBlock add(long l) + { + return add(new Long(l)); + } + + public ParameterBlock add(float f) + { + return add(new Float(f)); + } + + public ParameterBlock add(double d) + { + return add(new Double(d)); + } + + public ParameterBlock set(Object o, int index) + { + parameters.ensureCapacity(index); + parameters.set(index, o); + return this; + } + + public ParameterBlock set(byte b, int index) + { + return set(new Byte(b), index); + } + + public ParameterBlock set(char c, int index) + { + return set(new Character(c), index); + } + + public ParameterBlock set(short s, int index) + { + return set(new Short(s), index); + } + + public ParameterBlock set(int i, int index) + { + return set(new Integer(i), index); + } + + public ParameterBlock set(long l, int index) + { + return set(new Long(l), index); + } + + public ParameterBlock set(float f, int index) + { + return set(new Float(f), index); + } + + public ParameterBlock set(double d, int index) + { + return set(new Double(d), index); + } + + public Object getObjectParameter(int index) + { + return parameters.get(index); + } + + public byte getByteParameter(int index) + { + return ((Byte) parameters.get(index)).byteValue(); + } + + public char getCharParameter(int index) + { + return ((Character) parameters.get(index)).charValue(); + } + + public short getShortParameter(int index) + { + return ((Short) parameters.get(index)).shortValue(); + } + + public int getIntParameter(int index) + { + return ((Integer) parameters.get(index)).intValue(); + } + + public long getLongParameter(int index) + { + return ((Long) parameters.get(index)).longValue(); + } + + public float getFloatParameter(int index) + { + return ((Float) parameters.get(index)).floatValue(); + } + + public double getDoubleParameter(int index) + { + return ((Double) parameters.get(index)).doubleValue(); + } + + public Class[] getParamClasses() + { + int i = parameters.size(); + Class[] result = new Class[i]; + while (--i >= 0) + { + Class c = parameters.get(i).getClass(); + if (c == Byte.class) + result[i] = byte.class; + else if (c == Character.class) + result[i] = char.class; + else if (c == Short.class) + result[i] = short.class; + else if (c == Integer.class) + result[i] = int.class; + else if (c == Long.class) + result[i] = long.class; + else if (c == Float.class) + result[i] = float.class; + else if (c == Double.class) + result[i] = double.class; + else + result[i] = c; + } + return result; + } +} // class ParameterBlock diff --git a/libjava/classpath/java/awt/image/renderable/RenderContext.java b/libjava/classpath/java/awt/image/renderable/RenderContext.java new file mode 100644 index 00000000000..67f0b8adb23 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderContext.java @@ -0,0 +1,141 @@ +/* RenderContext.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; + +public class RenderContext implements Cloneable +{ + private AffineTransform xform; + private Shape aoi; + private RenderingHints hints; + + public RenderContext(AffineTransform xform, Shape aoi, RenderingHints hints) + { + this.xform = xform; + this.aoi = aoi; + this.hints = hints; + } + + public RenderContext(AffineTransform xform) + { + this(xform, null, null); + } + + public RenderContext(AffineTransform xform, RenderingHints hints) + { + this(xform, null, hints); + } + + public RenderContext(AffineTransform xform, Shape aoi) + { + this(xform, aoi, null); + } + + public RenderingHints getRenderingHints() + { + return hints; + } + + public void setRenderingHints(RenderingHints hints) + { + this.hints = hints; + } + + public void setTransform(AffineTransform xform) + { + this.xform = xform; + } + + public void preConcatenateTransform(AffineTransform pre) + { + preConcetenateTransform (pre); + } + + /** @deprecated */ + public void preConcetenateTransform(AffineTransform pre) + { + xform.preConcatenate (pre); + } + + public void concatenateTransform(AffineTransform post) + { + concetenateTransform (post); + } + + /** @deprecated */ + public void concetenateTransform(AffineTransform post) + { + xform.concatenate (post); + } + + public AffineTransform getTransform() + { + return xform; + } + + public void setAreaOfInterest(Shape aoi) + { + this.aoi = aoi; + } + + public Shape getAreaOfInterest() + { + return aoi; + } + + public Object clone() + { + try + { + RenderContext copy = (RenderContext) super.clone(); + if (xform != null) + copy.xform = (AffineTransform) xform.clone(); + if (hints != null) + copy.hints = (RenderingHints) hints.clone(); + return copy; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // impossible + } + } +} // class RenderContext diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImage.java b/libjava/classpath/java/awt/image/renderable/RenderableImage.java new file mode 100644 index 00000000000..45d2eb7ebf0 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImage.java @@ -0,0 +1,62 @@ +/* RenderableImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; +import java.util.Vector; + +public interface RenderableImage +{ + String HINTS_OBSERVED = "HINTS_OBSERVED"; + + Vector getSources(); + Object getProperty(String name); + String[] getPropertyNames(); + boolean isDynamic(); + float getWidth(); + float getHeight(); + float getMinX(); + float getMinY(); + RenderedImage createScaledRendering(int w, int h, RenderingHints hints); + RenderedImage createDefaultRendering(); + RenderedImage createRendering(RenderContext context); + +} // interface RenderableImage + diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java new file mode 100644 index 00000000000..5385a82a317 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java @@ -0,0 +1,157 @@ +/* RenderableImageOp.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.RenderedImage; +import java.util.Vector; + +public class RenderableImageOp implements RenderableImage +{ + private final ContextualRenderedImageFactory crif; + private ParameterBlock block; + + public RenderableImageOp(ContextualRenderedImageFactory crif, + ParameterBlock block) + { + this.crif = crif; + this.block = (ParameterBlock) block.clone(); + } + + public Vector getSources() + { + if (block.sources == null) + return null; + int size = block.sources.size(); + Vector v = new Vector(); + for (int i = 0; i < size; i++) + { + Object o = block.sources.get(i); + if (o instanceof RenderableImage) + v.add(o); + } + return v; + } + + public Object getProperty(String name) + { + return crif.getProperty(block, name); + } + + public String[] getPropertyNames() + { + return crif.getPropertyNames(); + } + + public boolean isDynamic() + { + return crif.isDynamic(); + } + + public float getWidth() + { + return (float) crif.getBounds2D(block).getWidth(); + } + + public float getHeight() + { + return (float) crif.getBounds2D(block).getHeight(); + } + + public float getMinX() + { + return (float) crif.getBounds2D(block).getX(); + } + + public float getMinY() + { + return (float) crif.getBounds2D(block).getY(); + } + + public ParameterBlock setParameterBlock(ParameterBlock block) + { + ParameterBlock result = this.block; + this.block = (ParameterBlock) block.clone(); + return result; + } + + public ParameterBlock getParameterBlock() + { + return block; + } + + public RenderedImage createScaledRendering(int w, int h, + RenderingHints hints) + { + if (w == 0) + if (h == 0) + throw new IllegalArgumentException(); + else + w = Math.round(h * getWidth() / getHeight()); + if (h == 0) + h = Math.round(w * getHeight() / getWidth()); + AffineTransform xform = AffineTransform.getScaleInstance(w * getWidth(), + h * getHeight()); + return createRendering(new RenderContext(xform, hints)); + } + + public RenderedImage createDefaultRendering() + { + return createRendering(new RenderContext(new AffineTransform())); + } + + public RenderedImage createRendering(RenderContext context) + { + ParameterBlock copy = (ParameterBlock) block.clone(); + int i = block.sources.size(); + while (--i >= 0) + { + Object o = block.sources.get(i); + if (o instanceof RenderableImage) + { + RenderableImage ri = (RenderableImage) o; + RenderContext rc = crif.mapRenderContext(i, context, block, ri); + copy.sources.set(i, ri.createRendering(rc)); + } + } + // Now copy.sources should be only RenderedImages. + return crif.create(context, copy); + } +} // class RenderableImageOp diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java b/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java new file mode 100644 index 00000000000..78f3051ea99 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java @@ -0,0 +1,79 @@ +/* RenderableImageProducer.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; + +public class RenderableImageProducer implements ImageProducer, Runnable +{ + public RenderableImageProducer(RenderableImage image, RenderContext context) + { + throw new Error("not implemented"); + } + + public void setRenderContext(RenderContext context) + { + } + + public void addConsumer(ImageConsumer consumer) + { + } + + public boolean isConsumer(ImageConsumer consumer) + { + return false; + } + + public void removeConsumer(ImageConsumer consumer) + { + } + + public void startProduction(ImageConsumer consumer) + { + } + + public void requestTopDownLeftRightResend(ImageConsumer consumer) + { + } + + public void run() + { + } +} // class RenderableImageProducer diff --git a/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java b/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java new file mode 100644 index 00000000000..6ff4cb03176 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java @@ -0,0 +1,47 @@ +/* RenderedImageFactory.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; + +public interface RenderedImageFactory +{ + RenderedImage create(ParameterBlock block, RenderingHints hints); +} // interface RenderedImageFactory diff --git a/libjava/classpath/java/awt/image/renderable/package.html b/libjava/classpath/java/awt/image/renderable/package.html new file mode 100644 index 00000000000..a24237e7238 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.image.renderable package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.image.renderable</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/package.html b/libjava/classpath/java/awt/package.html new file mode 100644 index 00000000000..c5ff9881af1 --- /dev/null +++ b/libjava/classpath/java/awt/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt</title></head> + +<body> +<p>Abstract Window Toolkit classes.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/peer/ButtonPeer.java b/libjava/classpath/java/awt/peer/ButtonPeer.java new file mode 100644 index 00000000000..a55fc2236db --- /dev/null +++ b/libjava/classpath/java/awt/peer/ButtonPeer.java @@ -0,0 +1,46 @@ +/* ButtonPeer.java -- Peer interface for buttons + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ButtonPeer extends ComponentPeer +{ + void setLabel (String label); + +} // interface ButtonPeer + diff --git a/libjava/classpath/java/awt/peer/CanvasPeer.java b/libjava/classpath/java/awt/peer/CanvasPeer.java new file mode 100644 index 00000000000..4b33835a455 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CanvasPeer.java @@ -0,0 +1,45 @@ +/* CanvasPeer.java -- Peer interface for a canvas + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface CanvasPeer extends ComponentPeer +{ + +} // interface CanvasPeer + diff --git a/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java b/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java new file mode 100644 index 00000000000..5213dc90693 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java @@ -0,0 +1,46 @@ +/* CheckboxMenuItemPeer.java -- Peer interface for checkbox menu items + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface CheckboxMenuItemPeer extends MenuItemPeer +{ + void setState (boolean state); + +} // interface CheckboxMenuItemPeer + diff --git a/libjava/classpath/java/awt/peer/CheckboxPeer.java b/libjava/classpath/java/awt/peer/CheckboxPeer.java new file mode 100644 index 00000000000..8b23b3f6f33 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CheckboxPeer.java @@ -0,0 +1,52 @@ +/* CheckboxPeer.java -- Interface for checkbox peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.CheckboxGroup; + +public interface CheckboxPeer extends ComponentPeer +{ + void setCheckboxGroup (CheckboxGroup group); + + void setLabel (String label); + + void setState (boolean state); + +} // interface CheckboxPeer + diff --git a/libjava/classpath/java/awt/peer/ChoicePeer.java b/libjava/classpath/java/awt/peer/ChoicePeer.java new file mode 100644 index 00000000000..8ed11072e15 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ChoicePeer.java @@ -0,0 +1,54 @@ +/* ChoicePeer.java -- Peer for choice box + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ChoicePeer extends ComponentPeer +{ + void add (String item, int index); + + void addItem (String item, int index); + + void remove (int index); + + void removeAll(); + + void select (int index); + +} // interface ChoicePeer + diff --git a/libjava/classpath/java/awt/peer/ComponentPeer.java b/libjava/classpath/java/awt/peer/ComponentPeer.java new file mode 100644 index 00000000000..7ed8f6051db --- /dev/null +++ b/libjava/classpath/java/awt/peer/ComponentPeer.java @@ -0,0 +1,187 @@ +/* ComponentPeer.java -- Toplevel component peer + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.PaintEvent; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; + +public interface ComponentPeer +{ + int checkImage(Image img, int width, int height, + ImageObserver ob); + Image createImage(ImageProducer prod); + Image createImage(int width, int height); + void disable(); + void dispose(); + void enable(); + ColorModel getColorModel(); + FontMetrics getFontMetrics(Font f); + Graphics getGraphics(); + Point getLocationOnScreen(); + Dimension getMinimumSize(); + Dimension getPreferredSize(); + Toolkit getToolkit(); + void handleEvent(AWTEvent e); + void hide(); + + /** + * Part of the earlier 1.1 API, replaced by isFocusable(). + */ + boolean isFocusTraversable(); + boolean isFocusable(); + Dimension minimumSize(); + Dimension preferredSize(); + void paint(Graphics graphics); + boolean prepareImage(Image img, int width, int height, + ImageObserver ob); + void print(Graphics graphics); + void repaint(long tm, int x, int y, int width, int height); + + /** + * Part of the earlier 1.1 API, apparently replaced by argument + * form of the same method. + */ + void requestFocus(); + boolean requestFocus (Component source, boolean bool1, boolean bool2, long x); + + void reshape(int x, int y, int width, int height); + void setBackground(Color color); + void setBounds(int x, int y, int width, int height); + + /** + * Part of the earlier 1.1 API, apparently no longer needed. + */ + void setCursor(Cursor cursor); + + void setEnabled(boolean enabled); + void setFont(Font font); + void setForeground(Color color); + void setVisible(boolean visible); + void show(); + + /** + * Get the graphics configuration of the component. The color model + * of the component can be derived from the configuration. + */ + GraphicsConfiguration getGraphicsConfiguration(); + + /** + * Part of an older API, no longer needed. + */ + void setEventMask (long mask); + + // Methods below are introduced since 1.1 + boolean isObscured(); + boolean canDetermineObscurity(); + void coalescePaintEvent(PaintEvent e); + void updateCursorImmediately(); + boolean handlesWheelScrolling(); + + /** + * A convenience method that creates a volatile image. The volatile + * image is created on the screen device on which this component is + * displayed, in the device's current graphics configuration. + * + * @param width width of the image + * @param height height of the image + * + * @see VolatileImage + * + * @since 1.2 + */ + VolatileImage createVolatileImage(int width, int height); + + /** + * Create a number of image buffers that implement a buffering + * strategy according to the given capabilities. + * + * @param numBuffers the number of buffers + * @param caps the buffering capabilities + * + * @throws AWTException if the specified buffering strategy is not + * implemented + * + * @since 1.2 + */ + void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException; + + /** + * Return the back buffer of this component. + * + * @return the back buffer of this component. + * + * @since 1.2 + */ + Image getBackBuffer(); + + /** + * Perform a page flip, leaving the contents of the back buffer in + * the specified state. + * + * @param contents the state in which to leave the back buffer + * + * @since 1.2 + */ + void flip(BufferCapabilities.FlipContents contents); + + /** + * Destroy the resources created by createBuffers. + * + * @since 1.2 + */ + void destroyBuffers(); +} diff --git a/libjava/classpath/java/awt/peer/ContainerPeer.java b/libjava/classpath/java/awt/peer/ContainerPeer.java new file mode 100644 index 00000000000..f1373a16472 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ContainerPeer.java @@ -0,0 +1,59 @@ +/* ContainerPeer.java -- Interface for container peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Insets; + +public interface ContainerPeer extends ComponentPeer +{ + Insets insets(); + + Insets getInsets(); + + void beginValidate(); + + void endValidate(); + + void beginLayout(); + + void endLayout(); + + boolean isPaintPending(); + +} // interface ContainerPeer + diff --git a/libjava/classpath/java/awt/peer/DialogPeer.java b/libjava/classpath/java/awt/peer/DialogPeer.java new file mode 100644 index 00000000000..e26d64ff96a --- /dev/null +++ b/libjava/classpath/java/awt/peer/DialogPeer.java @@ -0,0 +1,48 @@ +/* DialogPeer.java -- Interface for dialog box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface DialogPeer extends WindowPeer +{ + void setResizable (boolean resizeable); + + void setTitle (String title); + +} // interface DialogPeer + diff --git a/libjava/classpath/java/awt/peer/FileDialogPeer.java b/libjava/classpath/java/awt/peer/FileDialogPeer.java new file mode 100644 index 00000000000..7db1798162a --- /dev/null +++ b/libjava/classpath/java/awt/peer/FileDialogPeer.java @@ -0,0 +1,52 @@ +/* FileDialogPeer.java -- Interface for file selection dialog box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.io.FilenameFilter; + +public interface FileDialogPeer extends DialogPeer +{ + void setFile (String file); + + void setDirectory (String dir); + + void setFilenameFilter (FilenameFilter ff); + +} // interface FileDialogPeer + diff --git a/libjava/classpath/java/awt/peer/FontPeer.java b/libjava/classpath/java/awt/peer/FontPeer.java new file mode 100644 index 00000000000..f0ba6d830c8 --- /dev/null +++ b/libjava/classpath/java/awt/peer/FontPeer.java @@ -0,0 +1,45 @@ +/* FontPeer.java -- Interface for font peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface FontPeer +{ + +} // interface FontPeer + diff --git a/libjava/classpath/java/awt/peer/FramePeer.java b/libjava/classpath/java/awt/peer/FramePeer.java new file mode 100644 index 00000000000..13498ff2f67 --- /dev/null +++ b/libjava/classpath/java/awt/peer/FramePeer.java @@ -0,0 +1,55 @@ +/* FramePeer.java -- Interface for frame peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Image; +import java.awt.MenuBar; +import java.awt.Rectangle; + +public interface FramePeer extends WindowPeer +{ + void setIconImage(Image image); + void setMenuBar(MenuBar mb); + void setResizable(boolean resizable); + void setTitle(String title); + int getState(); + void setState(int state); + void setMaximizedBounds(Rectangle r); +} // interface FramePeer + diff --git a/libjava/classpath/java/awt/peer/LabelPeer.java b/libjava/classpath/java/awt/peer/LabelPeer.java new file mode 100644 index 00000000000..d0fca462fdc --- /dev/null +++ b/libjava/classpath/java/awt/peer/LabelPeer.java @@ -0,0 +1,46 @@ +/* LabelPeer.java -- Interface for simple text lable peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface LabelPeer extends ComponentPeer +{ + void setAlignment(int alignment); + void setText(String text); +} // interface LabelPeer + diff --git a/libjava/classpath/java/awt/peer/LightweightPeer.java b/libjava/classpath/java/awt/peer/LightweightPeer.java new file mode 100644 index 00000000000..93cad7a0318 --- /dev/null +++ b/libjava/classpath/java/awt/peer/LightweightPeer.java @@ -0,0 +1,45 @@ +/* LightweightPeer.java -- Interface for lightweight peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface LightweightPeer extends ComponentPeer +{ + +} // interface LightweightPeer + diff --git a/libjava/classpath/java/awt/peer/ListPeer.java b/libjava/classpath/java/awt/peer/ListPeer.java new file mode 100644 index 00000000000..c0f765d1655 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ListPeer.java @@ -0,0 +1,61 @@ +/* ListPeer.java -- Interface for list box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface ListPeer extends ComponentPeer +{ + void add(String item, int index); + void addItem(String item, int index); + void clear(); + void delItems(int start_index, int end_index); + void deselect(int index); + int[] getSelectedIndexes(); + void makeVisible(int index); + Dimension minimumSize(int s); + Dimension preferredSize(int s); + void removeAll(); + void select(int index); + void setMultipleMode(boolean multi); + void setMultipleSelections(boolean multi); + Dimension getPreferredSize(int s); + Dimension getMinimumSize(int s); +} // interface ListPeer + diff --git a/libjava/classpath/java/awt/peer/MenuBarPeer.java b/libjava/classpath/java/awt/peer/MenuBarPeer.java new file mode 100644 index 00000000000..c5f7c581ff5 --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuBarPeer.java @@ -0,0 +1,48 @@ +/* MenuBarPeer.java -- Interface for menu bar peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Menu; + +public interface MenuBarPeer extends MenuComponentPeer +{ + void addHelpMenu(Menu menu); + void delMenu(int index); +} // interface MenuBarPeer + diff --git a/libjava/classpath/java/awt/peer/MenuComponentPeer.java b/libjava/classpath/java/awt/peer/MenuComponentPeer.java new file mode 100644 index 00000000000..1b10ca1f4c7 --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuComponentPeer.java @@ -0,0 +1,45 @@ +/* MenuComponentPeer.java -- + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface MenuComponentPeer +{ + void dispose(); +} // interface MenuComponentPeer + diff --git a/libjava/classpath/java/awt/peer/MenuItemPeer.java b/libjava/classpath/java/awt/peer/MenuItemPeer.java new file mode 100644 index 00000000000..3ba1027e501 --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuItemPeer.java @@ -0,0 +1,48 @@ +/* MenuItemPeer.java -- Interface for menu item peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface MenuItemPeer extends MenuComponentPeer +{ + void disable(); + void enable(); + void setEnabled(boolean enabled); + void setLabel(String text); +} // interface MenuItemPeer + diff --git a/libjava/classpath/java/awt/peer/MenuPeer.java b/libjava/classpath/java/awt/peer/MenuPeer.java new file mode 100644 index 00000000000..c51ea73bb6c --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuPeer.java @@ -0,0 +1,48 @@ +/* MenuPeer.java -- Interface for menu peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.MenuItem; + +public interface MenuPeer extends MenuItemPeer +{ + void addItem (MenuItem item); + void delItem (int index); +} + diff --git a/libjava/classpath/java/awt/peer/PanelPeer.java b/libjava/classpath/java/awt/peer/PanelPeer.java new file mode 100644 index 00000000000..192632e46b3 --- /dev/null +++ b/libjava/classpath/java/awt/peer/PanelPeer.java @@ -0,0 +1,45 @@ +/* PanelPeer.java -- Interface for panel peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface PanelPeer extends ContainerPeer +{ + +} // interface PanelPeer + diff --git a/libjava/classpath/java/awt/peer/PopupMenuPeer.java b/libjava/classpath/java/awt/peer/PopupMenuPeer.java new file mode 100644 index 00000000000..2e8f4bbedc5 --- /dev/null +++ b/libjava/classpath/java/awt/peer/PopupMenuPeer.java @@ -0,0 +1,53 @@ +/* PopupMenuPeer.java -- Interface for popup menu peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Component; +import java.awt.Event; + +public interface PopupMenuPeer extends MenuPeer +{ + /** + * Part of the older API, replaced by event version instead. + */ + void show (Component origin, int x, int y); + + void show (Event e); +} // interface PopupMenuPeer + diff --git a/libjava/classpath/java/awt/peer/RobotPeer.java b/libjava/classpath/java/awt/peer/RobotPeer.java new file mode 100644 index 00000000000..db81c809d72 --- /dev/null +++ b/libjava/classpath/java/awt/peer/RobotPeer.java @@ -0,0 +1,54 @@ +/* RobotPeer.java -- Interface for programatically driving GUI + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Rectangle; + +public interface RobotPeer +{ + void mouseMove (int x, int y); + void mousePress (int buttons); + void mouseRelease (int buttons); + void mouseWheel (int wheelAmt); + void keyPress (int keycode); + void keyRelease (int keycode); + int getRGBPixel (int x, int y); + int[] getRGBPixels (Rectangle screen); +} // interface RobotPeer + diff --git a/libjava/classpath/java/awt/peer/ScrollPanePeer.java b/libjava/classpath/java/awt/peer/ScrollPanePeer.java new file mode 100644 index 00000000000..de4331e04d1 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ScrollPanePeer.java @@ -0,0 +1,52 @@ +/* ScrollPanePeer.java -- Interface for scrollable panes + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Adjustable; + +public interface ScrollPanePeer extends ContainerPeer +{ + int getHScrollbarHeight(); + int getVScrollbarWidth(); + void setScrollPosition(int h, int v); + void childResized(int width, int height); + void setUnitIncrement(Adjustable item, int inc); + void setValue(Adjustable item, int value); +} // interface ScollPanePeer + diff --git a/libjava/classpath/java/awt/peer/ScrollbarPeer.java b/libjava/classpath/java/awt/peer/ScrollbarPeer.java new file mode 100644 index 00000000000..fe4f24d649f --- /dev/null +++ b/libjava/classpath/java/awt/peer/ScrollbarPeer.java @@ -0,0 +1,47 @@ +/* ScrollbarPeer.java -- Interface for scrollbar peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ScrollbarPeer extends ComponentPeer +{ + void setLineIncrement(int inc); + void setPageIncrement(int inc); + void setValues(int value, int visible, int min, int max); +} // interface ScrollbarPeer + diff --git a/libjava/classpath/java/awt/peer/TextAreaPeer.java b/libjava/classpath/java/awt/peer/TextAreaPeer.java new file mode 100644 index 00000000000..354e46d9cc6 --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextAreaPeer.java @@ -0,0 +1,53 @@ +/* TextAreaPeer.java -- Interface for text area peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface TextAreaPeer extends TextComponentPeer +{ + void insert(String text, int pos); + void insertText(String text, int pos); + Dimension minimumSize(int rows, int cols); + Dimension getMinimumSize(int rows, int cols); + Dimension preferredSize(int rows, int cols); + Dimension getPreferredSize(int rows, int cols); + void replaceRange(String text, int start_pos, int end_pos); + void replaceText(String text, int start_pos, int end_pos); +} // interface TextAreaPeer diff --git a/libjava/classpath/java/awt/peer/TextComponentPeer.java b/libjava/classpath/java/awt/peer/TextComponentPeer.java new file mode 100644 index 00000000000..cacc7d8de4e --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextComponentPeer.java @@ -0,0 +1,57 @@ +/* TextComponentPeer.java -- Superclass interface for text components + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Rectangle; + +public interface TextComponentPeer extends ComponentPeer +{ + int getSelectionEnd(); + int getSelectionStart(); + String getText(); + void setText(String text); + void select(int start_pos, int end_pos); + void setEditable(boolean editable); + int getCaretPosition(); + void setCaretPosition(int pos); + int getIndexAtPoint(int x, int y); + Rectangle getCharacterBounds(int pos); + long filterEvents(long filter); +} // interface TextComponentPeer + diff --git a/libjava/classpath/java/awt/peer/TextFieldPeer.java b/libjava/classpath/java/awt/peer/TextFieldPeer.java new file mode 100644 index 00000000000..e68d6663fdc --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextFieldPeer.java @@ -0,0 +1,52 @@ +/* TextFieldPeer.java -- Interface for text field peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface TextFieldPeer extends TextComponentPeer +{ + Dimension minimumSize(int len); + Dimension preferredSize(int len); + Dimension getMinimumSize(int len); + Dimension getPreferredSize(int len); + void setEchoChar(char echo_char); + void setEchoCharacter(char echo_char); +} // interface TextFieldPeer + diff --git a/libjava/classpath/java/awt/peer/WindowPeer.java b/libjava/classpath/java/awt/peer/WindowPeer.java new file mode 100644 index 00000000000..8f136dd2d85 --- /dev/null +++ b/libjava/classpath/java/awt/peer/WindowPeer.java @@ -0,0 +1,46 @@ +/* WindowPeer.java -- Interface for window peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface WindowPeer extends ContainerPeer +{ + void toBack(); + void toFront(); +} // interface WindowPeer + diff --git a/libjava/classpath/java/awt/peer/package.html b/libjava/classpath/java/awt/peer/package.html new file mode 100644 index 00000000000..7a7458c177f --- /dev/null +++ b/libjava/classpath/java/awt/peer/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.peer package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.peer</title></head> + +<body> +<p>Interfaces for using native interface components.</p> + +</body> +</html> diff --git a/libjava/classpath/java/awt/print/Book.java b/libjava/classpath/java/awt/print/Book.java new file mode 100644 index 00000000000..b084a1723f1 --- /dev/null +++ b/libjava/classpath/java/awt/print/Book.java @@ -0,0 +1,159 @@ +/* Book.java -- A mixed group of pages to print. + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import java.util.Vector; + +/** + * This class allows documents to be created with different paper types, + * page formatters, and painters. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Book implements Pageable +{ + /** + * Painter objects for the book. + */ + Vector printables = new Vector(); + + /** + * Page formats for the book. + */ + Vector page_formats = new Vector(); + + /** + * Initializes a new instance of <code>Book</code> that is empty. + */ + public Book() + { + } + + /** + * Returns the number of pages in this book. + * + * @return The number of pages in this book. + */ + public int getNumberOfPages() + { + return printables.size(); + } + + /** + * This method returns the <code>PageFormat</code> object for the + * specified page. + * + * @param page_number The number of the page to get information for, where + * page numbers start at 0. + * + * @return The <code>PageFormat</code> object for the specified page. + * + * @exception IndexOutOfBoundsException If the page number is not valid. + */ + public PageFormat getPageFormat(int page_number) + { + return (PageFormat) page_formats.elementAt(page_number); + } + + /** + * This method returns the <code>Printable</code> object for the + * specified page. + * + * @param page_number The number of the page to get information for, where + * page numbers start at 0. + * + * @return The <code>Printable</code> object for the specified page. + * + * @exception IndexOutOfBoundsException If the page number is not valid. + */ + public Printable getPrintable(int page_number) + { + return (Printable) printables.elementAt(page_number); + } + + /** + * This method appends a page to the end of the book. + * + * @param printable The <code>Printable</code> for this page. + * @param page_format The <code>PageFormat</code> for this page. + * + * @exception NullPointerException If either argument is <code>null</code>. + */ + public void append(Printable printable, PageFormat page_format) + { + append(printable, page_format, 1); + } + + /** + * This method appends the specified number of pages to the end of the book. + * Each one will be associated with the specified <code>Printable</code> + * and <code>PageFormat</code>. + * + * @param printable The <code>Printable</code> for this page. + * @param page_format The <code>PageFormat</code> for this page. + * @param num_pages The number of pages to append. + * + * @exception NullPointerException If any argument is <code>null</code>. + */ + public void append(Printable printable, PageFormat page_format, int num_pages) + { + for (int i = 0; i < num_pages; i++) + { + printables.addElement(printable); + page_formats.addElement(page_format); + } + } + + /** + * This method changes the <code>Printable</code> and <code>PageFormat</code> + * for the specified page. The page must already exist or an exception + * will be thrown. + * + * @param page_num The page number to alter. + * @param printable The new <code>Printable</code> for the page. + * @param page_format The new <code>PageFormat</code> for the page. + * + * @throws IndexOutOfBoundsException If the specified page does not exist. + */ + public void setPage(int page_num, Printable printable, PageFormat page_format) + { + printables.setElementAt(printable, page_num); + page_formats.setElementAt(page_format, page_num); + } +} diff --git a/libjava/classpath/java/awt/print/PageFormat.java b/libjava/classpath/java/awt/print/PageFormat.java new file mode 100644 index 00000000000..6399552de44 --- /dev/null +++ b/libjava/classpath/java/awt/print/PageFormat.java @@ -0,0 +1,292 @@ +/* PageFormat.java -- Information about the page format + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This class contains information about the desired page format to + * use for printing a particular set of pages. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PageFormat implements Cloneable +{ + +/* + * Static Variables + */ + +/** + * A constant for a landscaped page orientation. Used by + * <code>getOrientation</code> and <code>setOrientation</code>. + */ +public static final int LANDSCAPE = 0; + +/** + * A constant for a portrait page orientation. Used by + * <code>getOrientation</code> and <code>setOrientation</code>. + */ +public static final int PORTRAIT = 1; + +/** + * A constant for a reversed landscaped page orientation. This is + * the orientation used by Macintosh's for landscape. The origin is + * in the upper right hand corner instead of the upper left. The + * X and Y axes are reversed. Used by <code>getOrientation</code> and + * <code>setOrientation</code>. + */ +public static final int REVERSE_LANDSCAPE = 2; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +// The page orientation +private int orientation; + +// The paper type +private Paper paper; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method creates a default page layout, which will be in portrait + * format. + */ +public +PageFormat() +{ + this.paper = new Paper(); + this.orientation = PORTRAIT; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method returns the width of the page, in 1/72nd's of an inch. The + * "width" measured depends on orientation. + * + * @return The width of the page. + */ +public double +getWidth() +{ + return(paper.getWidth()); +} + +/*************************************************************************/ + +/** + * This method returns the height of the page, in 1/72nd's of an inch. + * The "height" measured depends on the orientation. + * + * @return The height of the page. + */ +public double +getHeight() +{ + return(paper.getHeight()); +} + +/*************************************************************************/ + +/** + * This method returns the X coordinate value of the upper leftmost + * drawable area of the paper. + * + * @return The upper leftmost imageable X coordinate. + */ +public double +getImageableX() +{ + return(paper.getImageableX()); +} + +/*************************************************************************/ + +/** + * This method returns the Y coordinate value of the upper leftmost + * drawable area of the paper. + * + * @return The upper leftmost imageable Y coordinate. + */ +public double +getImageableY() +{ + return(paper.getImageableY()); +} + +/*************************************************************************/ + +/** + * This method returns the imageable width of the paper, in 1/72nd's of + * an inch. + * + * @return The imageable width of the paper. + */ +public double +getImageableWidth() +{ + return(paper.getImageableWidth()); +} + +/*************************************************************************/ + +/** + * This method returns the imageable height of the paper, in 1/72nd's of + * an inch. + * + * @return The imageable height of the paper. + */ +public double getImageableHeight() +{ + return(paper.getImageableHeight()); +} + +/*************************************************************************/ + +/** + * Returns a copy of the <code>paper</code> object being used for this + * page format. + * + * @return A copy of the <code>Paper</code> object for this format. + */ +public Paper +getPaper() +{ + return((Paper)paper.clone()); +} + +/*************************************************************************/ + +/** + * Sets the <code>Paper</code> object to be used by this page format. + * + * @param paper The new <code>Paper</code> object for this page format. + */ +public void +setPaper(Paper paper) +{ + this.paper = paper; +} + +/*************************************************************************/ + +/** + * This method returns the current page orientation. The value returned + * will be one of the page orientation constants from this class. + * + * @return The current page orientation. + */ +public int +getOrientation() +{ + return(orientation); +} + +/*************************************************************************/ + +/** + * This method sets the page orientation for this format to the + * specified value. It must be one of the page orientation constants + * from this class or an exception will be thrown. + * + * @param orientation The new page orientation. + * + * @exception IllegalArgumentException If the specified page orientation + * value is not one of the constants from this class. + */ +public void +setOrientation(int orientation) throws IllegalArgumentException +{ + if ((orientation != PORTRAIT) && + (orientation != LANDSCAPE) && + (orientation != REVERSE_LANDSCAPE)) + throw new IllegalArgumentException("Bad page orientation value: " + + orientation); + + this.orientation = orientation; +} + +/*************************************************************************/ + +/** + * This method returns a matrix used for transforming user space + * coordinates to page coordinates. The value returned will be six + * doubles as described in <code>java.awt.geom.AffineTransform</code>. + * + * @return The transformation matrix for this page format. + */ +public double[] +getMatrix() +{ + throw new RuntimeException("Not implemented since I don't know what to do"); +} + +/*************************************************************************/ + +/** + * This method returns a copy of this object. + * + * @return A copy of this object. + */ +public Object +clone() +{ + try + { + return(super.clone()); + } + catch(CloneNotSupportedException e) + { + return(null); + } +} + +} // class PageFormat + diff --git a/libjava/classpath/java/awt/print/Pageable.java b/libjava/classpath/java/awt/print/Pageable.java new file mode 100644 index 00000000000..12fa542a8c6 --- /dev/null +++ b/libjava/classpath/java/awt/print/Pageable.java @@ -0,0 +1,113 @@ +/* Pageable.java -- Pages to be printed + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This interface represents pages that are to be printed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Pageable +{ + +/* + * Static Variables + */ + +/** + * This constant is returned when <code>getNumberOfPages()</code> + * cannot determine the number of pages available for printing. + */ +int UNKNOWN_NUMBER_OF_PAGES = -1; + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method returns the number of pages this object contains, or + * <code>UNKNOWN_NUMBER_OF_PAGES</code> if it cannot determine the number + * of pages to be printed. + * + * @return The number of pages to be printed, or + * <code>UNKNOWN_NUMBER_OF_PAGES</code> if this is unknown. + */ +int +getNumberOfPages(); + +/*************************************************************************/ + +/** + * This method returns the <code>PageFormat</code> instance for the + * specified page. Page numbers start at zero. An exception is thrown if + * the requested page does not exist. + * + * @param pageIndex The index of the page to return the + * <code>PageFormat</code> for. + * + * @return The <code>PageFormat</code> for the requested page. + * + * @exception IndexOutOfBoundsException If the requested page number does + * not exist. + */ +PageFormat +getPageFormat(int pageIndex) throws IndexOutOfBoundsException; + +/*************************************************************************/ + +/** + * This method returns the <code>Printable</code> instance for the + * specified page. Page numbers start at zero. An exception is thrown if + * the requested page does not exist. + * + * @param pageIndex The index of the page to return the + * <code>Printable</code> for. + * + * @return The <code>Printable</code> for the requested page. + * + * @exception IndexOutOfBoundsException If the requested page number does + * not exist. + */ +Printable +getPrintable(int pageIndex) throws IndexOutOfBoundsException; + +} // interface Pageable + diff --git a/libjava/classpath/java/awt/print/Paper.java b/libjava/classpath/java/awt/print/Paper.java new file mode 100644 index 00000000000..4579da3ea3c --- /dev/null +++ b/libjava/classpath/java/awt/print/Paper.java @@ -0,0 +1,236 @@ +/* Paper.java -- Information about a paper type. + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This class describes a particular type of paper. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Paper implements Cloneable +{ + +/* + * Instance Variables + */ + +// Height of the paper +private double height; + +// Width of the paper +private double width; + +// Upper left imageable X coordinate +private double imageableX; + +// Upper left imageable Y coordinate +private double imageableY; + +// Imageable width of the page +private double imageableWidth; + +// Imageable height of the page +private double imageableHeight; + +/*************************************************************************/ + +/* + * Constructor + */ + +/** + * This method creates a letter sized paper with one inch margins + */ +public +Paper() +{ + width = 8.5 * 72; + height = 11 * 72; + imageableX = 72; + imageableY = 72; + imageableWidth = width - (2 * 72); + imageableHeight = height - (2 * 72); +} + +/*************************************************************************/ + +/** + * This method returns the height of the paper in 1/72nds of an inch. + * + * @return The height of the paper in 1/72nds of an inch. + */ +public double +getHeight() +{ + return(height); +} + +/*************************************************************************/ + +/** + * Returns the width of the paper in 1/72nds of an inch. + * + * @return The width of the paper in 1/72nds of an inch. + */ +public double +getWidth() +{ + return(width); +} + +/*************************************************************************/ + +/** + * This method returns the X coordinate of the upper left hand corner + * of the imageable area of the paper. + * + * @return The X coordinate of the upper left hand corner of the imageable + * area of the paper. + */ +public double +getImageableX() +{ + return(imageableX); +} + +/*************************************************************************/ + +/** + * This method returns the Y coordinate of the upper left hand corner + * of the imageable area of the paper. + * + * @return The Y coordinate of the upper left hand corner of the imageable + * area of the paper. + */ +public double +getImageableY() +{ + return(imageableY); +} + +/*************************************************************************/ + +/** + * Returns the width of the imageable area of the paper. + * + * @return The width of the imageable area of the paper. + */ +public double +getImageableWidth() +{ + return(imageableWidth); +} + +/*************************************************************************/ + +/** + * Returns the height of the imageable area of the paper. + * + * @return The height of the imageable area of the paper. + */ +public double +getImageableHeight() +{ + return(imageableHeight); +} + +/*************************************************************************/ + +/** + * This method sets the size of the paper to the specified width and + * height, which are specified in 1/72nds of an inch. + * + * @param width The width of the paper in 1/72nds of an inch. + * @param height The height of the paper in 1/72nds of an inch. + */ +public void +setSize(double width, double height) +{ + this.width = width; + this.height = height; +} + +/*************************************************************************/ + +/** + * This method sets the imageable area of the paper by specifying the + * coordinates of the upper left hand corner of that area, and its + * length and height. All values are in 1/72nds of an inch. + * + * @param imageableX The X coordinate of the upper left hand corner of + * the imageable area, in 1/72nds of an inch. + * @param imageableY The Y coordinate of the upper left hand corner of + * the imageable area, in 1/72nds of an inch. + * @param imageableWidth The width of the imageable area of the paper, + * in 1/72nds of an inch. + * @param imageableHeight The heigth of the imageable area of the paper, + * in 1/72nds of an inch. + */ +public void +setImageableArea(double imageableX, double imageableY, + double imageableWidth, double imageableHeight) +{ + this.imageableX = imageableX; + this.imageableY = imageableY; + this.imageableWidth = imageableWidth; + this.imageableHeight = imageableHeight; +} + +/*************************************************************************/ + +/** + * This method creates a copy of this object. + * + * @return A copy of this object. + */ +public Object +clone() +{ + try + { + return(super.clone()); + } + catch(CloneNotSupportedException e) + { + return(null); + } +} + +} // class Paper + diff --git a/libjava/classpath/java/awt/print/Printable.java b/libjava/classpath/java/awt/print/Printable.java new file mode 100644 index 00000000000..775167e669d --- /dev/null +++ b/libjava/classpath/java/awt/print/Printable.java @@ -0,0 +1,80 @@ +/* Printable.java -- Renders a page to the print device + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.print; + +import java.awt.Graphics; + + +/** + * This interface provides a mechanism for the actual printing of pages to the + * printer. The object implementing this interface performs the page + * rendering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Printable +{ + /** + * This value is returned by the <code>print()</code> method to indicate + * that the requested page exists and has been printed. + */ + int PAGE_EXISTS = 0; + + /** + * This value is returned by the <code>print()</code> method to indicate + * that the requested page number does not exist. + */ + int NO_SUCH_PAGE = 1; + + /** + * This method prints the specified page to the specified graphics + * context in the specified format. The pages are numbered starting + * from zero. + * + * @param graphics The graphics context to render the pages on. + * @param format The format in which to print the page. + * @param page_number The page number to print, where numbers start at zero. + * + * @return <code>PAGE_EXISTS</code> if the requested page exists and was + * successfully printed, <code>NO_SUCH_PAGE</code> otherwise. + * + * @exception PrinterException If an error occurs during printing. + */ + int print(Graphics graphics, PageFormat format, int page_number) + throws PrinterException; +} diff --git a/libjava/classpath/java/awt/print/PrinterAbortException.java b/libjava/classpath/java/awt/print/PrinterAbortException.java new file mode 100644 index 00000000000..4580630790d --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterAbortException.java @@ -0,0 +1,71 @@ +/* PrinterAbortException.java -- Indicates the print job was aborted + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This exception is thrown when the print job is aborted, either by the + * user or by the application. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class PrinterAbortException extends PrinterException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4725169026278854136L; + + /** + * Create a new instance with no detailed error message. + */ + public PrinterAbortException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public PrinterAbortException(String message) + { + super(message); + } +} // class PrinterAbortException diff --git a/libjava/classpath/java/awt/print/PrinterException.java b/libjava/classpath/java/awt/print/PrinterException.java new file mode 100644 index 00000000000..c105f549de4 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterException.java @@ -0,0 +1,71 @@ +/* PrinterException.java -- generic problem in the printing subsystem + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This is the generic toplevel exception for printing errors. Subclasses + * provide more detailed descriptions of the problem. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class PrinterException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -3757589981158265819L; + + /** + * Create a new instance with no detailed error message. + */ + public PrinterException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public PrinterException(String message) + { + super(message); + } +} // class PrinterException diff --git a/libjava/classpath/java/awt/print/PrinterGraphics.java b/libjava/classpath/java/awt/print/PrinterGraphics.java new file mode 100644 index 00000000000..5ca64190424 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterGraphics.java @@ -0,0 +1,61 @@ +/* PrinterGraphics.java -- Hook to return print job controller. + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This interface is implemented by the <code>Graphics</code> instance + * that is used for rendering pages. It provides a hook to return the + * object that is controlling the print job. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface PrinterGraphics +{ + +/** + * This method returns the instance of <code>PrinterJob</code> that is + * controlling this print job. + * + * @return The <code>PrinterJob</code> that is controlling this print job. + */ +PrinterJob +getPrinterJob(); + +} // interface PrinterGraphics + diff --git a/libjava/classpath/java/awt/print/PrinterIOException.java b/libjava/classpath/java/awt/print/PrinterIOException.java new file mode 100644 index 00000000000..c646acdec3a --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterIOException.java @@ -0,0 +1,98 @@ +/* PrinterIOException.java -- The print job encountered an I/O error + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import java.io.IOException; + +/** + * This exception is thrown when the print job encounters an I/O problem + * of some kind. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class PrinterIOException extends PrinterException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 5850870712125932846L; + + /** + * The exception that caused this (duplicates Throwable). + * + * @serial the I/O exception that terminated the job + */ + private final IOException mException; + + /** + * Initializes a new instance with the given cause. + * + * @param mException the cause + */ + public PrinterIOException(IOException mException) + { + super(mException == null ? null : mException.toString()); + initCause(mException); + this.mException = mException; + } + + /** + * Gets the underlying <code>IOException</code> that caused this exception. + * This legacy method has been replaced by {@link #getCause()}. + * + * @return the cause + */ + public IOException getIOException() + { + return mException; + } + + /** + * Gets the cause. + * + * @return the cause + */ + public Throwable getCause() + { + return mException; + } +} // class PrinterIOException + diff --git a/libjava/classpath/java/awt/print/PrinterJob.java b/libjava/classpath/java/awt/print/PrinterJob.java new file mode 100644 index 00000000000..e61ab61bc77 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterJob.java @@ -0,0 +1,299 @@ +/* PrinterJob.java -- This job is the printer control class + Copyright (C) 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import java.awt.HeadlessException; + +import javax.print.PrintService; +import javax.print.attribute.PrintRequestAttributeSet; + +/** + * This class controls printing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class PrinterJob +{ + // The print service associated with this job + private PrintService printer = null; + + /** + * Creates a new print job. + * + * @return A <code>PrinterJob</code> object for the newly created print job. + */ + public static PrinterJob getPrinterJob() + { + // FIXME: Need to fix this to load a default implementation instance. + return null; + } + + /** + * Initializes a new instance of <code>PrinterJob</code>. + */ + public PrinterJob() + { + } + + /** + * Returns the number of copies to be printed. + * + * @return The number of copies to be printed. + */ + public abstract int getCopies(); + + /** + * Sets the number of copies to be printed. + * + * @param copies The number of copies to be printed. + */ + public abstract void setCopies(int copies); + + /** + * Returns the name of the print job. + * + * @return The name of the print job. + */ + public abstract String getJobName(); + + /** + * Sets the name of the print job. + * + * @param job_name The name of the print job. + */ + public abstract void setJobName(String job_name); + + /** + * Returns the printing user name. + * + * @return The printing username. + */ + public abstract String getUserName(); + + /** + * Cancels an in progress print job. + */ + public abstract void cancel(); + + /** + * Tests whether or not this job has been cancelled. + * + * @return <code>true</code> if this job has been cancelled, <code>false</code> + * otherwise. + */ + public abstract boolean isCancelled(); + + /** + * Returns an instance of the default page which will have the default + * paper and orientation. + * + * @return A default instance of <code>PageFormat</code>. + */ + public PageFormat defaultPage() + { + return new PageFormat(); + } + + /** + * Clones the specified <code>PageFormat</code> object then alters the + * clone so that it represents the default page format. + * + * @param page_format The <code>PageFormat</code> to clone. + * + * @return A new default page format. + */ + public abstract PageFormat defaultPage(PageFormat page_format); + + /** + * Displays a dialog box to the user which allows the page format + * attributes to be modified. + * + * @param page_format The <code>PageFormat</code> object to modify. + * + * @return The modified <code>PageFormat</code>. + */ + public abstract PageFormat pageDialog(PageFormat page_format) + throws HeadlessException; + + /** + * @since 1.4 + */ + public PageFormat pageDialog(PrintRequestAttributeSet attributes) + throws HeadlessException + { + // FIXME: Implement this for real. + return pageDialog((PageFormat) null); + } + + /** + * Prints the pages. + */ + public abstract void print () throws PrinterException; + + /** + * Prints the page with given attributes. + */ + public abstract void print (PrintRequestAttributeSet attributes) + throws PrinterException; + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return <code>false</code> if the user cancels the dialog box, + * <code>true</code> otherwise. + */ + public abstract boolean printDialog() + throws HeadlessException; + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return <code>false</code> if the user cancels the dialog box, + * <code>true</code> otherwise. + */ + public boolean printDialog(PrintRequestAttributeSet attributes) + throws HeadlessException + { + // FIXME: Implement this for real. + return printDialog(); + } + + /** + * This sets the pages that are to be printed. + * + * @param pageable The pages to be printed, which may not be <code>null</code>. + */ + public abstract void setPageable(Pageable pageable); + + /** + * Sets this specified <code>Printable</code> as the one to use for + * rendering the pages on the print device. + * + * @param printable The <code>Printable</code> for the print job. + */ + public abstract void setPrintable(Printable printable); + + /** + * Sets the <code>Printable</code> and the page format for the pages + * to be printed. + * + * @param printable The <code>Printable</code> for the print job. + * @param page_format The <code>PageFormat</code> for the print job. + */ + public abstract void setPrintable(Printable printable, PageFormat page_format); + + /** + * Makes any alterations to the specified <code>PageFormat</code> + * necessary to make it work with the current printer. The alterations + * are made to a clone of the input object, which is then returned. + * + * @param page_format The <code>PageFormat</code> to validate. + * + * @return The validated <code>PageFormat</code>. + */ + public abstract PageFormat validatePage(PageFormat page_format); + + /** + * Find and return 2D image print services. + * + * This is the same as calling PrintServiceLookup.lookupPrintServices() + * with Pageable service-specified DocFlavor. + * @return Array of PrintService objects, could be empty. + * @since 1.4 + */ + public static PrintService[] lookupPrintServices() + { + return new PrintService[0]; + // FIXME: + // Enable this when javax.print has this implemented. +// return PrintServiceLookup.lookupPrintServices( +// new DocFlavor("application/x-java-jvm-local-objectref", +// "java.awt.print.Pageable"), +// null); + } + + /** + * Find and return 2D image stream print services. + * + * This is the same as calling + * StreamPrintServiceFactory.lookupStreamPrintServices() + * with Pageable service-specified DocFlavor. + * @param mimeType The output format mime type, or null for any type. + * @return Array of stream print services, could be empty. + * @since 1.4 + */ + // FIXME: + // Enable when javax.print has StreamPrintServiceFactory +// public static StreamPrintServiceFactory[] lookupStreamPrintServices(String mimeType) +// { +// return StreamPrintServiceFactory.lookupStreamServiceFactories( +// new DocFlavor("application/x-java-jvm-local-objectref", +// "java.awt.print.Pageable"), +// mimeType); +// } + + /** + * Return the printer for this job. If print services aren't supported by + * the subclass, returns null. + * + * @return The associated PrintService. + * @since 1.4 + */ + public PrintService getPrintService() + { + return null; + } + + /** + * Change the printer for this print job to service. Subclasses that + * support setting the print service override this method. Throws + * PrinterException when the class doesn't support setting the printer, + * the service doesn't support Pageable or Printable interfaces for 2D + * print output. + * @param service The new printer to use. + * @throws PrinterException if service is not valid. + */ + public void setPrintService(PrintService service) + throws PrinterException + { + throw new PrinterException(); + } +} diff --git a/libjava/classpath/java/awt/print/package.html b/libjava/classpath/java/awt/print/package.html new file mode 100644 index 00000000000..50abcbfad63 --- /dev/null +++ b/libjava/classpath/java/awt/print/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.awt.print package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.awt.print</title></head> + +<body> +<p>Classes for printer jobs, pages, paper sizes, graphics and formats.</p> + +</body> +</html> diff --git a/libjava/classpath/java/beans/AppletInitializer.java b/libjava/classpath/java/beans/AppletInitializer.java new file mode 100644 index 00000000000..69dc2cae50e --- /dev/null +++ b/libjava/classpath/java/beans/AppletInitializer.java @@ -0,0 +1,61 @@ +/* java.beans.AppletInitializer + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; + + +/** This interface is a mechanism for the initialization of a Java + * Bean that is also an Applet. It is used by + * <code>Beans.instantiate()</code>. + * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.2 + */ +public interface AppletInitializer +{ + /** Activate the applet. */ + void activate (Applet applet); + + /** This method will be called by <code>Beans.instantiate()</code> + * to associated the new Applet with its AppletContext, AppletStub, + * and Container. + */ + void initialize (Applet applet, BeanContext context); +} diff --git a/libjava/classpath/java/beans/BeanDescriptor.java b/libjava/classpath/java/beans/BeanDescriptor.java new file mode 100644 index 00000000000..21227b2fee0 --- /dev/null +++ b/libjava/classpath/java/beans/BeanDescriptor.java @@ -0,0 +1,89 @@ +/* java.beans.BeanDescriptor + Copyright (C) 1998, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanDescriptor describes general information about a Bean, plus + ** stores the Bean's Class and it's customizer's Class.<P> + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 31 May 1998 + **/ + +public class BeanDescriptor extends FeatureDescriptor { + Class beanClass; + Class customizerClass; + + /** Create a new BeanDescriptor with the given beanClass and + ** no customizer class. + ** @param beanClass the class of the Bean. + **/ + public BeanDescriptor(Class beanClass) { + this(beanClass,null); + } + + /** Create a new BeanDescriptor with the given bean class and + ** customizer class. + ** @param beanClass the class of the Bean. + ** @param customizerClass the class of the Bean's Customizer. + **/ + public BeanDescriptor(Class beanClass, Class customizerClass) { + this.beanClass = beanClass; + this.customizerClass = customizerClass; + + // Set the FeatureDescriptor programmatic name. + String name = beanClass.getName(); + int lastInd = name.lastIndexOf('.'); + if (lastInd != -1) + name = name.substring(lastInd + 1); + + setName(name); + } + + /** Get the Bean's class. **/ + public Class getBeanClass() { + return beanClass; + } + + /** Get the Bean's customizer's class. **/ + public Class getCustomizerClass() { + return customizerClass; + } +} diff --git a/libjava/classpath/java/beans/BeanInfo.java b/libjava/classpath/java/beans/BeanInfo.java new file mode 100644 index 00000000000..525500a389c --- /dev/null +++ b/libjava/classpath/java/beans/BeanInfo.java @@ -0,0 +1,181 @@ +/* java.beans.BeanInfo + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanInfo can be implemented in order to provide explicit information to the Introspector. + ** + ** When you write a BeanInfo class, you implement this interface + ** and provide explicit information by returning a non-null + ** value from the appropriate method. If you wish the + ** Introspector to determine certain information in the normal + ** way, just return null (or in the case of int methods, return + ** -1). There is a class called SimpleBeanInfo which returns + ** null from all methods, which you may extend and only + ** override the methods you wish to override.<P> + ** + ** When you have written the class, give it the name + ** <CODE><Bean Class Name>BeanInfo</CODE> and place it in + ** the same package as the Bean, or in the bean info search path + ** (see Introspector for information on search paths).<P> + ** + ** A simple note about the way the Introspector interacts with + ** BeanInfo. Introspectors look at a Bean class and determine + ** if there is a BeanInfo class with it. If there is not a + ** BeanInfo class, it will behave as if the BeanInfo class + ** provided was a SimpleBeanInfo class (i.e. it will determine + ** all information automatically).<P>If there is a BeanInfo + ** class, then any methods that do *not* return null are + ** regarded as providing definitive information about the class + ** and all of its superclasses for those information types. + ** Even if a parent BeanInfo class explicitly returns that + ** information, it will not be used. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 28 Jul 1998 + **/ + +public interface BeanInfo { + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_16x16 = 1; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_32x32 = 2; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_16x16 = 3; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_32x32 = 4; + + /** Get the general description of this Bean type. + ** @return the BeanDescriptor for the Bean, or null if + ** the BeanDescriptor should be obtained by + ** Introspection. + **/ + BeanDescriptor getBeanDescriptor(); + + /** Get the events this Bean type fires. + ** @return the EventDescriptors representing events this + ** Bean fires. Returns <CODE>null</CODE> if the + ** events are to be acquired by Introspection. + **/ + EventSetDescriptor[] getEventSetDescriptors(); + + /** Get the "default" event, basically the one a RAD tool + ** user is most likely to select. + ** @return the index into the getEventSetDescriptors() + ** that the user is most likely to use. Returns + ** <CODE>-1</CODE> if there is no default event. + **/ + int getDefaultEventIndex(); + + /** Get the properties (get/set method pairs) this Bean + ** type supports. + ** @return the PropertyDescriptors representing the + ** properties this Bean type supports. + ** Returns <CODE>null</CODE> if the properties + ** are to be obtained by Introspection. + **/ + PropertyDescriptor[] getPropertyDescriptors(); + + /** Get the "default" property, basically the one a RAD + ** tool user is most likely to select. + ** @return the index into the getPropertyDescriptors() + ** that the user is most likely to use. Returns + ** <CODE>-1</CODE> if there is no default event. + **/ + int getDefaultPropertyIndex(); + + /** Get the methods this Bean type supports. + ** @return the MethodDescriptors representing the + ** methods this Bean type supports. Returns + ** <CODE>null</CODE> if the methods are to be + ** obtained by Introspection. + **/ + MethodDescriptor[] getMethodDescriptors(); + + /** Get additional BeanInfos representing this Bean. + ** In this version of JavaBeans, this method is used so + ** that space and time can be saved by reading a BeanInfo + ** for each class in the hierarchy (super, super(super), + ** and so on).<P> + ** + ** The order of precedence when two pieces of BeanInfo + ** conflict (such as two PropertyDescriptors that have + ** the same name), in order from highest precedence to + ** lowest, is: + ** <OL> + ** <LI>This BeanInfo object.</LI> + ** <LI><CODE>getAdditionalBeanInfo()[getAdditionalBeanInfo().length]</CODE></LI> + ** <LI> ... </LI> + ** <LI><CODE>getAdditionalBeanInfo()[1]</CODE></LI> + ** <LI><CODE>getAdditionalBeanInfo()[0]</CODE></LI> + ** </OL><P> + ** + ** <STRONG>Spec Note:</STRONG> It is possible that + ** returning <CODE>null</CODE> from this method could + ** stop Introspection in its tracks, but it is unclear + ** from the spec whether this is the case. + ** + ** @return additional BeanInfos representing this Bean. + ** <CODE>null</CODE> may be returned (see Spec + ** Note, above). + **/ + BeanInfo[] getAdditionalBeanInfo(); + + /** Get a visual icon for this Bean. + ** A Bean does not have to support icons, and if it does + ** support icons, it does not have to support every single + ** type. Sun recommends that if you only support one + ** type, you support 16x16 color. Sun also notes that you + ** should try to use a type (like GIF) that allows for + ** transparent pixels, so that the background of the RAD + ** tool can show through.<P> + ** + ** <STRONG>Spec Note:</STRONG> If you do not support the + ** type of icon that is being asked for, but you do + ** support another type, it is unclear whether you should + ** return the other type or not. I would presume not. + ** + ** @param iconType the type of icon to get (see the + ** ICON_* constants in this class). + ** @return the icon, or null if that type of icon is + ** unsupported by this Bean. + **/ + java.awt.Image getIcon(int iconType); +} diff --git a/libjava/classpath/java/beans/Beans.java b/libjava/classpath/java/beans/Beans.java new file mode 100644 index 00000000000..ffcb83fc1f3 --- /dev/null +++ b/libjava/classpath/java/beans/Beans.java @@ -0,0 +1,368 @@ +/* java.beans.Beans + Copyright (C) 1998, 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import gnu.java.beans.DummyAppletStub; +import gnu.java.io.ClassLoaderObjectInputStream; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.URL; + +/** + * <code>Beans</code> provides some helper methods that allow the basic + * operations of Bean-ness. + * + * @author John Keiser + * @author Robert Schuster + * + * @since 1.1 + * @status updated to 1.4 + * + */ +public class Beans +{ + static boolean designTime = false; + static boolean guiAvailable = true; + + /** + * Once again, we have a java.beans class with only + * static methods that can be instantiated. When + * will the madness end? :) + */ + public Beans() + { + // Does intentionally nothing here. + } + + /** Creates a bean. + * <p>This is a convenience method that calls <code>instantiate(cl, beanName, null, null)</code>.</p> + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate(ClassLoader cl, String beanName) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, null, null); + } + + /** Creates a bean. + * + * <p>This is a convenience method that calls <code>instantiate(cl, beanName, beanContext, null)</code>.</p> + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, beanContext, null); + } + + /** Instantiates a bean according to Beans 1.0. + * + * <p>In Beans 1.0 the instantiation scheme is as follows:</p> + * <p>The name should be dot-separated (e.g "place.for.beans.myBean") and indicate either a + * serialized object or a class name. In the first case all dots in the name are replaced with + * slashes ('/') and ".ser" is appended ("place.for.beans.myBean" becomes "place/for/beans/myBean.ser"). + * The bean is then loaded as an application or system resource depending on whether a + * <code>ClassLoader</code> was provided.</p> + * + * <p>If no such resource exists or if it contains no bean the name is interpreted as a class name of + * which an instance is then created.</p> + * + * <p>If a <code>BeanContext</code> instance is available the created bean is added to it.</p> + * + * <p>If the created Bean is an <code>Applet</code> or subclass and an <code>AppletInitializer</code> + * instance is available the applet is initialized and afterwards activated using the initializer. Additionally + * every instantiated <code>Applet</code> bean is initialized using the {@link Applet.init} method. + * Furthermore every applet gets a default <code>AppletStub</code>. The <code>Applet</code>'s + * document base is the location of the ".ser" file if it was deserialized or the location of its class + * file if it was instantiated.</p> + * + * <p>A <code>ClassNotFoundException</code> is not only thrown when a class name was unknown + * but even when the class has public no-argument constructor + * (<code>IllegalAccessException</code> is wrapped) or an exception is thrown while + * invoking such a constructor (causing exception is wrapped).</p> + * + * @param cl ClassLoader to be used or <code>null</code> for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @param initializer The AppletInitializer which is used for initializing <code>Applet</code> beans. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext, + AppletInitializer initializer) + throws IOException, ClassNotFoundException + { + Object bean = null; + URL beanLocation = null; + URL classLocation = null; + + // Converts bean name into a resource name (eg. "a.b.c" -> "a/b/c"). + String resourceName = beanName.replace('.', '/'); + + /* Tries to get an input stream of the Bean, reading it as a system resource + * if no ClassLoader is present or as an application resource if a classloader + * is given. + */ + beanLocation = + (cl == null) + ? ClassLoader.getSystemResource(resourceName + ".ser") + : cl.getResource(resourceName + ".ser"); + + // Reads the serialized Bean from the returned URL. + if (beanLocation != null) + { + // Deserializes the bean instance. + ObjectInputStream ois = + (cl == null) + ? new ObjectInputStream(beanLocation.openStream()) + : new ClassLoaderObjectInputStream( + beanLocation.openStream(), + cl); + + bean = ois.readObject(); + + /* Implementation note: The result of ObjectInputStream.readObject() + * may have been null at this point (its a valid value to deserialize) + * and we explicitly want to try instantiation in such a case + * (this is important for compatibility). + */ + } + + // Instantiates the Bean using reflective instantiation if it has not been created yet. + if (bean == null) + { + // Makes sure that the deserialization was NOT done. + beanLocation = null; + + Class beanClass; + if (cl == null) + { + beanClass = Class.forName(beanName); + classLocation = + ClassLoader.getSystemResource(resourceName + ".class"); + } + else + { + beanClass = cl.loadClass(beanName); + classLocation = cl.getResource(resourceName + ".class"); + } + + // Instantiates and optionally registers the new bean. + try + { + bean = beanClass.newInstance(); + } + catch(Exception e) { + /* Wraps all kinds of Exceptions in a ClassNotFoundException (this behavior + * matches with official >= 1.5, this was different for <=1.4) + */ + throw new ClassNotFoundException(null, e); + } + } + + /* Applet beans are treated in the following way: + * - all AppletS get a default AppletStub + * - all AppletS are initialized using the AppletInitializer instance (if it is available) + * - as every other Bean Applets are added to a BeanContext if one is available + * - each instantiated Applet is initialized using Applet.init() (this is not done for deserialized ones) + * - finally AppletS get activated using the AppletInitializerS activate-Method + * + * The order of operations is important for compatibility. + */ + Applet applet = null; + if (bean instanceof Applet) + { + // Makes a second instanceof call unneccessary (instanceof is expensive). + applet = (Applet) bean; + + /* The AppletStub's code and document base is set as follows: + * The code base is always the URL from where the class data originated + * (without the package name). + * If the Applet was deserialized the document base is the location of + * the serialized instance (usually the ".ser" file) otherwise its the URL + * from where the class data originated (usually the absolute directory + * location of the ".class" file). + */ + applet.setStub( + new DummyAppletStub( + applet + .getClass() + .getProtectionDomain() + .getCodeSource() + .getLocation(), + (beanLocation == null) ? classLocation : beanLocation)); + + // Runs the Applet's initialization using an AppletInitializer. + if (initializer != null) + { + initializer.initialize(applet, beanContext); + } + } + + // Adds the new bean to its BeanContext. + if (beanContext != null) + { + beanContext.add(bean); + } + + if (applet != null) + { + + // Initializes an instantiated (not deserialized) Applet using its own method. + if (beanLocation == null) + { + applet.init(); + } + + // Runs the Applet's activation using an AppletInitializer. + if (initializer != null) + { + initializer.activate(applet); + } + } + + return bean; + } + + /** + * Returns the Bean as a different class type. + * This should be used instead of casting to get a new + * type view of a Bean, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newClass the Class to cast it to. + * + * @return the Bean as a new view, or if the operation + * could not be performed, the Bean itself. + */ + public static Object getInstanceOf(Object bean, Class newClass) + { + return bean; + } + + /** + * Determines whether the Bean can be cast to a different + * class type. + * This should be used instead of instanceof to determine + * a Bean's castability, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newClass the Class to cast it to. + * + * @return whether the Bean can be cast to the class type + * in question. + */ + public static boolean isInstanceOf(Object bean, Class newBeanClass) + { + return newBeanClass.isInstance(bean); + } + + /** + * Returns whether the GUI is available to use. + * <p>Defaults to true.</p> + * + * @return whether the GUI is available to use. + */ + public static boolean isGuiAvailable() + { + return guiAvailable; + } + + /** + * Returns whether it is design time. Design time means + * we are in a RAD tool. + * <p>Defaults to false.</p> + * + * @return whether it is design time. + */ + public static boolean isDesignTime() + { + return designTime; + } + + /** + * Sets whether the GUI is available to use. + * + * @param guiAvailable whether the GUI is available to use. + */ + public static void setGuiAvailable(boolean guiAvailable) + throws SecurityException + { + Beans.guiAvailable = guiAvailable; + } + + /** + * Sets whether it is design time. Design time means we + * are in a RAD tool. + * + * @param designTime whether it is design time. + */ + public static void setDesignTime(boolean designTime) + throws SecurityException + { + Beans.designTime = designTime; + } + +} diff --git a/libjava/classpath/java/beans/Customizer.java b/libjava/classpath/java/beans/Customizer.java new file mode 100644 index 00000000000..b36c89f8846 --- /dev/null +++ b/libjava/classpath/java/beans/Customizer.java @@ -0,0 +1,86 @@ +/* java.beans.Customizer + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** You may explicitly provide a Customizer for your Bean + ** class, which allows you complete control of the editing + ** of the Bean.<P> + ** + ** A Customizer is meant to be embedded in an RAD tool, + ** and thus must be a descendant of <CODE>java.awt.Component</CODE>.<P> + ** + ** It must also have a constructor with no arguments. This + ** is the constructor that will be called by the RAD tool to + ** instantiate the Customizer.<P> + ** + ** Over its lifetime, an instance of a Customizer will only + ** customize one single Bean. A new instance of the + ** Customizer will be instantiated to edit any other Beans.<P> + ** + ** The Customizer is responsible for notifying its + ** PropertyChangeListeners of any changes that are made, + ** according to the rules of PropertyChangeListeners (i.e. + ** notify the clients <EM>after</EM> the property has + ** changed). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanDescriptor.getCustomizerClass() + **/ + +public interface Customizer { + /** Set the object to Customize. This will always be a + ** Bean that had a BeanDescriptor indicating this + ** Customizer. + ** @param bean the Bean to customize. + **/ + void setObject(Object bean); + + /** Add a PropertyChangeListener. + ** @param l the PropertyChangeListener to add. + **/ + void addPropertyChangeListener(PropertyChangeListener l); + + /** Remove a PropertyChangeListener. + ** @param l the PropertyChangeListener to remove. + **/ + void removePropertyChangeListener(PropertyChangeListener l); +} diff --git a/libjava/classpath/java/beans/DesignMode.java b/libjava/classpath/java/beans/DesignMode.java new file mode 100644 index 00000000000..39805d50c18 --- /dev/null +++ b/libjava/classpath/java/beans/DesignMode.java @@ -0,0 +1,93 @@ +/* java.beans.DesignMode + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * <code>BeanContextChild</code> implementors implement this to get information about whether they are in a design time or runtime environment. + * The reason this is restricted to <code>BeanContextChild</code>ren is that + * only things in the <code>BeanContext</code> hierarchy are given this + * information in the first place. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextChild + */ + +public interface DesignMode { + /** + * Use this name when firing <code>PropertyChangeEvent</code>s from your Bean. + * @fixme Check whether PROPERTYNAME is set to same value as Sun. + */ + String PROPERTYNAME = "designTime"; + + /** + * The environment will call this method on your + * <code>BeanContextChild</code> when it is registered in a parent + * <code>BeanContext</code> or when behavior needs to switch from + * design time to runtime behavior (or vice versa). + * <P> + * + * <code>BeanContext</code>s are required to fire + * <code>PropertyChangeEvent</code>s when properties change. + * <code>designTime</code> is a property, and therefore when you + * implement <code>setDesignTime()</code>, you need to fire a + * <code>PropertyChangeEvent</code> with the old value, the new + * value and using <code>PROPERTYNAME</code> as the property name. + * + * @param designTime the new value of design time, + * <code>true</code> if it is design time, + * <code>false</code> if it is runtime. + * + * @fixme I'm frankly not really sure whether it's the case that + * the BeanContext can <em>change</em> the status of the Bean from + * design time to runtime. But it appears that it may be so. + * + * @see java.util.PropertyChangeEvent + * @see java.beans.beancontext.BeanContext + * @see #PROPERTYNAME + */ + void setDesignTime(boolean designTime); + + /** + * This method should tell whether it is design time or runtime. + * @return <code>true</code> if design time, <code>false</code> if + * runtime. + */ + boolean isDesignTime(); +} diff --git a/libjava/classpath/java/beans/EventHandler.java b/libjava/classpath/java/beans/EventHandler.java new file mode 100644 index 00000000000..9c85893e0f3 --- /dev/null +++ b/libjava/classpath/java/beans/EventHandler.java @@ -0,0 +1,606 @@ +/* java.beans.EventHandler + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * <p>EventHandler forms a bridge between dynamically created listeners and + * arbitrary properties and methods.</p> + * + * <p>You can use this class to easily create listener implementations for + * some basic interactions between an event source and its target. Using + * the three static methods named <code>create</code> you can create + * these listener implementations.</p> + * + * <p>See the documentation of each method for usage examples.</p> + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (thebohemian@gmx.net) + * @since 1.4 + */ +public class EventHandler implements InvocationHandler +{ + // The name of the method that will be implemented. If null, any method. + private String listenerMethod; + + // The object to call action on. + private Object target; + + // The name of the method or property setter in target. + private String action; + + // The property to extract from an event passed to listenerMethod. + private String property; + + // The target objects Class. + private Class targetClass; + + // String class doesn't already have a capitalize routine. + private String capitalize(String s) + { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * Creates a new <code>EventHandler</code> instance. + * + * <p>Typical creation is done with the create method, not by knewing an + * EventHandler.</p> + * + * <p>This constructs an EventHandler that will connect the method + * listenerMethodName to target.action, extracting eventPropertyName from + * the first argument of listenerMethodName. and sending it to action.</p> + * + * <p>Throws a <code>NullPointerException</code> if the <code>target</code> + * argument is <code>null</code>. + * + * @param target Object that will perform the action. + * @param action A property or method of the target. + * @param eventPropertyName A readable property of the inbound event. + * @param listenerMethodName The listener method name triggering the action. + */ + public EventHandler(Object target, String action, String eventPropertyName, + String listenerMethodName) + { + this.target = target; + + // Retrieving the class is done for two reasons: + // 1) The class object is needed very frequently in the invoke() method. + // 2) The constructor should throw a NullPointerException if target is null. + targetClass = target.getClass(); + + this.action = action; // Turn this into a method or do we wait till + // runtime + property = eventPropertyName; + listenerMethod = listenerMethodName; + } + + /** + * Returns the event property name. + */ + public String getEventPropertyName() + { + return property; + } + + /** + * Returns the listener's method name. + */ + public String getListenerMethodName() + { + return listenerMethod; + } + + /** + * Returns the target object. + */ + public Object getTarget() + { + return target; + } + + /** + * Returns the action method name. + */ + public String getAction() + { + return action; + } + + // Fetch a qualified property like a.b.c from object o. The properties can + // be boolean isProp or object getProp properties. + // + // Returns a length 2 array with the first entry containing the value + // extracted from the property, and the second entry contains the class of + // the method return type. + // + // We play this game because if the method returns a native type, the return + // value will be a wrapper. If we then take the type of the wrapper and use + // it to locate the action method that takes the native type, it won't match. + private Object[] getProperty(Object o, String prop) + { + // Isolate the first property name from a.b.c. + int pos; + String rest = null; + if ((pos = prop.indexOf('.')) != -1) + { + rest = prop.substring(pos + 1); + prop = prop.substring(0, pos); + } + + // Find a method named getProp. It could be isProp instead. + Method getter; + try + { + // Look for boolean property getter isProperty + getter = o.getClass().getMethod("is" + capitalize(prop), + null); + } + catch (NoSuchMethodException nsme1) + { + try { + // Look for regular property getter getProperty + getter = o.getClass().getMethod("get" + capitalize(prop), + null); + } catch(NoSuchMethodException nsme2) { + try { + // Finally look for a method of the name prop + getter = o.getClass().getMethod(prop, null); + } catch(NoSuchMethodException nsme3) { + // Ok, give up with an intelligent hint for the user. + throw new RuntimeException("Method not called: Could not find a property or method '" + prop + + "' in " + o.getClass() + " while following the property argument '" + property + "'."); + } + } + } + try { + Object val = getter.invoke(o, null); + + if (rest != null) + return getProperty(val, rest); + + return new Object[] {val, getter.getReturnType()}; + } catch(InvocationTargetException ite) { + throw new RuntimeException("Method not called: Property or method '" + prop + "' has thrown an exception.", ite); + } catch(IllegalAccessException iae) { + // This cannot happen because we looked up method with Class.getMethod() + // which returns public methods only. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * Invokes the <code>EventHandler</code>. + * + * <p>This method is normally called by the listener's proxy implementation.</p> + * + * @param proxy The listener interface that is implemented using + * the proxy mechanism. + * @param method The method that was called on the proxy instance. + * @param arguments The arguments which where given to the method. + * @throws Throwable <code>NoSuchMethodException</code> is thrown when the EventHandler's + * action method or property cannot be found. + */ + public Object invoke(Object proxy, Method method, Object[] arguments) + { + try { + // The method instance of the target object. We have to find out which + // one we have to invoke. + Method actionMethod = null; + + // Listener methods that weren't specified are ignored. If listenerMethod + // is null, then all listener methods are processed. + if (listenerMethod != null && !method.getName().equals(listenerMethod)) + return null; + + // If a property is defined we definitely need a valid object at + // arguments[0] that can be used to retrieve a value to which the + // property of the target gets set. + if(property != null) { + // Extracts the argument. We will let it fail with a NullPointerException + // the caller used a listener method that has no arguments. + Object event = arguments[0]; + + // Obtains the property XXX propertyType keeps showing up null - why? + // because the object inside getProperty changes, but the ref variable + // can't change this way, dolt! need a better way to get both values out + // - need method and object to do the invoke and get return type + Object v[] = getProperty(event, property); + Object[] args = new Object[] { v[0] }; + + // Changes the class array that controls which method signature we are going + // to look up in the target object. + Class[] argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + // Look for a property setter for action. + actionMethod = targetClass.getMethod("set" + capitalize(action), argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + // If action as property didn't work, try as method later. + } + + argTypes[0] = nextClass(argTypes[0]); + } + + // We could not find a suitable setter method. Now we try again interpreting + // action as the method name itself. + // Since we probably have changed the block local argTypes array + // we need to rebuild it. + argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + actionMethod = targetClass.getMethod(action, argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + } + + argTypes[0] = nextClass(argTypes[0]); + } + + throw new RuntimeException("Method not called: Could not find a public method named '" + + action + "' in target " + targetClass + " which takes a '" + + v[1] + "' argument or a property of this type."); + } + + // If property was null we will search for a no-argument method here. + // Note: The ordering of method lookups is important because we want to prefer no-argument + // calls like the JDK does. This means if we have actionMethod() and actionMethod(Event) we will + // call the first *EVEN* if we have a valid argument for the second method. This is behavior compliant + // to the JDK. + // If actionMethod() is not available but there is a actionMethod(Event) we take this. That makes us + // more specification compliant than the JDK itself because this one will fail in such a case. + try + { + actionMethod = targetClass.getMethod(action, null); + } + catch(NoSuchMethodException nsme) + { + // Note: If we want to be really strict the specification says that a no-argument method should + // accept an EventObject (or subclass I guess). However since the official implementation is broken + // anyways, it's more flexible without the EventObject restriction and we are compatible on everything + // else this can stay this way. + if(arguments != null && arguments.length >= 1/* && arguments[0] instanceof EventObject*/) { + Class[] targetArgTypes = new Class[] { initClass(arguments[0].getClass()) }; + + while(targetArgTypes[0] != null) { + try + { + // If no property exists we expect the first element of the arguments to be + // an EventObject which is then applied to the target method. + + actionMethod = targetClass.getMethod(action, targetArgTypes); + + return actionMethod.invoke(target, new Object[] { arguments[0] }); + } + catch(NoSuchMethodException nsme2) + { + + } + + targetArgTypes[0] = nextClass(targetArgTypes[0]); + } + + } + } + + // If we do not have a Method instance at this point this means that all our tries + // failed. The JDK throws an ArrayIndexOutOfBoundsException in this case. + if(actionMethod == null) + throw new ArrayIndexOutOfBoundsException(0); + + // Invoke target.action(property) + return actionMethod.invoke(target, null); + } catch(InvocationTargetException ite) { + throw new RuntimeException(ite.getCause()); + } catch(IllegalAccessException iae) { + // Cannot happen because we always use getMethod() which returns public + // methods only. Otherwise there is something seriously broken in + // GNU Classpath. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * <p>Returns the primitive type for every wrapper class or the + * class itself if it is no wrapper class.</p> + * + * <p>This is needed because to be able to find both kinds of methods: + * One that takes a wrapper class as the first argument and one that + * accepts a primitive instead.</p> + */ + private Class initClass(Class klass) { + if(klass == Boolean.class) { + return Boolean.TYPE; + } else if(klass == Byte.class) { + return Byte.TYPE; + } else if(klass == Short.class) { + return Short.TYPE; + } else if(klass == Integer.class) { + return Integer.TYPE; + } else if(klass == Long.class) { + return Long.TYPE; + } else if(klass == Float.class) { + return Float.TYPE; + } else if(klass == Double.class) { + return Double.TYPE; + } else { + return klass; + } + } + + /** + * + * + * @param klass + * @return + */ + private Class nextClass(Class klass) { + if(klass == Boolean.TYPE) { + return Boolean.class; + } else if(klass == Byte.TYPE) { + return Byte.class; + } else if(klass == Short.TYPE) { + return Short.class; + } else if(klass == Integer.TYPE) { + return Integer.class; + } else if(klass == Long.TYPE) { + return Long.class; + } else if(klass == Float.TYPE) { + return Float.class; + } else if(klass == Double.TYPE) { + return Double.class; + } else { + return klass.getSuperclass(); + } + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>You can use such an implementation to simply call a public + * no-argument method of an arbitrary target object or to forward + * the first argument of the listener method to the target method.</p> + * + * <p>Call this method like:</p> + * <code> + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, target, "dispose")); + * </code> + * + * <p>to achieve the following behavior:</p> + * <code> + * button.addActionListener(new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * target.dispose(); + * } + * }); + * </code> + * + * <p>That means if you need a listener implementation that simply calls a + * a no-argument method on a given instance for <strong>each</strong> + * method of the listener interface.</p> + * + * <p>Note: The <code>action</code> is interpreted as a method name. If your target object + * has no no-argument method of the given name the EventHandler tries to find + * a method with the same name but which can accept the first argument of the + * listener method. Usually this will be an event object but any other object + * will be forwarded, too. Keep in mind that using a property name instead of a + * real method here is wrong and will throw an <code>ArrayIndexOutOfBoundsException</code> + * whenever one of the listener methods is called.<p/> + * + * <p>The <code>EventHandler</code> will automatically convert primitives + * to their wrapper class and vice versa. Furthermore it will call + * a target method if it accepts a superclass of the type of the + * first argument of the listener method.</p> + * + * <p>In case that the method of the target object throws an exception + * it will be wrapped in a <code>RuntimeException</code> and thrown out + * of the listener method.</p> + * + * <p>In case that the method of the target object cannot be found an + * <code>ArrayIndexOutOfBoundsException</code> will be thrown when the + * listener method is invoked.</p> + * + * <p>A call to this method is equivalent to: + * <code>create(listenerInterface, target, action, null, null)</code></p> + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @return A constructed proxy object. + */ + public static Object create(Class listenerInterface, Object target, String action) + { + return create(listenerInterface, target, action, null, null); + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>Use this method if you want to create an implementation that retrieves + * a property value from the <b>first</b> argument of the listener method + * and applies it to the target's property or method. This first argument + * of the listener is usually an event object but any other object is + * valid, too.</p> + * + * <p>You can set the value of <code>eventPropertyName</code> to "prop" + * to denote the retrieval of a property named "prop" from the event + * object. In case that no such property exists the <code>EventHandler</code> + * will try to find a method with that name.</p> + * + * <p>If you set <code>eventPropertyName</code> to a value like this "a.b.c" + * <code>EventHandler</code> will recursively evaluate the properties "a", "b" + * and "c". Again if no property can be found the <code>EventHandler</code> + * tries a method name instead. This allows mixing the names, too: "a.toString" + * will retrieve the property "a" from the event object and will then call + * the method "toString" on it.</p> + * + * <p>An exception thrown in any of these methods will provoke a + * <code>RuntimeException</code> to be thrown which contains an + * <code>InvocationTargetException</code> containing the triggering exception.</p> + * + * <p>If you set <code>eventPropertyName</code> to a non-null value the + * <code>action</code> parameter will be interpreted as a property name + * or a method name of the target object.</p> + * + * <p>Any object retrieved from the event object and applied to the + * target will converted from primitives to their wrapper class or + * vice versa or applied to a method that accepts a superclass + * of the object.</p> + * + * <p>Examples:</p> + * <p>The following code:</p><code> + * button.addActionListener( + * new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * Object o = ae.getSource().getClass().getName(); + * textField.setText((String) o); + * } + * }); + * </code> + * + * <p>Can be expressed using the <code>EventHandler</code> like this:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "text", "source.class.name"); + * <code> + * </p> + * + * <p>As said above you can specify the target as a method, too:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.class.name"); + * <code> + * </p> + * + * <p>Furthermore you can use method names in the property:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "getSource.getClass.getName"); + * <code> + * </p> + * + * <p>Finally you can mix names:</p> + * <p> + * <code>button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.getClass.name"); + * <code> + * </p> + * + * <p>A call to this method is equivalent to: + * <code>create(listenerInterface, target, action, null, null)</code> + * </p> + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @param eventPropertyName Name of property to extract from event. + * @return A constructed proxy object. + */ + public static Object create(Class listenerInterface, Object target, + String action, String eventPropertyName) + { + return create(listenerInterface, target, action, eventPropertyName, null); + } + + /** + * <p>Constructs an implementation of <code>listenerInterface</code> + * to dispatch events.</p> + * + * <p>Besides the functionality described for {@link create(Class, Object, String)} + * and {@link create(Class, Object, String, String)} this method allows you + * to filter the listener method that should have an effect. Look at these + * method's documentation for more information about the <code>EventHandler</code>'s + * usage.</p> + * + * <p>If you want to call <code>dispose</code> on a <code>JFrame</code> instance + * when the <code>WindowListener.windowClosing()</code> method was invoked use + * the following code:</p> + * <p> + * <code> + * EventHandler.create(WindowListener.class, jframeInstance, "dispose", null, "windowClosing"); + * </code> + * </p> + * + * <p>A <code>NullPointerException</code> is thrown if the <code>listenerInterface</code> + * or <code>target</code> argument are <code>null</code>. + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target method name to invoke. + * @param eventPropertyName Name of property to extract from event. + * @param listenerMethodName Listener method to implement. + * @return A constructed proxy object. + */ + public static Object create(Class listenerInterface, Object target, + String action, String eventPropertyName, + String listenerMethodName) + { + // Create EventHandler instance + EventHandler eh = new EventHandler(target, action, eventPropertyName, + listenerMethodName); + + // Create proxy object passing in the event handler + Object proxy = Proxy.newProxyInstance(listenerInterface.getClassLoader(), + new Class[] {listenerInterface}, + eh); + + return proxy; + } + +} diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java new file mode 100644 index 00000000000..8624e643476 --- /dev/null +++ b/libjava/classpath/java/beans/EventSetDescriptor.java @@ -0,0 +1,442 @@ +/* java.beans.EventSetDescriptor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.lang.ClassHelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Vector; + +/** + ** EventSetDescriptor describes the hookup between an event source + ** class and an event listener class. + ** + ** EventSets have several attributes: the listener class, the events + ** that can be fired to the listener (methods in the listener class), and + ** an add and remove listener method from the event firer's class.<P> + ** + ** The methods have these constraints on them:<P> + ** <UL> + ** <LI>event firing methods: must have <CODE>void</CODE> return value. Any + ** parameters and exceptions are allowed. May be public, protected or + ** package-protected. (Don't ask me why that is, I'm just following the spec. + ** The only place it is even mentioned is in the Java Beans white paper, and + ** there it is only implied.)</LI> + ** <LI>add listener method: must have <CODE>void</CODE> return value. Must + ** take exactly one argument, of the listener class's type. May fire either + ** zero exceptions, or one exception of type <CODE>java.util.TooManyListenersException</CODE>. + ** Must be public.</LI> + ** <LI>remove listener method: must have <CODE>void</CODE> return value. + ** Must take exactly one argument, of the listener class's type. May not + ** fire any exceptions. Must be public.</LI> + ** </UL> + ** + ** A final constraint is that event listener classes must extend from EventListener.<P> + ** + ** There are also various design patterns associated with some of the methods + ** of construction. Those are explained in more detail in the appropriate + ** constructors.<P> + ** + ** <STRONG>Documentation Convention:</STRONG> for proper + ** Internalization of Beans inside an RAD tool, sometimes there + ** are two names for a property or method: a programmatic, or + ** locale-independent name, which can be used anywhere, and a + ** localized, display name, for ease of use. In the + ** documentation I will specify different String values as + ** either <EM>programmatic</EM> or <EM>localized</EM> to + ** make this distinction clear. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 31 May 1998 + **/ + +public class EventSetDescriptor extends FeatureDescriptor { + private Method addListenerMethod; + private Method removeListenerMethod; + private Class listenerType; + private MethodDescriptor[] listenerMethodDescriptors; + private Method[] listenerMethods; + + private boolean unicast; + private boolean inDefaultEventSet = true; + + /** Create a new EventSetDescriptor. + ** This version of the constructor enforces the rules imposed on the methods + ** described at the top of this class, as well as searching for:<P> + ** <OL> + ** <LI>The event-firing method must be non-private with signature + ** <CODE>void <listenerMethodName>(<eventSetName>Event)</CODE> + ** (where <CODE><eventSetName></CODE> has its first character capitalized + ** by the constructor and the Event is a descendant of + ** <CODE>java.util.EventObject</CODE>) in class <CODE>listenerType</CODE> + ** (any exceptions may be thrown). + ** <B>Implementation note:</B> Note that there could conceivably be multiple + ** methods with this type of signature (example: java.util.MouseEvent vs. + ** my.very.own.MouseEvent). In this implementation, all methods fitting the + ** description will be put into the <CODE>EventSetDescriptor</CODE>, even + ** though the spec says only one should be chosen (they probably weren't thinking as + ** pathologically as I was). I don't like arbitrarily choosing things. + ** If your class has only one such signature, as most do, you'll have no problems.</LI> + ** <LI>The add and remove methods must be public and named + ** <CODE>void add<eventSetName>Listener(<listenerType>)</CODE> and + ** <CODE>void remove<eventSetName>Listener(<listenerType>)</CODE> in + ** in class <CODE>eventSourceClass</CODE>, where + ** <CODE><eventSetName></CODE> will have its first letter capitalized. + ** Standard exception rules (see class description) apply.</LI> + ** </OL> + ** @param eventSourceClass the class containing the add/remove listener methods. + ** @param eventSetName the programmatic name of the event set, generally starting + ** with a lowercase letter (i.e. fooManChu instead of FooManChu). This will be used + ** to generate the name of the event object as well as the names of the add and + ** remove methods. + ** @param listenerType the class containing the event firing method. + ** @param listenerMethodName the name of the event firing method. + ** @exception IntrospectionException if listenerType is not an EventListener, + ** or if methods are not found or are invalid. + **/ + public EventSetDescriptor(Class eventSourceClass, + String eventSetName, + Class listenerType, + String listenerMethodName) throws IntrospectionException { + setName(eventSetName); + if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { + throw new IntrospectionException("Listener type is not an EventListener."); + } + + String[] names = new String[1]; + names[0] = listenerMethodName; + + try { + eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + eventSetName.substring(1); + } catch(StringIndexOutOfBoundsException e) { + eventSetName = ""; + } + + findMethods(eventSourceClass,listenerType,names,"add"+eventSetName+"Listener","remove"+eventSetName+"Listener",eventSetName+"Event"); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if(this.removeListenerMethod.getExceptionTypes().length > 0) { + throw new IntrospectionException("Listener remove method throws exceptions."); + } + } + + /** Create a new EventSetDescriptor. + ** This form of the constructor allows you to specify the names of the methods and adds + ** no new constraints on top of the rules already described at the top of the class.<P> + ** + ** @param eventSourceClass the class containing the add and remove listener methods. + ** @param eventSetName the programmatic name of the event set, generally starting + ** with a lowercase letter (i.e. fooManChu instead of FooManChu). + ** @param listenerType the class containing the event firing methods. + ** @param listenerMethodNames the names of the even firing methods. + ** @param addListenerMethodName the name of the add listener method. + ** @param removeListenerMethodName the name of the remove listener method. + ** @exception IntrospectionException if listenerType is not an EventListener + ** or if methods are not found or are invalid. + **/ + public EventSetDescriptor(Class eventSourceClass, + String eventSetName, + Class listenerType, + String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName) throws IntrospectionException { + setName(eventSetName); + if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { + throw new IntrospectionException("Listener type is not an EventListener."); + } + + findMethods(eventSourceClass,listenerType,listenerMethodNames,addListenerMethodName,removeListenerMethodName,null); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if(this.removeListenerMethod.getExceptionTypes().length > 0) { + throw new IntrospectionException("Listener remove method throws exceptions."); + } + } + + /** Create a new EventSetDescriptor. + ** This form of constructor allows you to explicitly say which methods do what, and + ** no reflection is done by the EventSetDescriptor. The methods are, however, + ** checked to ensure that they follow the rules set forth at the top of the class. + ** @param eventSetName the programmatic name of the event set, generally starting + ** with a lowercase letter (i.e. fooManChu instead of FooManChu). + ** @param listenerType the class containing the listenerMethods. + ** @param listenerMethods the event firing methods. + ** @param addListenerMethod the add listener method. + ** @param removeListenerMethod the remove listener method. + ** @exception IntrospectionException if the listenerType is not an EventListener, + ** or any of the methods are invalid. + **/ + public EventSetDescriptor(String eventSetName, + Class listenerType, + Method[] listenerMethods, + Method addListenerMethod, + Method removeListenerMethod) throws IntrospectionException { + setName(eventSetName); + if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { + throw new IntrospectionException("Listener type is not an EventListener."); + } + + this.listenerMethods = listenerMethods; + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if(this.removeListenerMethod.getExceptionTypes().length > 0) { + throw new IntrospectionException("Listener remove method throws exceptions."); + } + } + + /** Create a new EventSetDescriptor. + ** This form of constructor allows you to explicitly say which methods do what, and + ** no reflection is done by the EventSetDescriptor. The methods are, however, + ** checked to ensure that they follow the rules set forth at the top of the class. + ** @param eventSetName the programmatic name of the event set, generally starting + ** with a lowercase letter (i.e. fooManChu instead of FooManChu). + ** @param listenerType the class containing the listenerMethods. + ** @param listenerMethodDescriptors the event firing methods. + ** @param addListenerMethod the add listener method. + ** @param removeListenerMethod the remove listener method. + ** @exception IntrospectionException if the listenerType is not an EventListener, + ** or any of the methods are invalid. + **/ + public EventSetDescriptor(String eventSetName, + Class listenerType, + MethodDescriptor[] listenerMethodDescriptors, + Method addListenerMethod, + Method removeListenerMethod) throws IntrospectionException { + setName(eventSetName); + if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { + throw new IntrospectionException("Listener type is not an EventListener."); + } + + this.listenerMethodDescriptors = listenerMethodDescriptors; + this.listenerMethods = new Method[listenerMethodDescriptors.length]; + for(int i=0;i<this.listenerMethodDescriptors.length;i++) { + this.listenerMethods[i] = this.listenerMethodDescriptors[i].getMethod(); + } + + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if(this.removeListenerMethod.getExceptionTypes().length > 0) { + throw new IntrospectionException("Listener remove method throws exceptions."); + } + } + + /** Get the class that contains the event firing methods. **/ + public Class getListenerType() { + return listenerType; + } + + /** Get the event firing methods. **/ + public Method[] getListenerMethods() { + return listenerMethods; + } + + /** Get the event firing methods as MethodDescriptors. **/ + public MethodDescriptor[] getListenerMethodDescriptors() { + if(listenerMethodDescriptors == null) { + listenerMethodDescriptors = new MethodDescriptor[listenerMethods.length]; + for(int i=0;i<listenerMethods.length;i++) { + listenerMethodDescriptors[i] = new MethodDescriptor(listenerMethods[i]); + } + } + return listenerMethodDescriptors; + } + + /** Get the add listener method. **/ + public Method getAddListenerMethod() { + return addListenerMethod; + } + + /** Get the remove listener method. **/ + public Method getRemoveListenerMethod() { + return removeListenerMethod; + } + + /** Set whether or not multiple listeners may be added. + ** @param unicast whether or not multiple listeners may be added. + **/ + public void setUnicast(boolean unicast) { + this.unicast = unicast; + } + + /** Get whether or not multiple listeners may be added. (Defaults to false.) **/ + public boolean isUnicast() { + return unicast; + } + + /** Set whether or not this is in the default event set. + ** @param inDefaultEventSet whether this is in the default event set. + **/ + public void setInDefaultEventSet(boolean inDefaultEventSet) { + this.inDefaultEventSet = inDefaultEventSet; + } + + /** Get whether or not this is in the default event set. (Defaults to true.)**/ + public boolean isInDefaultEventSet() { + return inDefaultEventSet; + } + + private void checkAddListenerUnicast() throws IntrospectionException { + Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes(); + if(addListenerExceptions.length > 1) { + throw new IntrospectionException("Listener add method throws too many exceptions."); + } else if(addListenerExceptions.length == 1 + && !java.util.TooManyListenersException.class.isAssignableFrom(addListenerExceptions[0])) { + throw new IntrospectionException("Listener add method throws too many exceptions."); + } + } + + private void checkMethods() throws IntrospectionException { + if(!addListenerMethod.getDeclaringClass().isAssignableFrom(removeListenerMethod.getDeclaringClass()) + && !removeListenerMethod.getDeclaringClass().isAssignableFrom(addListenerMethod.getDeclaringClass())) { + throw new IntrospectionException("add and remove listener methods do not come from the same class. This is bad."); + } + if(!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || addListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(addListenerMethod.getParameterTypes()[0]) + || !Modifier.isPublic(addListenerMethod.getModifiers())) { + throw new IntrospectionException("Add Listener Method invalid."); + } + if(!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || removeListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(removeListenerMethod.getParameterTypes()[0]) + || removeListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(removeListenerMethod.getModifiers())) { + throw new IntrospectionException("Remove Listener Method invalid."); + } + + for(int i=0;i<listenerMethods.length;i++) { + if(!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE) + || Modifier.isPrivate(listenerMethods[i].getModifiers())) { + throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " non-void or private."); + } + if(!listenerMethods[i].getDeclaringClass().isAssignableFrom(listenerType)) { + throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " not from class " + listenerType.getName()); + } + } + } + + private void findMethods(Class eventSourceClass, + Class listenerType, + String listenerMethodNames[], + String addListenerMethodName, + String removeListenerMethodName, + String absurdEventClassCheckName) throws IntrospectionException { + + /* Find add listener method and remove listener method. */ + Class[] listenerArgList = new Class[1]; + listenerArgList[0] = listenerType; + try { + this.addListenerMethod = eventSourceClass.getMethod(addListenerMethodName,listenerArgList); + } catch(SecurityException E) { + throw new IntrospectionException("SecurityException trying to access method " + addListenerMethodName + "."); + } catch(NoSuchMethodException E) { + throw new IntrospectionException("Could not find method " + addListenerMethodName + "."); + } + + if(this.addListenerMethod == null || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) { + throw new IntrospectionException("Add listener method does not exist, is not public, or is not void."); + } + + try { + this.removeListenerMethod = eventSourceClass.getMethod(removeListenerMethodName,listenerArgList); + } catch(SecurityException E) { + throw new IntrospectionException("SecurityException trying to access method " + removeListenerMethodName + "."); + } catch(NoSuchMethodException E) { + throw new IntrospectionException("Could not find method " + removeListenerMethodName + "."); + } + if(this.removeListenerMethod == null || !this.removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) { + throw new IntrospectionException("Remove listener method does not exist, is not public, or is not void."); + } + + /* Find the listener methods. */ + Method[] methods; + try { + methods = ClassHelper.getAllMethods(listenerType); + } catch(SecurityException E) { + throw new IntrospectionException("Security: You cannot access fields in this class."); + } + + Vector chosenMethods = new Vector(); + boolean[] listenerMethodFound = new boolean[listenerMethodNames.length]; + for(int i=0;i<methods.length;i++) { + if(Modifier.isPrivate(methods[i].getModifiers())) { + continue; + } + Method currentMethod = methods[i]; + Class retval = currentMethod.getReturnType(); + if(retval.equals(java.lang.Void.TYPE)) { + for(int j=0;j<listenerMethodNames.length;j++) { + if(currentMethod.getName().equals(listenerMethodNames[j]) + && (absurdEventClassCheckName == null + || (currentMethod.getParameterTypes().length == 1 + && ((currentMethod.getParameterTypes()[0]).getName().equals(absurdEventClassCheckName) + || (currentMethod.getParameterTypes()[0]).getName().endsWith("."+absurdEventClassCheckName) + ) + ) + ) + ) { + chosenMethods.addElement(currentMethod); + listenerMethodFound[j] = true; + } + } + } + } + + /* Make sure we found all the methods we were looking for. */ + for(int i=0;i<listenerMethodFound.length;i++) { + if(!listenerMethodFound[i]) { + throw new IntrospectionException("Could not find event method " + listenerMethodNames[i]); + } + } + + /* Now that we've chosen the listener methods we want, store them. */ + this.listenerMethods = new Method[chosenMethods.size()]; + for(int i=0;i<chosenMethods.size();i++) { + this.listenerMethods[i] = (Method)chosenMethods.elementAt(i); + } + } +} diff --git a/libjava/classpath/java/beans/ExceptionListener.java b/libjava/classpath/java/beans/ExceptionListener.java new file mode 100644 index 00000000000..0eeb8a9561f --- /dev/null +++ b/libjava/classpath/java/beans/ExceptionListener.java @@ -0,0 +1,57 @@ +/* ExceptionListener.java -- listen for recoverable internal exceptions + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * This interface allows a class to monitor internal exceptions, to try to + * recover from them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public interface ExceptionListener +{ + /** + * Fired after an exception occurs. + * + * @param e the trapped exception + */ + void exceptionThrown(Exception e); +} // interface ExceptionListener diff --git a/libjava/classpath/java/beans/Expression.java b/libjava/classpath/java/beans/Expression.java new file mode 100644 index 00000000000..20b04f1c190 --- /dev/null +++ b/libjava/classpath/java/beans/Expression.java @@ -0,0 +1,133 @@ +/* java.beans.Expression + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * class Expression + * + * An Expression captures the execution of an object method that + * returns a value. It stores an object, the method to call, and the + * arguments to pass to the method. + * + * @since 1.4 + */ +public class Expression extends Statement +{ + // This is a placeholder to indicate that value hasn't been set + // yet; + private static final Object unset = new Object(); + + // The value to return. This is equal to unset until getValue is called. + private Object value; + + + /** + * Constructor + * + * Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); However, it will never + * be executed. Instead, value will always be returned. + * + * @param value The value to return. + * @param target The object to invoke the method on. + * @param methodName The object method to invoke. + * @param arguments An array of arguments to pass to the method. + */ + public Expression(Object value, Object target, String methodName, + Object[] arguments) + { + super(target, methodName, arguments); + this.value = value; + } + + /** + * Constructor + * + * Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); + * + * @param target The object to invoke the method on. + * @param methodName The object method to invoke. + * @param arguments An array of arguments to pass to the method. + */ + public Expression(Object target, String methodName, Object[] arguments) + { + super(target, methodName, arguments); + this.value = unset; + } + + /** + * Return the result of executing the method. + * + * If the cached value has not yet been set, the method is + * executed in the same way as Statement.execute(), except that + * the value is cached, and then returned. If the value has been + * set, it is returned without executing the method again. + * + * @return the result of executing the method. + * @exception Exception if an error occurs + */ + public Object getValue() throws Exception + { + if (value == unset) + value = doExecute(); + return value; + } + + /** + * Set the cached value to be returned by getValue() + * + * @param value the value to cache and return. + */ + public void setValue(Object value) + { + this.value = value; + } + + /** + * Return a string representation of this expression. + */ + public String toString() + { + String result = super.toString(); + if (value != unset) + return value.getClass().getName() + " " + result; + return result; + } +} diff --git a/libjava/classpath/java/beans/FeatureDescriptor.java b/libjava/classpath/java/beans/FeatureDescriptor.java new file mode 100644 index 00000000000..aeb40949099 --- /dev/null +++ b/libjava/classpath/java/beans/FeatureDescriptor.java @@ -0,0 +1,232 @@ +/* java.beans.FeatureDescriptor + Copyright (C) 1998, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * FeatureDescriptor is the common superclass for all JavaBeans Descriptor + * classes. JavaBeans descriptors are abstract descriptors of properties, + * events, methods, beans, etc.<P> + * + * <STRONG>Documentation Convention:</STRONG> for proper + * Internalization of Beans inside an RAD tool, sometimes there + * are two names for a property or method: a programmatic, or + * locale-independent name, which can be used anywhere, and a + * localized, display name, for ease of use. In the + * documentation I will specify different String values as + * either <EM>programmatic</EM> or <EM>localized</EM> to + * make this distinction clear. + * + * @author John Keiser + * @since 1.1 + */ + +public class FeatureDescriptor +{ + String name; + String displayName; + String shortDescription; + boolean expert; + boolean hidden; + boolean preferred; + + Hashtable valueHash; + + /** + * Instantiate this FeatureDescriptor with appropriate default values. + */ + public FeatureDescriptor() + { + valueHash = new Hashtable(); + } + + /** + * Get the programmatic name of this feature. + */ + public String getName() + { + return name; + } + + /** + * Set the programmatic name of this feature. + * + * @param name the new name for this feature. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Get the localized (display) name of this feature. + * + * @returns The localized display name of this feature or falls + * back to the programmatic name. + */ + public String getDisplayName() + { + return (displayName == null) ? name : displayName; + } + + /** + * Set the localized (display) name of this feature. + * + * @param displayName the new display name for this feature. + */ + public void setDisplayName(String displayName) + { + this.displayName = displayName; + } + + /** + * Get the localized short description for this feature. + * + * @returns A short localized description of this feature or + * what <code>getDisplayName</code> returns in case, that no short description + * is available. + */ + public String getShortDescription() + { + return (shortDescription == null) ? getDisplayName() : shortDescription; + } + + /** + * Set the localized short description for this feature. + * + * @param shortDescription the new short description for this feature. + */ + public void setShortDescription(String shortDescription) + { + this.shortDescription = shortDescription; + } + + /** + * Indicates whether this feature is for expert use only. + * + * @return true if for use by experts only, + * or false if anyone can use it. + */ + public boolean isExpert() + { + return expert; + } + + /** + * Set whether this feature is for expert use only. + * + * @param expert true if for use by experts only, + * or false if anyone can use it. + */ + public void setExpert(boolean expert) + { + this.expert = expert; + } + + /** + * Indicates whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @return true if tools only should use it, + * or false if anyone can see it. + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Set whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @param hidden true if tools only should use it, + * or false if anyone can see it. + */ + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public boolean isPreferred () + { + return preferred; + } + + public void setPreferred (boolean preferred) + { + this.preferred = preferred; + } + + /** + * Get an arbitrary value set with setValue(). + * + * @param name the programmatic name of the key. + * + * @return the value associated with this name, + * or null if there is none. + */ + public Object getValue(String name) + { + return valueHash.get(name); + } + + /** + * Set an arbitrary string-value pair with this feature. + * + * @param name the programmatic name of the key. + * @param value the value to associate with the name. + */ + public void setValue(String name, Object value) + { + valueHash.put(name, value); + } + + /** + * Get a list of the programmatic key names set with setValue(). + * + * @return an Enumerator over all the programmatic key names associated + * with this feature. + */ + public Enumeration attributeNames() + { + return valueHash.keys(); + } +} diff --git a/libjava/classpath/java/beans/IndexedPropertyDescriptor.java b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java new file mode 100644 index 00000000000..efdc7b40238 --- /dev/null +++ b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java @@ -0,0 +1,307 @@ +/* java.beans.IndexedPropertyDescriptor + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + ** IndexedPropertyDescriptor describes information about a JavaBean + ** indexed property, by which we mean an array-like property that + ** has been exposed via a pair of get and set methods and another + ** pair that allows you to get to the property by an index.<P> + ** + ** An example property would have four methods like this:<P> + ** <CODE>FooBar[] getFoo()</CODE><BR> + ** <CODE>void setFoo(FooBar[])</CODE><BR> + ** <CODE>FooBar getFoo(int)</CODE><BR> + ** <CODE>void setFoo(int,FooBar)</CODE><P> + ** + ** The constraints put on get and set methods are:<P> + ** <OL> + ** <LI>There must be at least a get(int) or a set(int,...) method. + ** Nothing else is required. <B>Spec note:</B>One nice restriction + ** would be that if there is a get() there must be a get(int), same + ** with set, but that is not in the spec and is fairly harmless.)</LI> + ** <LI>A get array method must have signature + ** <CODE><propertyType>[] <getMethodName>()</CODE></LI> + ** <LI>A set array method must have signature + ** <CODE>void <setMethodName>(<propertyType>[])</CODE></LI> + ** <LI>A get index method must have signature + ** <CODE><propertyType> <getMethodName>(int)</CODE></LI> + ** <LI>A set index method must have signature + ** <CODE>void <setMethodName>(int,<propertyType>)</CODE></LI> + ** <LI>All these methods may throw any exception.</LI> + ** <LI>All these methods must be public.</LI> + ** </OL> + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ + +public class IndexedPropertyDescriptor extends PropertyDescriptor { + private Class indexedPropertyType; + private Method setIndex; + private Method getIndex; + + /** Create a new IndexedPropertyDescriptor by introspection. + ** This form of constructor creates the PropertyDescriptor by + ** looking for getter methods named <CODE>get<name>()</CODE> + ** and setter methods named + ** <CODE>set<name>()</CODE> in class + ** <CODE><beanClass></CODE>, where <name> has its + ** first letter capitalized by the constructor.<P> + ** + ** <B>Implementation note:</B> If there is a get(int) method, + ** then the return type of that method is used to find the + ** remaining methods. If there is no get method, then the + ** set(int) method is searched for exhaustively and that type + ** is used to find the others.<P> + ** + ** <B>Spec note:</B> + ** If there is no get(int) method and multiple set(int) methods with + ** the same name and the correct parameters (different type of course), + ** then an IntrospectionException is thrown. While Sun's spec + ** does not state this, it can make Bean behavior different on + ** different systems (since method order is not guaranteed) and as + ** such, can be treated as a bug in the spec. I am not aware of + ** whether Sun's implementation catches this. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @exception IntrospectionException if the methods are not found or invalid. + **/ + public IndexedPropertyDescriptor(String name, Class beanClass) throws IntrospectionException { + super(name); + String capitalized; + try { + capitalized = Character.toUpperCase(name.charAt(0)) + name.substring(1); + } catch(StringIndexOutOfBoundsException e) { + capitalized = ""; + } + findMethods(beanClass, "get" + capitalized, "set" + capitalized, "get" + capitalized, "set" + capitalized); + } + + /** Create a new IndexedPropertyDescriptor by introspection. + ** This form of constructor allows you to specify the + ** names of the get and set methods to search for.<P> + ** + ** <B>Implementation note:</B> If there is a get(int) method, + ** then the return type of that method is used to find the + ** remaining methods. If there is no get method, then the + ** set(int) method is searched for exhaustively and that type + ** is used to find the others.<P> + ** + ** <B>Spec note:</B> + ** If there is no get(int) method and multiple set(int) methods with + ** the same name and the correct parameters (different type of course), + ** then an IntrospectionException is thrown. While Sun's spec + ** does not state this, it can make Bean behavior different on + ** different systems (since method order is not guaranteed) and as + ** such, can be treated as a bug in the spec. I am not aware of + ** whether Sun's implementation catches this. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @param getMethodName the name of the get array method. + ** @param setMethodName the name of the set array method. + ** @param getIndexName the name of the get index method. + ** @param setIndexName the name of the set index method. + ** @exception IntrospectionException if the methods are not found or invalid. + **/ + public IndexedPropertyDescriptor(String name, Class beanClass, String getMethodName, String setMethodName, String getIndexName, String setIndexName) throws IntrospectionException { + super(name); + findMethods(beanClass, getMethodName, setMethodName, getIndexName, setIndexName); + } + + /** Create a new PropertyDescriptor using explicit Methods. + ** Note that the methods will be checked for conformance to standard + ** Property method rules, as described above at the top of this class. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param getMethod the get array method. + ** @param setMethod the set array method. + ** @param getIndex the get index method. + ** @param setIndex the set index method. + ** @exception IntrospectionException if the methods are not found or invalid. + **/ + public IndexedPropertyDescriptor(String name, Method getMethod, Method setMethod, Method getIndex, Method setIndex) throws IntrospectionException { + super(name); + if(getMethod != null && getMethod.getParameterTypes().length > 0) { + throw new IntrospectionException("get method has parameters"); + } + if(getMethod != null && setMethod.getParameterTypes().length != 1) { + throw new IntrospectionException("set method does not have exactly one parameter"); + } + if(getMethod != null && setMethod != null) { + if(!getMethod.getReturnType().equals(setMethod.getParameterTypes()[0])) { + throw new IntrospectionException("set and get methods do not share the same type"); + } + if(!getMethod.getDeclaringClass().isAssignableFrom(setMethod.getDeclaringClass()) + && !setMethod.getDeclaringClass().isAssignableFrom(getMethod.getDeclaringClass())) { + throw new IntrospectionException("set and get methods are not in the same class."); + } + } + + if(getIndex != null && (getIndex.getParameterTypes().length != 1 + || !(getIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) { + throw new IntrospectionException("get index method has wrong parameters"); + } + if(setIndex != null && (setIndex.getParameterTypes().length != 2 + || !(setIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) { + throw new IntrospectionException("set index method has wrong parameters"); + } + if(getIndex != null && setIndex != null) { + if(!getIndex.getReturnType().equals(setIndex.getParameterTypes()[1])) { + throw new IntrospectionException("set index methods do not share the same type"); + } + if(!getIndex.getDeclaringClass().isAssignableFrom(setIndex.getDeclaringClass()) + && !setIndex.getDeclaringClass().isAssignableFrom(getIndex.getDeclaringClass())) { + throw new IntrospectionException("get and set index methods are not in the same class."); + } + } + + if(getIndex != null && getMethod != null && !getIndex.getDeclaringClass().isAssignableFrom(getMethod.getDeclaringClass()) + && !getMethod.getDeclaringClass().isAssignableFrom(getIndex.getDeclaringClass())) { + throw new IntrospectionException("methods are not in the same class."); + } + + if(getIndex != null && getMethod != null && !Array.newInstance(getIndex.getReturnType(),0).getClass().equals(getMethod.getReturnType())) { + throw new IntrospectionException("array methods do not match index methods."); + } + + this.getMethod = getMethod; + this.setMethod = setMethod; + this.getIndex = getIndex; + this.setIndex = setIndex; + this.indexedPropertyType = getIndex != null ? getIndex.getReturnType() : setIndex.getParameterTypes()[1]; + this.propertyType = getMethod != null ? getMethod.getReturnType() : (setMethod != null ? setMethod.getParameterTypes()[0] : Array.newInstance(this.indexedPropertyType,0).getClass()); + } + + public Class getIndexedPropertyType() { + return indexedPropertyType; + } + + public Method getIndexedReadMethod() { + return getIndex; + } + + public Method getIndexedWriteMethod() { + return setIndex; + } + + private void findMethods(Class beanClass, String getMethodName, String setMethodName, String getIndexName, String setIndexName) throws IntrospectionException { + try { + if(getIndexName != null) { + try { + Class[] getArgs = new Class[1]; + getArgs[0] = java.lang.Integer.TYPE; + getIndex = beanClass.getMethod(getIndexName,getArgs); + indexedPropertyType = getIndex.getReturnType(); + } catch(NoSuchMethodException E) { + } + } + if(getIndex != null) { + if(setIndexName != null) { + try { + Class[] setArgs = new Class[2]; + setArgs[0] = java.lang.Integer.TYPE; + setArgs[1] = indexedPropertyType; + setIndex = beanClass.getMethod(setIndexName,setArgs); + if(!setIndex.getReturnType().equals(java.lang.Void.TYPE)) { + throw new IntrospectionException(setIndexName + " has non-void return type"); + } + } catch(NoSuchMethodException E) { + } + } + } else if(setIndexName != null) { + Method[] m = beanClass.getMethods(); + for(int i=0;i<m.length;i++) { + Method current = m[i]; + if(current.getName().equals(setIndexName) + && current.getParameterTypes().length == 2 + && (current.getParameterTypes()[0]).equals(java.lang.Integer.TYPE) + && current.getReturnType().equals(java.lang.Void.TYPE)) { + if(setIndex != null) { + throw new IntrospectionException("Multiple, different set methods found that fit the bill!"); + } else { + setIndex = current; + indexedPropertyType = current.getParameterTypes()[1]; + } + } + } + if(setIndex == null) { + throw new IntrospectionException("Cannot find get or set methods."); + } + } else { + throw new IntrospectionException("Cannot find get or set methods."); + } + + Class arrayType = Array.newInstance(indexedPropertyType,0).getClass(); + + Class[] setArgs = new Class[1]; + setArgs[0] = arrayType; + try { + setMethod = beanClass.getMethod(setMethodName,setArgs); + if(!setMethod.getReturnType().equals(java.lang.Void.TYPE)) { + setMethod = null; + } + } catch(NoSuchMethodException E) { + } + + Class[] getArgs = new Class[0]; + try { + getMethod = beanClass.getMethod(getMethodName,getArgs); + if(!getMethod.getReturnType().equals(arrayType)) { + getMethod = null; + } + } catch(NoSuchMethodException E) { + } + } catch(SecurityException E) { + throw new IntrospectionException("SecurityException while trying to find methods."); + } + } +} diff --git a/libjava/classpath/java/beans/IntrospectionException.java b/libjava/classpath/java/beans/IntrospectionException.java new file mode 100644 index 00000000000..7bb83dac5b3 --- /dev/null +++ b/libjava/classpath/java/beans/IntrospectionException.java @@ -0,0 +1,67 @@ +/* IntrospectionException -- thrown when an exception occurs in introspection + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * IntrospectionException is thrown when the Introspector fails. Typical + * causes are the inability to map a name to its Class, or specifying a + * wrong type signature. + * + * @author John Keiser + * @see Introspector + * @since 1.1 + * @status updated to 1.4 + */ +public class IntrospectionException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3728150539969542619L; + + /** + * Instantiate this exception with the given message. + * + * @param msg the message for the exception + */ + public IntrospectionException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/beans/Introspector.java b/libjava/classpath/java/beans/Introspector.java new file mode 100644 index 00000000000..02781b46e0d --- /dev/null +++ b/libjava/classpath/java/beans/Introspector.java @@ -0,0 +1,608 @@ +/* java.beans.Introspector + Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.BeanInfoEmbryo; +import gnu.java.beans.ExplicitBeanInfo; +import gnu.java.beans.IntrospectionIncubator; +import gnu.java.lang.ClassHelper; + +import java.util.Hashtable; +import java.util.Vector; + +/** + * Introspector is the class that does the bulk of the + * design-time work in Java Beans. Every class must have + * a BeanInfo in order for an RAD tool to use it; but, as + * promised, you don't have to write the BeanInfo class + * yourself if you don't want to. All you have to do is + * call getBeanInfo() in the Introspector and it will use + * standard JavaBeans-defined method signatures to + * determine the information about your class.<P> + * + * Don't worry about it too much, though: you can provide + * JavaBeans with as much customized information as you + * want, or as little as you want, using the BeanInfo + * interface (see BeanInfo for details).<P> + * + * <STRONG>Order of Operations</STRONG><P> + * + * When you call getBeanInfo(class c), the Introspector + * first searches for BeanInfo class to see if you + * provided any explicit information. It searches for a + * class named <bean class name>BeanInfo in different + * packages, first searching the bean class's package + * and then moving on to search the beanInfoSearchPath.<P> + * + * If it does not find a BeanInfo class, it acts as though + * it had found a BeanInfo class returning null from all + * methods (meaning it should discover everything through + * Introspection). If it does, then it takes the + * information it finds in the BeanInfo class to be + * canonical (that is, the information speaks for its + * class as well as all superclasses).<P> + * + * When it has introspected the class, calls + * getBeanInfo(c.getSuperclass) and adds that information + * to the information it has, not adding to any information + * it already has that is canonical.<P> + * + * <STRONG>Introspection Design Patterns</STRONG><P> + * + * When the Introspector goes in to read the class, it + * follows a well-defined order in order to not leave any + * methods unaccounted for. Its job is to step over all + * of the public methods in a class and determine whether + * they are part of a property, an event, or a method (in + * that order). + * + * + * <STRONG>Properties:</STRONG><P> + * + * <OL> + * <LI>If there is a <CODE>public boolean isXXX()</CODE> + * method, then XXX is a read-only boolean property. + * <CODE>boolean getXXX()</CODE> may be supplied in + * addition to this method, although isXXX() is the + * one that will be used in this case and getXXX() + * will be ignored. If there is a + * <CODE>public void setXXX(boolean)</CODE> method, + * it is part of this group and makes it a read-write + * property.</LI> + * <LI>If there is a + * <CODE>public <type> getXXX(int)</CODE> + * method, then XXX is a read-only indexed property of + * type <type>. If there is a + * <CODE>public void setXXX(int,<type>)</CODE> + * method, then it is a read-write indexed property of + * type <type>. There may also be a + * <CODE>public <type>[] getXXX()</CODE> and a + * <CODE>public void setXXX(<type>)</CODE> + * method as well.</LI> + * <LI>If there is a + * <CODE>public void setXXX(int,<type>)</CODE> + * method, then it is a write-only indexed property of + * type <type>. There may also be a + * <CODE>public <type>[] getXXX()</CODE> and a + * <CODE>public void setXXX(<type>)</CODE> + * method as well.</LI> + * <LI>If there is a + * <CODE>public <type> getXXX()</CODE> method, + * then XXX is a read-only property of type + * <type>. If there is a + * <CODE>public void setXXX(<type>)</CODE> + * method, then it will be used for the property and + * the property will be considered read-write.</LI> + * <LI>If there is a + * <CODE>public void setXXX(<type>)</CODE> + * method, then as long as XXX is not already used as + * the name of a property, XXX is assumed to be a + * write-only property of type <type>.</LI> + * <LI>In all of the above cases, if the setXXX() method + * throws <CODE>PropertyVetoException</CODE>, then the + * property in question is assumed to be constrained. + * No properties are ever assumed to be bound + * (<STRONG>Spec Note:</STRONG> this is not in the + * spec, it just makes sense). See PropertyDescriptor + * for a description of bound and constrained + * properties.</LI> + * </OL> + * + * <STRONG>Events:</STRONG><P> + * + * If there is a pair of methods, + * <CODE>public void addXXX(<type>)</CODE> and + * <CODE>public void removeXXX(<type>)</CODE>, where + * <type> is a descendant of + * <CODE>java.util.EventListener</CODE>, then the pair of + * methods imply that this Bean will fire events to + * listeners of type <type>.<P> + * + * If the addXXX() method throws + * <CODE>java.util.TooManyListenersException</CODE>, then + * the event set is assumed to be <EM>unicast</EM>. See + * EventSetDescriptor for a discussion of unicast event + * sets.<P> + * + * <STRONG>Spec Note:</STRONG> the spec seems to say that + * the listener type's classname must be equal to the XXX + * part of addXXX() and removeXXX(), but that is not the + * case in Sun's implementation, so I am assuming it is + * not the case in general.<P> + * + * <STRONG>Methods:</STRONG><P> + * + * Any public methods (including those which were used + * for Properties or Events) are used as Methods. + * + * @author John Keiser + * @since JDK1.1 + * @see java.beans.BeanInfo + */ +public class Introspector { + + public static final int USE_ALL_BEANINFO = 1; + public static final int IGNORE_IMMEDIATE_BEANINFO = 2; + public static final int IGNORE_ALL_BEANINFO = 3; + + static String[] beanInfoSearchPath = {"gnu.java.beans.info"}; + static Hashtable beanInfoCache = new Hashtable(); + + private Introspector() {} + + /** + * Get the BeanInfo for class <CODE>beanClass</CODE>, + * first by looking for explicit information, next by + * using standard design patterns to determine + * information about the class. + * + * @param beanClass the class to get BeanInfo about. + * @return the BeanInfo object representing the class. + */ + public static BeanInfo getBeanInfo(Class beanClass) + throws IntrospectionException + { + BeanInfo cachedInfo; + synchronized(beanClass) + { + cachedInfo = (BeanInfo)beanInfoCache.get(beanClass); + if(cachedInfo != null) + { + return cachedInfo; + } + cachedInfo = getBeanInfo(beanClass,null); + beanInfoCache.put(beanClass,cachedInfo); + return cachedInfo; + } + } + + /** + * Flush all of the Introspector's internal caches. + * + * @since 1.2 + */ + public static void flushCaches() + { + beanInfoCache.clear(); + + // Clears all the intermediate ExplicitInfo instances which + // have been created. + // This makes sure we have to retrieve stuff like BeanDescriptors + // again. (Remember that FeatureDescriptor can be modified by the user.) + ExplicitInfo.flushCaches(); + } + + /** + * Flush the Introspector's internal cached information for a given + * class. + * + * @param clz the class to be flushed. + * @throws NullPointerException if clz is null. + * @since 1.2 + */ + public static void flushFromCaches(Class clz) + { + synchronized (clz) + { + beanInfoCache.remove(clz); + } + } + + /** + * Get the BeanInfo for class <CODE>beanClass</CODE>, + * first by looking for explicit information, next by + * using standard design patterns to determine + * information about the class. It crawls up the + * inheritance tree until it hits <CODE>topClass</CODE>. + * + * @param beanClass the Bean class. + * @param stopClass the class to stop at. + * @return the BeanInfo object representing the class. + */ + public static BeanInfo getBeanInfo(Class beanClass, Class stopClass) + throws IntrospectionException + { + ExplicitInfo explicit = new ExplicitInfo(beanClass, stopClass); + + IntrospectionIncubator ii = new IntrospectionIncubator(); + ii.setPropertyStopClass(explicit.propertyStopClass); + ii.setEventStopClass(explicit.eventStopClass); + ii.setMethodStopClass(explicit.methodStopClass); + ii.addMethods(beanClass.getMethods()); + + BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo(); + PropertyDescriptor[] p = explicit.explicitPropertyDescriptors; + if(p!=null) + { + for(int i=0;i<p.length;i++) + { + if(!currentInfo.hasProperty(p[i])) + { + currentInfo.addProperty(p[i]); + } + } + if(explicit.defaultProperty != -1) + { + currentInfo.setDefaultPropertyName(p[explicit.defaultProperty].getName()); + } + } + EventSetDescriptor[] e = explicit.explicitEventSetDescriptors; + if(e!=null) + { + for(int i=0;i<e.length;i++) + { + if(!currentInfo.hasEvent(e[i])) + { + currentInfo.addEvent(e[i]); + } + } + if(explicit.defaultEvent != -1) + { + currentInfo.setDefaultEventName(e[explicit.defaultEvent].getName()); + } + } + MethodDescriptor[] m = explicit.explicitMethodDescriptors; + if(m!=null) + { + for(int i=0;i<m.length;i++) + { + if(!currentInfo.hasMethod(m[i])) + { + currentInfo.addMethod(m[i]); + } + } + } + + // Sets the info's BeanDescriptor to the one we extracted from the + // explicit BeanInfo instance(s) if they contained one. Otherwise we + // create the BeanDescriptor from scratch. + // Note: We do not create a copy the retrieved BeanDescriptor which will allow + // the user to modify the instance while it is cached. However this is how + // the RI does it. + currentInfo.setBeanDescriptor( + (explicit.explicitBeanDescriptor == null ? + new BeanDescriptor(beanClass, null) : + explicit.explicitBeanDescriptor)); + + currentInfo.setAdditionalBeanInfo(explicit.explicitBeanInfo); + currentInfo.setIcons(explicit.im); + + return currentInfo.getBeanInfo(); + } + + /** + * Get the search path for BeanInfo classes. + * + * @return the BeanInfo search path. + */ + public static String[] getBeanInfoSearchPath() + { + return beanInfoSearchPath; + } + + /** + * Set the search path for BeanInfo classes. + * @param beanInfoSearchPath the new BeanInfo search + * path. + */ + public static void setBeanInfoSearchPath(String[] beanInfoSearchPath) + { + Introspector.beanInfoSearchPath = beanInfoSearchPath; + } + + /** + * A helper method to convert a name to standard Java + * naming conventions: anything with two capitals as the + * first two letters remains the same, otherwise the + * first letter is decapitalized. URL = URL, I = i, + * MyMethod = myMethod. + * + * @param name the name to decapitalize. + * @return the decapitalized name. + */ + public static String decapitalize(String name) + { + try + { + if(!Character.isUpperCase(name.charAt(0))) + { + return name; + } + else + { + try + { + if(Character.isUpperCase(name.charAt(1))) + { + return name; + } + else + { + char[] c = name.toCharArray(); + c[0] = Character.toLowerCase(c[0]); + return new String(c); + } + } + catch(StringIndexOutOfBoundsException E) + { + char[] c = new char[1]; + c[0] = Character.toLowerCase(name.charAt(0)); + return new String(c); + } + } + } + catch(StringIndexOutOfBoundsException E) + { + return name; + } + catch(NullPointerException E) + { + return null; + } + } + + static BeanInfo copyBeanInfo(BeanInfo b) + { + java.awt.Image[] icons = new java.awt.Image[4]; + for(int i=1;i<=4;i++) + { + icons[i-1] = b.getIcon(i); + } + + return new ExplicitBeanInfo(b.getBeanDescriptor(), + b.getAdditionalBeanInfo(), + b.getPropertyDescriptors(), + b.getDefaultPropertyIndex(), + b.getEventSetDescriptors(), + b.getDefaultEventIndex(), + b.getMethodDescriptors(), + icons); + } +} + +class ExplicitInfo +{ + BeanDescriptor explicitBeanDescriptor; + BeanInfo[] explicitBeanInfo; + + PropertyDescriptor[] explicitPropertyDescriptors; + EventSetDescriptor[] explicitEventSetDescriptors; + MethodDescriptor[] explicitMethodDescriptors; + + int defaultProperty; + int defaultEvent; + + java.awt.Image[] im = new java.awt.Image[4]; + + Class propertyStopClass; + Class eventStopClass; + Class methodStopClass; + + static Hashtable explicitBeanInfos = new Hashtable(); + static Vector emptyBeanInfos = new Vector(); + + ExplicitInfo(Class beanClass, Class stopClass) + { + while(beanClass != null && !beanClass.equals(stopClass)) + { + + BeanInfo explicit = findExplicitBeanInfo(beanClass); + + + if(explicit != null) + { + + if(explicitBeanDescriptor == null) + { + explicitBeanDescriptor = explicit.getBeanDescriptor(); + } + + if(explicitBeanInfo == null) + { + explicitBeanInfo = explicit.getAdditionalBeanInfo(); + } + + if(explicitPropertyDescriptors == null) + { + if(explicit.getPropertyDescriptors() != null) + { + explicitPropertyDescriptors = explicit.getPropertyDescriptors(); + defaultProperty = explicit.getDefaultPropertyIndex(); + propertyStopClass = beanClass; + } + } + + if(explicitEventSetDescriptors == null) + { + if(explicit.getEventSetDescriptors() != null) + { + explicitEventSetDescriptors = explicit.getEventSetDescriptors(); + defaultEvent = explicit.getDefaultEventIndex(); + eventStopClass = beanClass; + } + } + + if(explicitMethodDescriptors == null) + { + if(explicit.getMethodDescriptors() != null) + { + explicitMethodDescriptors = explicit.getMethodDescriptors(); + methodStopClass = beanClass; + } + } + + if(im[0] == null && im[1] == null + && im[2] == null && im[3] == null) + { + im[0] = explicit.getIcon(0); + im[1] = explicit.getIcon(1); + im[2] = explicit.getIcon(2); + im[3] = explicit.getIcon(3); + } + } + beanClass = beanClass.getSuperclass(); + } + + if(propertyStopClass == null) + { + propertyStopClass = stopClass; + } + + if(eventStopClass == null) + { + eventStopClass = stopClass; + } + + if(methodStopClass == null) + { + methodStopClass = stopClass; + } + } + + /** Throws away all cached data and makes sure we re-instantiate things + * like BeanDescriptors again. + */ + static void flushCaches() { + explicitBeanInfos.clear(); + emptyBeanInfos.clear(); + } + + static BeanInfo findExplicitBeanInfo(Class beanClass) + { + BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass); + if(retval != null) + { + return retval; + } + else if(emptyBeanInfos.indexOf(beanClass) != -1) + { + return null; + } + else + { + retval = reallyFindExplicitBeanInfo(beanClass); + if(retval != null) + { + explicitBeanInfos.put(beanClass,retval); + } + else + { + emptyBeanInfos.addElement(beanClass); + } + return retval; + } + } + + static BeanInfo reallyFindExplicitBeanInfo(Class beanClass) + { + ClassLoader beanClassLoader = beanClass.getClassLoader(); + BeanInfo beanInfo; + + beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo"); + if (beanInfo == null) + { + String newName; + newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo"; + + for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++) + { + if (Introspector.beanInfoSearchPath[i].equals("")) + beanInfo = getBeanInfo(beanClassLoader, newName); + else + beanInfo = getBeanInfo(beanClassLoader, + Introspector.beanInfoSearchPath[i] + "." + + newName); + + // Returns the beanInfo if it exists and the described class matches + // the one we searched. + if (beanInfo != null && beanInfo.getBeanDescriptor() != null && + beanInfo.getBeanDescriptor().getBeanClass() == beanClass) + + return beanInfo; + } + } + + return beanInfo; + } + + /** + * Returns an instance of the given class name when it can be loaded + * through the given class loader, or null otherwise. + */ + private static BeanInfo getBeanInfo(ClassLoader cl, String infoName) + { + try + { + return (BeanInfo) Class.forName(infoName, true, cl).newInstance(); + } + catch (ClassNotFoundException cnfe) + { + return null; + } + catch (IllegalAccessException iae) + { + return null; + } + catch (InstantiationException ie) + { + return null; + } + } + +} diff --git a/libjava/classpath/java/beans/MethodDescriptor.java b/libjava/classpath/java/beans/MethodDescriptor.java new file mode 100644 index 00000000000..123dba60cc9 --- /dev/null +++ b/libjava/classpath/java/beans/MethodDescriptor.java @@ -0,0 +1,88 @@ +/* java.beans.MethodDescriptor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Method; + +/** MethodDescriptor describes information about a JavaBeans method. + ** It's a fairly straightforward class (at least something in this + ** package is straightforward!). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class MethodDescriptor extends FeatureDescriptor { + private Method m; + private ParameterDescriptor[] parameterDescriptors; + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + **/ + public MethodDescriptor(Method m) { + setName(m.getName()); + this.m = m; + } + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + ** @param parameterDescriptors descriptions of the parameters (especially names). + **/ + public MethodDescriptor(Method m, ParameterDescriptor[] parameterDescriptors) { + setName(m.getName()); + this.m = m; + this.parameterDescriptors = parameterDescriptors; + } + + /** Get the parameter descriptors from this method. + ** Since MethodDescriptor has no way of determining what + ** the parameter names were, this defaults to null. + **/ + public ParameterDescriptor[] getParameterDescriptors() { + return parameterDescriptors; + } + + /** Get the method this MethodDescriptor represents. **/ + public Method getMethod() { + return m; + } +} + diff --git a/libjava/classpath/java/beans/ParameterDescriptor.java b/libjava/classpath/java/beans/ParameterDescriptor.java new file mode 100644 index 00000000000..8ea93a9bd8d --- /dev/null +++ b/libjava/classpath/java/beans/ParameterDescriptor.java @@ -0,0 +1,52 @@ +/* java.beans.MethodDescriptor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** ParameterDescriptor represents a single parameter to a method. + ** As it turns out, FeatureDescriptor is sufficient to hold all + ** the information. Use its constructor and methods to set + ** the appropriate values. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class ParameterDescriptor extends FeatureDescriptor { + +} diff --git a/libjava/classpath/java/beans/PropertyChangeEvent.java b/libjava/classpath/java/beans/PropertyChangeEvent.java new file mode 100644 index 00000000000..3e3f948e32a --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeEvent.java @@ -0,0 +1,189 @@ +/* PropertyChangeEvent.java -- describes a change in a property + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventObject; + +/** + * PropertyChangeEvents are fired in the PropertyChange and VetoableChange + * event classes. They represent the old and new values as well as the + * source Bean. If the old or new value is a primitive type, it must be + * wrapped in the appropriate wrapper type (java.lang.Integer for int, etc., + * etc.). + * + * <p>If the old or new values are unknown (although why that would be I do + * not know), they may be null. Also, if the set of properties itself has + * changed, the name should be null, and the old and new values may also be + * null. Right now Sun put in a propagationId, reserved for future use. Read + * the comments on the constructor and on setPropagationId for more + * information. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status udpated to 1.4 + */ +public class PropertyChangeEvent extends EventObject +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7042693688939648123L; + + /** + * The name of the property that changed, may be null. Package visible for + * use by PropertyChangeSupport. + * + * @serial the changed property name + */ + final String propertyName; + + /** + * The new value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the new property value + */ + final Object newValue; + + /** + * The old value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the old property value + */ + final Object oldValue; + + /** + * The propagation ID, reserved for future use. May be null. + * + * @see #getPropagationId() + * @serial the Propagation ID + */ + private Object propagationId; + + /** + * Create a new PropertyChangeEvent. Remember that if you received a + * PropertyChangeEvent and are sending a new one, you should also set the + * propagation ID from the old PropertyChangeEvent. + * + * @param source the Bean containing the property + * @param propertyName the property's name + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @throws IllegalArgumentException if source is null + */ + public PropertyChangeEvent(Object source, String propertyName, + Object oldVal, Object newVal) + { + super(source); + this.propertyName = propertyName; + oldValue = oldVal; + newValue = newVal; + } + + /** + * Get the property name. May be null if multiple properties changed. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } + + /** + * Get the property's new value. May be null if multiple properties changed. + * + * @return the property's new value + */ + public Object getNewValue() + { + return newValue; + } + + /** + * Get the property's old value. May be null if multiple properties changed. + * + * @return the property's old value + */ + public Object getOldValue() + { + return oldValue; + } + + /** + * Set the propagation ID. This is a way for the event to be passed from + * hand to hand and retain a little extra state. Right now it is unused, + * but it should be propagated anyway so that future versions of JavaBeans + * can use it, for God knows what. + * + * @param propagationId the propagation ID + * @see #getPropagationId() + */ + public void setPropagationId(Object propagationId) + { + this.propagationId = propagationId; + } + + /** + * Get the propagation ID. Right now, it is not used for anything. + * + * @return the propagation ID + * @see #setPropagationId(Object) + */ + public Object getPropagationId() + { + return propagationId; + } + + /** + * Utility method to rollback a change. + * + * @param event the event to rollback + * @return a new event with old and new swapped + */ + PropertyChangeEvent rollback() + { + PropertyChangeEvent result + = new PropertyChangeEvent(source, propertyName, newValue, oldValue); + result.propagationId = propagationId; + return result; + } +} // class PropertyChangeEvent diff --git a/libjava/classpath/java/beans/PropertyChangeListener.java b/libjava/classpath/java/beans/PropertyChangeListener.java new file mode 100644 index 00000000000..a97e6e1c2e9 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListener.java @@ -0,0 +1,61 @@ +/* PropertyChangeListener.java -- listen for changes in a bound property + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * PropertyChangeListener allows a class to monitor properties of a Bean for + * changes. A propertyChange() event will only be fired <em>after</em> the + * property has changed. + * + * @author John Keiser + * @see PropertyChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface PropertyChangeListener extends EventListener +{ + /** + * Fired after a Bean's property has changed. + * + * @param e the change (containing the old and new values) + */ + void propertyChange(PropertyChangeEvent e); +} // interface PropertyChangeListener diff --git a/libjava/classpath/java/beans/PropertyChangeListenerProxy.java b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java new file mode 100644 index 00000000000..68a8153032b --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* PropertyChangeListenerProxy.java -- adds a name to a property listener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to <code>PropertyChangeListener</code> - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class PropertyChangeListenerProxy extends EventListenerProxy + implements PropertyChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * PropertyChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. A null propertyName + * or listener does not fail now, but may cause a NullPointerException down + * the road. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public PropertyChangeListenerProxy(String propertyName, + PropertyChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + */ + public void propertyChange(PropertyChangeEvent event) + { + // Note: Sun does not filter, under the assumption that since + // PropertyChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((PropertyChangeListener) getListener()).propertyChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class PropertyChangeListenerProxy diff --git a/libjava/classpath/java/beans/PropertyChangeSupport.java b/libjava/classpath/java/beans/PropertyChangeSupport.java new file mode 100644 index 00000000000..a0e64af4d29 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeSupport.java @@ -0,0 +1,488 @@ +/* PropertyChangeSupport.java -- support to manage property change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * PropertyChangeSupport makes it easy to fire property change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6401253773779951803L; + + /** + * Maps property names (String) to named listeners (PropertyChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int propertyChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered property listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the <code>children</code> hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a PropertyChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public PropertyChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a PropertyChangeListener to the list of global listeners. All + * property change events will be sent to this listener. The listener add + * is not unique: that is, <em>n</em> adds with the same listener will + * result in <em>n</em> events being sent to that listener for every + * property change. Adding a null listener may cause a NullPointerException + * down the road. This method will unwrap a PropertyChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + addPropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a PropertyChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If <code>add()</code> has been called multiple times for a + * particular listener, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removePropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + removePropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered property change listeners. Those that + * were registered under a name will be wrapped in a + * <code>PropertyChangeListenerProxy</code>, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see PropertyChangeListenerProxy + * @since 1.4 + */ + public synchronized PropertyChangeListener[] getPropertyChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((PropertyChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new PropertyChangeListenerProxy + (name, (PropertyChangeListener) v.get(j))); + } + } + return (PropertyChangeListener[]) + list.toArray(new PropertyChangeListener[list.size()]); + } + + /** + * Adds a PropertyChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, <em>n</em> adds on a particular + * property for a particular listener will result in <em>n</em> events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all property changes, and then you register on a particular property, + * you will receive change events for that property twice. Adding a null + * listener may cause a NullPointerException down the road. This method + * will unwrap a PropertyChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + * @throws NullPointerException if propertyName is null + */ + public synchronized void addPropertyChangeListener(String propertyName, + PropertyChangeListener l) + { + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + PropertyChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + { + s = new PropertyChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a PropertyChangeListener from listening to a specific property. + * If <code>add()</code> has been called multiple times for a particular + * listener on a property, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removePropertyChangeListener(String propertyName, PropertyChangeListener l) + { + if (children == null) + return; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all property change listeners registered under the + * given property name. If there are no registered listeners, this returns + * an empty array. + * + * @return the array of registered listeners + * @throws NullPointerException if propertyName is null + * @since 1.4 + */ + public synchronized PropertyChangeListener[] + getPropertyChangeListeners(String propertyName) + { + if (children == null) + return new PropertyChangeListener[0]; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return new PropertyChangeListener[0]; + return (PropertyChangeListener[]) + s.listeners.toArray(new PropertyChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + Object oldVal, Object newVal) + { + firePropertyChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, int oldVal, int newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + new Integer(oldVal), + new Integer(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + boolean oldVal, boolean newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. + * + * @param event the event to fire + * @throws NullPointerException if event is null + */ + public void firePropertyChange(PropertyChangeEvent event) + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + PropertyChangeSupport s + = (PropertyChangeSupport) h.get(event.propertyName); + if (s != null) + { + v = s.listeners; // Be thread-safe. + int i = v == null ? 0 : v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + } + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return <code>true</code> if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + * @throws NullPointerException if propertyName is null + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if propertyChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + PropertyChangeListener l = (PropertyChangeListener) s.readObject(); + while (l != null) + { + addPropertyChangeListener(l); + l = (PropertyChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + PropertyChangeSupport pcs = (PropertyChangeSupport) e.getValue(); + if (pcs.listeners == null) + pcs.listeners = new Vector(); + if (pcs.children != null) + pcs.listeners.addAll + (Arrays.asList(pcs.getPropertyChangeListeners(name))); + if (pcs.listeners.size() == 0) + iter.remove(); + else + pcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class PropertyChangeSupport diff --git a/libjava/classpath/java/beans/PropertyDescriptor.java b/libjava/classpath/java/beans/PropertyDescriptor.java new file mode 100644 index 00000000000..416d468576f --- /dev/null +++ b/libjava/classpath/java/beans/PropertyDescriptor.java @@ -0,0 +1,583 @@ +/* java.beans.PropertyDescriptor + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.reflect.Method; + +/** + ** PropertyDescriptor describes information about a JavaBean property, + ** by which we mean a property that has been exposed via a pair of + ** get and set methods. (There may be no get method, which means + ** the property is write-only, or no set method, which means the + ** the property is read-only.)<P> + ** + ** The constraints put on get and set methods are:<P> + ** <OL> + ** <LI>A get method must have signature + ** <CODE><propertyType> <getMethodName>()</CODE></LI> + ** <LI>A set method must have signature + ** <CODE>void <setMethodName>(<propertyType>)</CODE></LI> + ** <LI>Either method type may throw any exception.</LI> + ** <LI>Both methods must be public.</LI> + ** </OL> + ** + ** @author John Keiser + ** @author Robert Schuster (thebohemian@gmx.net) + ** @since 1.1 + ** @status updated to 1.4 + **/ + +public class PropertyDescriptor extends FeatureDescriptor +{ + Class propertyType; + Method getMethod; + Method setMethod; + + Class propertyEditorClass; + boolean bound; + boolean constrained; + + PropertyDescriptor(String name) + { + setName(name); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor creates the PropertyDescriptor by + ** looking for a getter method named <CODE>get<name>()</CODE> + ** (or, optionally, if the property is boolean, + ** <CODE>is<name>()</CODE>) and + ** <CODE>set<name>()</CODE> in class + ** <CODE><beanClass></CODE>, where <name> has its + ** first letter capitalized by the constructor.<P> + ** + ** Note that using this constructor the given property must be read- <strong>and</strong> + ** writeable. If the implementation does not both, a read and a write method, an + ** <code>IntrospectionException</code> is thrown. + ** + ** <B>Implementation note:</B> If there is both are both isXXX and + ** getXXX methods, the former is used in preference to the latter. + ** We do not check that an isXXX method returns a boolean. In both + ** cases, this matches the behaviour of JDK 1.4<P> + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor(String name, Class beanClass) + throws IntrospectionException + { + setName(name); + if (name.length() == 0) + { + throw new IntrospectionException("empty property name"); + } + String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); + findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); + + if (getMethod == null) + { + throw new IntrospectionException( + "Cannot find a is" + caps + " or get" + caps + " method"); + } + + if (setMethod == null) + { + throw new IntrospectionException( + "Cannot find a " + caps + " method"); + } + + // finally check the methods compatibility + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor allows you to specify the + ** names of the get and set methods to search for.<P> + ** + ** <B>Implementation note:</B> If there is a get method (or + ** boolean isXXX() method), then the return type of that method + ** is used to find the set method. If there is no get method, + ** then the set method is searched for exhaustively.<P> + ** + ** <B>Spec note:</B> + ** If there is no get method and multiple set methods with + ** the same name and a single parameter (different type of course), + ** then an IntrospectionException is thrown. While Sun's spec + ** does not state this, it can make Bean behavior different on + ** different systems (since method order is not guaranteed) and as + ** such, can be treated as a bug in the spec. I am not aware of + ** whether Sun's implementation catches this. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @param getMethodName the name of the get method or <code>null</code> if the property is write-only. + ** @param setMethodName the name of the set method or <code>null</code> if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Class beanClass, + String getMethodName, + String setMethodName) + throws IntrospectionException + { + setName(name); + findMethods(beanClass, getMethodName, null, setMethodName); + + if (getMethod == null && getMethodName != null) + { + throw new IntrospectionException( + "Cannot find a getter method called " + getMethodName); + } + + if (setMethod == null && setMethodName != null) + { + throw new IntrospectionException( + "Cannot find a setter method called " + setMethodName); + } + + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor using explicit Methods. + ** Note that the methods will be checked for conformance to standard + ** Property method rules, as described above at the top of this class. + **<br> + ** It is possible to call this method with both <code>Method</code> arguments + ** being <code>null</code>. In such a case the property type is <code>null</code>. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param readMethod the read method or <code>null</code> if the property is write-only. + ** @param writeMethod the write method or <code>null</code> if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Method readMethod, + Method writeMethod) + throws IntrospectionException + { + setName(name); + getMethod = readMethod; + setMethod = writeMethod; + propertyType = checkMethods(getMethod, setMethod); + } + + /** Get the property type. + ** This is the type the get method returns and the set method + ** takes in. + **/ + public Class getPropertyType() + { + return propertyType; + } + + /** Get the get method. Why they call it readMethod here and + ** get everywhere else is beyond me. + **/ + public Method getReadMethod() + { + return getMethod; + } + + /** Sets the read method.<br/> + * The read method is used to retrieve the value of a property. A legal + * read method must have no arguments. Its return type must not be + * <code>void</code>. If this methods succeeds the property type + * is adjusted to the return type of the read method.<br/> + * <br/> + * It is legal to set the read and the write method to <code>null</code> + * or provide method which have been declared in distinct classes. + * + * @param readMethod The new method to be used or <code>null</code>. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setReadMethod(Method readMethod) throws IntrospectionException + { + propertyType = checkMethods(readMethod, setMethod); + + getMethod = readMethod; + } + + /** Get the set method. Why they call it writeMethod here and + ** set everywhere else is beyond me. + **/ + public Method getWriteMethod() + { + return setMethod; + } + + /** Sets the write method.<br/> + * The write method is used to set the value of a property. A legal write method + * must have a single argument which can be assigned to the property. If no + * read method exists the property type changes to the argument type of the + * write method.<br/> + * <br/> + * It is legal to set the read and the write method to <code>null</code> + * or provide method which have been declared in distinct classes. + * + * @param writeMethod The new method to be used or <code>null</code>. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setWriteMethod(Method writeMethod) + throws IntrospectionException + { + propertyType = checkMethods(getMethod, writeMethod); + + setMethod = writeMethod; + } + + /** Get whether the property is bound. Defaults to false. **/ + public boolean isBound() + { + return bound; + } + + /** Set whether the property is bound. + ** As long as the the bean implements addPropertyChangeListener() and + ** removePropertyChangeListener(), setBound(true) may safely be called.<P> + ** If these things are not true, then the behavior of the system + ** will be undefined.<P> + ** + ** When a property is bound, its set method is required to fire the + ** <CODE>PropertyChangeListener.propertyChange())</CODE> event + ** after the value has changed. + ** @param bound whether the property is bound or not. + **/ + public void setBound(boolean bound) + { + this.bound = bound; + } + + /** Get whether the property is constrained. Defaults to false. **/ + public boolean isConstrained() + { + return constrained; + } + + /** Set whether the property is constrained. + ** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> + ** (or subclass thereof) and the bean implements addVetoableChangeListener() + ** and removeVetoableChangeListener(), then setConstrained(true) may safely + ** be called. Otherwise, the system behavior is undefined. + ** <B>Spec note:</B> given those strict parameters, it would be nice if it + ** got set automatically by detection, but oh well.<P> + ** When a property is constrained, its set method is required to:<P> + ** <OL> + ** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> + ** event notifying others of the change and allowing them a chance to + ** say it is a bad thing.</LI> + ** <LI>If any of the listeners throws a PropertyVetoException, then + ** it must fire another vetoableChange() event notifying the others + ** of a reversion to the old value (though, of course, the change + ** was never made). Then it rethrows the PropertyVetoException and + ** exits.</LI> + ** <LI>If all has gone well to this point, the value may be changed.</LI> + ** </OL> + ** @param constrained whether the property is constrained or not. + **/ + public void setConstrained(boolean constrained) + { + this.constrained = constrained; + } + + /** Get the PropertyEditor class. Defaults to null. **/ + public Class getPropertyEditorClass() + { + return propertyEditorClass; + } + + /** Set the PropertyEditor class. If the class does not implement + ** the PropertyEditor interface, you will likely get an exception + ** late in the game. + ** @param propertyEditorClass the PropertyEditor class for this + ** class to use. + **/ + public void setPropertyEditorClass(Class propertyEditorClass) + { + this.propertyEditorClass = propertyEditorClass; + } + + private void findMethods( + Class beanClass, + String getMethodName1, + String getMethodName2, + String setMethodName) + throws IntrospectionException + { + try + { + // Try the first get method name + if (getMethodName1 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName1, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Fall back to the second get method name + if (getMethod == null && getMethodName2 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName2, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Try the set method name + if (setMethodName != null) + { + if (getMethod != null) + { + // If there is a get method, use its return type to help + // select the corresponding set method. + Class propertyType = getMethod.getReturnType(); + if (propertyType == Void.TYPE) + { + String msg = + "The property's read method has return type 'void'"; + throw new IntrospectionException(msg); + } + + Class[] setArgs = new Class[] { propertyType }; + try + { + setMethod = beanClass.getMethod(setMethodName, setArgs); + } + catch (NoSuchMethodException e) + {} + } + else if (getMethodName1 == null && getMethodName2 == null) + { + // If this is a write-only property, choose the first set method + // with the required name, one parameter and return type 'void' + Method[] methods = beanClass.getMethods(); + for (int i = 0; i < methods.length; i++) + { + if (methods[i].getName().equals(setMethodName) + && methods[i].getParameterTypes().length == 1 + && methods[i].getReturnType() == Void.TYPE) + { + setMethod = methods[i]; + break; + } + } + } + } + } + catch (SecurityException e) + { + // FIXME -- shouldn't we just allow SecurityException to propagate? + String msg = + "SecurityException thrown on attempt to access methods."; + throw new IntrospectionException(msg); + } + } + + /** Checks whether the given <code>Method</code> instances are legal read and + * write methods. The following requirements must be met:<br/> + * <ul> + * <li>the read method must not have an argument</li> + * <li>the read method must have a non void return type</li> + * <li>the read method may not exist</li> + * <li>the write method must have a single argument</li> + * <li>the property type and the read method's return type must be assignable from the + * write method's argument type</li> + * <li>the write method may not exist</li> + * </ul> + * While checking the methods a common new property type is calculated. If the method + * succeeds this property type is returned.<br/> + * <br/> + * For compatibility this has to be noted:<br/> + * The two methods are allowed to be defined in two distinct classes and may both be null. + * + * @param readMethod The new read method to check. + * @param writeMethod The new write method to check. + * @return The common property type of the two method. + * @throws IntrospectionException If any of the above requirements are not met. + */ + private Class checkMethods(Method readMethod, Method writeMethod) + throws IntrospectionException + { + Class newPropertyType = propertyType; + + // a valid read method has zero arguments and a non-void return type. + if (readMethod != null) + { + if (readMethod.getParameterTypes().length > 0) + { + throw new IntrospectionException("read method has unexpected parameters"); + } + + newPropertyType = readMethod.getReturnType(); + + if (newPropertyType == Void.TYPE) + { + throw new IntrospectionException("read method return type is void"); + } + } + + // a valid write method has one argument which can be assigned to the property + if (writeMethod != null) + { + if (writeMethod.getParameterTypes().length != 1) + { + String msg = "write method does not have exactly one parameter"; + throw new IntrospectionException(msg); + } + + if (readMethod == null) + { + // changes the property type if there is no read method + newPropertyType = writeMethod.getParameterTypes()[0]; + } + else + { + // checks whether the write method can be assigned to the return type of the read + // method (if this is not the case, the methods are not compatible) + // note: newPropertyType may be null if no methods or method names have been + // delivered in the constructor. + if (newPropertyType != null + && !newPropertyType.isAssignableFrom( + writeMethod.getParameterTypes()[0])) + { + // note: newPropertyType is the same as readMethod.getReturnType() at this point + throw new IntrospectionException("read and write method are not compatible"); + } + + /* note: the check whether both method are defined in related classes makes sense but is not + * done in the JDK. + * I leave this code here in case someone at Sun decides to add that functionality in later versions (rschuster) + if ((!readMethod + .getDeclaringClass() + .isAssignableFrom(writeMethod.getDeclaringClass())) + && (!writeMethod + .getDeclaringClass() + .isAssignableFrom(readMethod.getDeclaringClass()))) + { + String msg = + "set and get methods are not in the same class."; + throw new IntrospectionException(msg); + } + */ + + } + } + + return newPropertyType; + } + + /** Compares this <code>PropertyDescriptor</code> against the + * given object. + * Two PropertyDescriptors are equals if + * <ul> + * <li>the read methods are equal</li> + * <li>the write methods are equal</li> + * <li>the property types are equals</li> + * <li>the property editor classes are equal</li> + * <li>the flags (constrained and bound) are equal</li> + * </ul> + * @return Whether both objects are equal according to the rules given above. + * @since 1.4 + */ + public boolean equals(Object o) + { + if (o instanceof PropertyDescriptor) + { + PropertyDescriptor that = (PropertyDescriptor) o; + + // compares the property types and checks the case where both are null + boolean samePropertyType = + (propertyType == null) + ? that.propertyType == null + : propertyType.equals(that.propertyType); + + // compares the property editor classes and checks the case where both are null + boolean samePropertyEditorClass = + (propertyEditorClass == null) + ? that.propertyEditorClass == null + : propertyEditorClass.equals(that.propertyEditorClass); + + // compares the flags for equality + boolean sameFlags = + bound == that.bound && constrained == that.constrained; + + // compares the read methods and checks the case where both are null + boolean sameReadMethod = + (getMethod == null) + ? that.getMethod == null + : getMethod.equals(that.getMethod); + + boolean sameWriteMethod = + (setMethod == null) + ? that.setMethod == null + : setMethod.equals(that.setMethod); + + return samePropertyType + && sameFlags + && sameReadMethod + && sameWriteMethod + && samePropertyEditorClass; + } + else + { + return false; + } + + } + +} diff --git a/libjava/classpath/java/beans/PropertyEditor.java b/libjava/classpath/java/beans/PropertyEditor.java new file mode 100644 index 00000000000..d1c5103419f --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditor.java @@ -0,0 +1,209 @@ +/* java.beans.PropertyEditor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** PropertyEditors are custom GUI editors for specific types of values. + ** + ** A PropertyEditor can be used, for example, if you are editing a type of value + ** that can be more easily represented graphically, such as a Point, or one that + ** can be more easily represented by a list, such as a boolean (true/false).<P> + ** + ** A PropertyEditor must be able to display its contents when asked to and + ** be able to allow the user to change its underlying field value. However, it + ** is not the PropertyEditor's responsibility to make the change to the + ** underlying Object; in fact, the PropertyEditor does not even know about the + ** Object it is actually editing--only about the property it is currently + ** editing. When a change is made to the property, the PropertyEditor must + ** simply fire a PropertyChangeEvent and allow the RAD tool to actually set + ** the property in the underlying Bean.<P> + ** + ** PropertyEditors should not change the Objects they are given by setValue(). + ** These Objects may or may not be the actual Objects which are properties of + ** the Bean being edited. Instead, PropertyEditors should create a new Object + ** and fire a PropertyChangeEvent with the old and new values.<P> + ** + ** PropertyEditors also must support the ability to return a Java + ** initialization string. See the getJavaInitializationString() method for + ** details.<P> + ** + ** There are several different ways a PropertyEditor may display and control + ** editing of its value. When multiple types of input and display are + ** given by a single PropertyEditor, the RAD tool may decide which of the call + ** to support. Some RAD tools may even be text-only, so even if you support + ** a graphical set and get, it may choose the text set and get whenever it can. + ** <OL> + ** <LI>Every PropertyEditor must support getValue() and setValue(). For + ** setValue(), the component must only support it when the argument is + ** the same type that the PropertyEditor supports.</LI> + ** <LI>Every PropertyEditor must support getJavaInitializationString().</LI> + ** <LI>You may support painting the value yourself if you wish. To do this, + ** have isPaintable() return true and implement the paintValue() method. + ** This method does not determine in any way how the value is edited; + ** merely how it is displayed.</LI> + ** <LI>Let the caller of the PropertyEditor give the user a text input. Do + ** this by returning a non-null String from getAsText(). If you support + ** text input, you *must* support setAsText().</LI> + ** <LI>Give the caller a set of possible values, such as "true"/"false", that + ** the user must select from. To do this, return the list of Strings + ** from the getTags() method. The RAD tool may choose to implement the + ** user input any way it wishes, and only guarantees that setAsText() will + ** only be called with one of the Strings returned from getTags().</LI> + ** <LI>You may support a whole custom editing control by supporting + ** getCustomEditor(). To do this, return true from supportsCustomEditor() + ** and return a Component that does the job. It is the component's job, + ** or the PropertyEditor's job, to make sure that when the editor changes + ** its value, the PropertyChangeEvent is thrown.</LI> + ** </OL> + ** + ** The PropertyEditor for a particular Bean can be found using the + ** PropertyEditorManager class, which goes through a series of different + ** checks to find the appropriate class.<P> + ** + ** A PropertyChangeEvent should be thrown from the PropertyEditor whenever a + ** bound property (a property PropertyDescriptor.isBound() set to true) + ** changes. When this happens, the editor itself should *not* change the value + ** itself, but rather allow the RAD tool to call setValue() or setAsText(). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 30 June 1998 + ** @see java.beans.PropertyEditorManager + ** @see java.beans.PropertyEditorSupport + **/ + +public interface PropertyEditor { + /** Called by the RAD tool to set the value of this property for the PropertyEditor. + ** If the property type is native, it should be wrapped in the appropriate + ** wrapper type. + ** @param value the value to set this property to. + **/ + void setValue(Object value); + + /** Accessor method to get the current value the PropertyEditor is working with. + ** If the property type is native, it will be wrapped in the appropriate + ** wrapper type. + ** @return the current value of the PropertyEditor. + **/ + Object getValue(); + + + /** Set the value of this property using a String. + ** Whether or not this PropertyEditor is editing a String type, this converts + ** the String into the type of the PropertyEditor. + ** @param text the text to set it to. + ** @exception IllegalArgumentException if the String is in the wrong format or setAsText() is not supported. + **/ + void setAsText(String text) throws IllegalArgumentException; + + /** Get the value of this property in String format. + ** Many times this can simply use Object.toString().<P> + ** Return null if you do not support getAsText()/setAsText(). + ** <code>setAsText(getAsText())</code> should be valid; i.e. the stuff you spit out in + ** getAsText() should be able to go into setAsText(). + ** @return the value of this property in String format. + **/ + String getAsText(); + + /** Get a list of possible Strings which this property type can have. + ** The value of these will be used by the RAD tool to construct some sort + ** of list box or to check text box input, and the resulting String passed + ** to setAsText() should be one of these. Note, however, that like most things + ** with this mammoth, unwieldy interface, this is not guaranteed. Thus, you + ** must check the value in setAsText() anyway. + ** @return the list of possible String values for this property type. + **/ + String[] getTags(); + + + /** The RAD tool calls this to find out whether the PropertyEditor can paint itself. + ** @return true if it can paint itself graphically, false if it cannot. + **/ + boolean isPaintable(); + + /** The RAD tool calls this to paint the actual value of the property. + ** The Graphics context will have the same current font, color, etc. as the + ** parent Container. You may safely change the font, color, etc. and not + ** change them back.<P> + ** This method should do a silent no-op if isPaintable() is false. + ** @param g the Graphics context to paint on + ** @param bounds the rectangle you have reserved to work in + **/ + void paintValue(java.awt.Graphics g, java.awt.Rectangle bounds); + + + /** The RAD tool calls this to find out whether the PropertyEditor supports a custom component to edit and display itself. + ** @return true if getCustomEditor() will return a component, false if not. + **/ + boolean supportsCustomEditor(); + + /** The RAD tool calls this to grab the component that can edit this type. + ** The component may be painted anywhere the RAD tool wants to paint it-- + ** even in its own window.<P> + ** The component must hook up with the PropertyEditor and, whenever a + ** change to the value is made, fire a PropertyChangeEvent to the source.<P> + ** @return the custom editor for this property type. + **/ + java.awt.Component getCustomEditor(); + + + /** Adds a property change listener to this PropertyEditor. + ** @param listener the listener to add + **/ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** Removes a property change listener from this PropertyEditor. + ** @param listener the listener to remove + **/ + void removePropertyChangeListener(PropertyChangeListener listener); + + /** Get a Java language-specific String which could be used to create an Object + ** of the specified type. Every PropertyEditor must support this.<P> + ** The reason for this is that while most RAD tools will serialize the Beans + ** and deserialize them at runtime, some RAD tools will generate code that + ** creates the Beans. Examples of Java initialization strings would be:<P> + ** <OL> + ** <LI><CODE>2</CODE></LI> + ** <LI><CODE>"I am a String"</CODE></LI> + ** <LI><CODE>new MyObject(2, "String", new StringBuffer())</CODE></LI> + ** </OL> + ** @return the initialization string for this object in Java. + **/ + String getJavaInitializationString(); +} diff --git a/libjava/classpath/java/beans/PropertyEditorManager.java b/libjava/classpath/java/beans/PropertyEditorManager.java new file mode 100644 index 00000000000..da2a5678c5c --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditorManager.java @@ -0,0 +1,215 @@ +/* java.beans.PropertyEditorManager + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.editors.ColorEditor; +import gnu.java.beans.editors.FontEditor; +import gnu.java.beans.editors.NativeBooleanEditor; +import gnu.java.beans.editors.NativeByteEditor; +import gnu.java.beans.editors.NativeDoubleEditor; +import gnu.java.beans.editors.NativeFloatEditor; +import gnu.java.beans.editors.NativeIntEditor; +import gnu.java.beans.editors.NativeLongEditor; +import gnu.java.beans.editors.NativeShortEditor; +import gnu.java.beans.editors.StringEditor; +import gnu.java.lang.ClassHelper; + +import java.awt.Color; +import java.awt.Font; + +/** + * PropertyEditorManager is used to find property editors + * for various types (not necessarily Beans).<P> + * + * It first checks to see if the property editor is + * already registered; if it is, that property editor is + * used. Next it takes the type's classname and appends + * "Editor" to it, and searches first in the class's + * package and then in the property editor search path. + * + * <p>Default property editors are provided for:</p> + * + * <ol> + * <li>boolean, byte, short, int, long, float, and double</li> + * <li>java.lang.String</li> + * <li>java.awt.Color</li> + * <li>java.awt.Font</li> + * </ol> + * + * <p><strong>Spec Suggestion:</strong> Perhaps an editor for + * Filename or something like it should be provided. As well + * as char.</p> + * + * @author John Keiser + * @since 1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public class PropertyEditorManager +{ + static java.util.Hashtable editors = new java.util.Hashtable(); + static String[] editorSearchPath = { "gnu.java.beans.editors", + "sun.beans.editors" }; + + static + { + registerEditor(Boolean.TYPE, NativeBooleanEditor.class); + registerEditor(Byte.TYPE, NativeByteEditor.class); + registerEditor(Short.TYPE, NativeShortEditor.class); + registerEditor(Integer.TYPE, NativeIntEditor.class); + registerEditor(Long.TYPE, NativeLongEditor.class); + registerEditor(Float.TYPE, NativeFloatEditor.class); + registerEditor(Double.TYPE, NativeDoubleEditor.class); + registerEditor(String.class, StringEditor.class); + registerEditor(Color.class, ColorEditor.class); + registerEditor(Font.class, FontEditor.class); + } + + /** + * Beats me why this class can be instantiated, but there + * you have it. + */ + public PropertyEditorManager() + { + // Do nothing here + } + + /** + * Register an editor for a class. Replaces old editor + * if there was one registered before. + * + * @param editedClass the class that the property editor + * will edit. + * @param editorClass the PropertyEditor class. + */ + public static void registerEditor(Class editedClass, Class editorClass) + { + editors.put(editedClass, editorClass); + } + + /** + * Returns a new instance of the property editor for the + * specified class. + * + * @param editedClass the class that the property editor + * will edit. + * @return a PropertyEditor instance that can edit the + * specified class. + */ + public static PropertyEditor findEditor(Class editedClass) + { + try + { + Class found = (Class)editors.get(editedClass); + if(found != null) + { + return (PropertyEditor)found.newInstance(); + } + + ClassLoader contextClassLoader + = Thread.currentThread().getContextClassLoader(); + + try + { + found = Class.forName(editedClass.getName()+"Editor", true, + contextClassLoader); + registerEditor(editedClass,found); + return (PropertyEditor)found.newInstance(); + } + catch(ClassNotFoundException E) + { + } + + String appendName + = "." + + ClassHelper.getTruncatedClassName(editedClass) + + "Editor"; + synchronized(editorSearchPath) + { + for(int i=0;i<editorSearchPath.length;i++) + { + try + { + found = Class.forName(editorSearchPath[i] + appendName, + true, contextClassLoader); + registerEditor(editedClass,found); + return (PropertyEditor)found.newInstance(); + } + catch(ClassNotFoundException E) + { + } + } + } + } + catch(InstantiationException E) + { + } + catch(IllegalAccessException E) + { + } + + return null; + } + + /** + * Get the editor search path. + * As a minor departure from the spec, the default value + * for the editor search path is "gnu.java.beans.editors", + * "sun.beans.editors". + * + * @return the editor search path. + */ + public static String[] getEditorSearchPath() + { + return editorSearchPath; + } + + /** + * Set the editor search path. + * + * @param editorSearchPath the new value for the editor search path. + */ + public static void setEditorSearchPath(String[] editorSearchPath) + { + synchronized(editorSearchPath) + { + PropertyEditorManager.editorSearchPath = editorSearchPath; + } + } +} diff --git a/libjava/classpath/java/beans/PropertyEditorSupport.java b/libjava/classpath/java/beans/PropertyEditorSupport.java new file mode 100644 index 00000000000..bb68e0e3145 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditorSupport.java @@ -0,0 +1,265 @@ +/* java.beans.PropertyEditorSupport + Copyright (C) 1998, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + + +/** + * PropertyEditorSupport helps with PropertyEditors, + * implementing base functionality that they usually must + * have but which is a pain to implement. You may extend + * from this class or use it as a standalone.<P> + * + * This class does not do any painting or actual editing. + * For that, you must use or extend it. See the + * PropertyEditor class for better descriptions of what + * the various methods do. + * + * @author John Keiser + * @author Robert Schuster + * @since 1.1 + * @status updated to 1.5 + */ +public class PropertyEditorSupport implements PropertyEditor +{ + Object eventSource; + Object value; + PropertyChangeSupport pSupport; + + /** Call this constructor when you are deriving from + * PropertyEditorSupport. + * + * Using this constructor the event source is this PropertyEditorSupport + * instance itself. + * + * @since 1.5 + * @specnote this was <code>protected</code> prior to 1.5 + */ + public PropertyEditorSupport() + { + eventSource = this; + pSupport = new PropertyChangeSupport(this); + } + + /** Call this constructor when you are using + * PropertyEditorSupport as a helper object. + * + * This constructor throws a NullPointerException when <code>source</code> is <code>null</code>, + * for compatibility reasons with J2SDK 1.5.0 . + * + * @param source The source to use when firing + * property change events. + * @since 1.5 + * @specnote this was <code>protected</code> prior to 1.5 + */ + public PropertyEditorSupport(Object source) + { + // note: constructor rejects source being null for the sake of compatibility + // with official 1.5.0 implementation + if (source == null) + throw new NullPointerException("Event source must not be null."); + + eventSource = source; + pSupport = new PropertyChangeSupport(eventSource); + } + + /** Sets the current value of the property and a property change + * event is fired to all registered PropertyChangeListener instances. + * + * @param newValue The new value for the property. + */ + public void setValue(Object newValue) + { + value = newValue; + + // specification in java.beans.PropertyChangeEvent says + // that without a property name (first argument) the + // new and the old value should always be null + pSupport.firePropertyChange(null, null, null); + } + + /** Gets the current value of the property. + * + * @return the current value of the property. + */ + public Object getValue() + { + return value; + } + + /** Gets whether this object is paintable or not. + * + * @return <CODE>false</CODE> + */ + public boolean isPaintable() + { + return false; + } + + /** Paints this object. This class does nothing in + * this method. + */ + public void paintValue(java.awt.Graphics g, java.awt.Rectangle r) + { + } + + /** Gets the Java initialization String for the current + * value of the Object. This class returns gibberish or + * null (though the spec does not say which).<P> + * <STRONG>Implementation Note:</STRONG> This class + * returns the string "@$#^" to make sure the code will + * be broken, so that you will know to override it when + * you create your own property editor. + * + * @return the Java initialization string. + */ + public String getJavaInitializationString() + { + return "@$#^"; + } + + /** Gets the value as text. + * In this class, you cannot count on getAsText() doing + * anything useful, although in this implementation I + * do toString(). + * + * @return the value as text. + */ + public String getAsText() + { + return value != null ? value.toString() : "null"; + } + + /** Sets the value as text. + * In this class, you cannot count on setAsText() doing + * anything useful across implementations. + * <STRONG>Implementation Note:</STRONG> In this + * implementation it checks if the String is "null", and + * if it is, sets the value to null, otherwise it throws + * an IllegalArgumentException. + * + * @param s the text to convert to a new value. + * @exception IllegalArgumentException if the text is + * malformed. + */ + public void setAsText(String s) throws IllegalArgumentException + { + if (s.equals("null")) + setValue(null); + else + throw new IllegalArgumentException(); + } + + /** Returns a list of possible choices for the value. + * + * @return <CODE>null</CODE> + */ + public String[] getTags() + { + return null; + } + + /** Returns a custom component to edit the value. + * + * @return <CODE>null</CODE> in this class. + */ + public java.awt.Component getCustomEditor() + { + return null; + } + + /** Finds out whether this property editor supports a + * custom component to edit its value. + * + * @return <CODE>false</CODE> in this class. + */ + public boolean supportsCustomEditor() + { + return false; + } + + /** Adds a property change listener to this property editor. + * + * @param l the listener to add. + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + pSupport.addPropertyChangeListener(l); + } + + /** Removes a property change listener from this property editor. + * + * @param l the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + pSupport.removePropertyChangeListener(l); + } + + /** Notifies people that we've changed, although we don't + * tell them just how. + */ + public void firePropertyChange() + { + pSupport.firePropertyChange(null, null, null); + } + + /** Returns the bean that is used as the source of events. + * + * @return The event source object + * @since 1.5 + */ + public Object getSource() + { + return eventSource; + } + + /** Sets the bean that is used as the source of events + * when property changes occur. + * + * The event source bean is for informational purposes only + * and should not be changed by the <code>PropertyEditor</code>. + * + * @param source + * @since 1.5 + */ + public void setSource(Object source) + { + eventSource = source; + } +} diff --git a/libjava/classpath/java/beans/PropertyVetoException.java b/libjava/classpath/java/beans/PropertyVetoException.java new file mode 100644 index 00000000000..1f0399b4bfb --- /dev/null +++ b/libjava/classpath/java/beans/PropertyVetoException.java @@ -0,0 +1,85 @@ +/* PropertyVetoException.java -- thrown to veto a proposed property change + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * PropertyVetoException is thrown when a VetoableChangeListener doesn't + * like the proposed change. + * + * @author John Keiser + * @see VetoableChangeListener + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyVetoException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 129596057694162164L; + + /** + * The vetoed change. + * + * @serial the event that was vetoed + */ + private final PropertyChangeEvent evt; + + /** + * Instantiate this exception with the given message and property change. + * + * @param msg the reason for the veto + * @param changeEvent the PropertyChangeEvent that was thrown + */ + public PropertyVetoException(String msg, PropertyChangeEvent changeEvent) + { + super(msg); + evt = changeEvent; + } + + /** + * Get the PropertyChange event that was vetoed. + * + * @return the vetoed change + */ + public PropertyChangeEvent getPropertyChangeEvent() + { + return evt; + } +} diff --git a/libjava/classpath/java/beans/SimpleBeanInfo.java b/libjava/classpath/java/beans/SimpleBeanInfo.java new file mode 100644 index 00000000000..cfb96048498 --- /dev/null +++ b/libjava/classpath/java/beans/SimpleBeanInfo.java @@ -0,0 +1,139 @@ +/* java.beans.SimpleBeanInfo + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.awt.Image; +import java.awt.Toolkit; + +/** + ** SimpleBeanInfo is a class you may extend to more easily + ** provide select information to the Introspector. It + ** implements all of the methods in BeanInfo by returning + ** null and forces the Introspector to behave exactly as + ** if there were no BeanInfo class at all (Introspecting + ** everything).<P> + ** + ** Overriding one or two of these functions + ** to give explicit information on only those things you + ** wish to give explicit information is perfectly safe, + ** and even desirable.<P> + ** + ** See the BeanInfo class for information on what the + ** various methods actually do. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanInfo + **/ + +public class SimpleBeanInfo implements BeanInfo { + /** Force Introspection of the general bean info. + ** @return <CODE>null</CODE>. + **/ + public BeanDescriptor getBeanDescriptor() { + return null; + } + + /** Force Introspection of the events this Bean type + ** fires. + ** @return <CODE>null</CODE> + **/ + public EventSetDescriptor[] getEventSetDescriptors() { + return null; + } + + /** Say that there is no "default" event set. + ** @return <CODE>-1</CODE>. + **/ + public int getDefaultEventIndex() { + return -1; + } + + /** Force Introspection of the Bean properties. + ** @return <CODE>null</CODE>. + **/ + public PropertyDescriptor[] getPropertyDescriptors() { + return null; + } + + /** Say that there is no "default" property. + ** @return <CODE>-1</CODE>. + **/ + public int getDefaultPropertyIndex() { + return -1; + } + + /** Force Introspection of the Bean's methods. + ** @return <CODE>null</CODE>. + **/ + public MethodDescriptor[] getMethodDescriptors() { + return null; + } + + /** Tell the Introspector to go look for other BeanInfo + ** itself. + ** @return <CODE>null</CODE>. + **/ + public BeanInfo[] getAdditionalBeanInfo() { + return null; + } + + /** Say that this Bean has no icons. + ** @param iconType the type of icon + ** @return <CODE>null</CODE>. + **/ + public Image getIcon(int iconType) { + return null; + } + + /** Helper method to load an image using the Bean class + ** getResource() method on the BeanInfo class (using + ** getClass(), since you'll extend this class to get + ** the BeanInfo). Basically it's assumed that the Bean + ** and its BeanInfo are both loaded by the same + ** ClassLoader, generally a reasonable assumption. + ** @param location the URL relative + ** @return the Image in question. + **/ + public Image loadImage(String location) { + return Toolkit.getDefaultToolkit().getImage(getClass().getResource(location)); + } +} + diff --git a/libjava/classpath/java/beans/Statement.java b/libjava/classpath/java/beans/Statement.java new file mode 100644 index 00000000000..01f86dd286f --- /dev/null +++ b/libjava/classpath/java/beans/Statement.java @@ -0,0 +1,326 @@ +/* java.beans.Statement + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * class Statement + * + * A Statement captures the execution of an object method. It stores + * the object, the method to call, and the arguments to the method and + * provides the ability to execute the method on the object, using the + * provided arguments. + * + * @since 1.4 + */ +public class Statement +{ + private Object target; + private String methodName; + private Object[] arguments; + + // One or the other of these will get a value after execute is + // called once, but not both. + private transient Method method; + private transient Constructor ctor; + + /** + * Constructs a statement representing the invocation of + * object.methodName(arg[0], arg[1], ...); + * + * @param target The object to invoke the method on. + * @param methodName The object method to invoke. + * @param arguments An array of arguments to pass to the method. + */ + public Statement(Object target, String methodName, Object[] arguments) + { + this.target = target; + this.methodName = methodName; + this.arguments = arguments; + } + + /** + * Execute the statement. + * + * Finds the specified method in the target object and calls it with + * the arguments given in the constructor. + * + * The most specific method according to the JLS(15.11) is used when + * there are multiple methods with the same name. + * + * Execute performs some special handling for methods and + * parameters: + * + * Static methods can be executed by providing the class as a + * target. + * + * The method name new is reserved to call the constructor + * new() will construct an object and return it. Not useful unless + * an expression :-) + * + * If the target is an array, get and set as defined in + * java.util.List are recognized as valid methods and mapped to the + * methods of the same name in java.lang.reflect.Array. + * + * The native datatype wrappers Boolean, Byte, Character, Double, + * Float, Integer, Long, and Short will map to methods that have + * native datatypes as parameters, in the same way as Method.invoke. + * However, these wrappers also select methods that actually take + * the wrapper type as an argument. + * + * The Sun spec doesn't deal with overloading between int and + * Integer carefully. If there are two methods, one that takes an + * Integer and the other taking an int, the method chosen is not + * specified, and can depend on the order in which the methods are + * declared in the source file. + * + * @throws Exception if an exception occurs while locating or + * invoking the method. + */ + public void execute() throws Exception + { + doExecute(); + } + + private static Class wrappers[] = + { + Boolean.class, Byte.class, Character.class, Double.class, Float.class, + Integer.class, Long.class, Short.class + }; + + private static Class natives[] = + { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Double.TYPE, Float.TYPE, + Integer.TYPE, Long.TYPE, Short.TYPE + }; + + // Given a wrapper class, return the native class for it. For + // example, if c is Integer, Integer.TYPE is returned. + private Class unwrap(Class c) + { + for (int i = 0; i < wrappers.length; i++) + if (c == wrappers[i]) + return natives[i]; + return null; + } + + // Return true if all args can be assigned to params, false + // otherwise. Arrays are guaranteed to be the same length. + private boolean compatible(Class[] params, Class[] args) + { + for (int i = 0; i < params.length; i++) + { + // Treat Integer like int if appropriate + Class nativeType = unwrap(args[i]); + if (nativeType != null && params[i].isPrimitive() + && params[i].isAssignableFrom(nativeType)) + continue; + if (params[i].isAssignableFrom(args[i])) + continue; + + return false; + } + return true; + } + + /** + * Return true if the method arguments in first are more specific + * than the method arguments in second, i.e. all args in first can + * be assigned to those in second. + * + * A method is more specific if all parameters can also be fed to + * the less specific method, because, e.g. the less specific method + * accepts a base class of the equivalent argument for the more + * specific one. + * + * @param first a <code>Class[]</code> value + * @param second a <code>Class[]</code> value + * @return a <code>boolean</code> value + */ + private boolean moreSpecific(Class[] first, Class[] second) + { + for (int j=0; j < first.length; j++) + { + if (second[j].isAssignableFrom(first[j])) + continue; + return false; + } + return true; + } + + final Object doExecute() throws Exception + { + Class klazz = (target instanceof Class) + ? (Class) target : target.getClass(); + Object args[] = (arguments == null) ? new Object[0] : arguments; + Class argTypes[] = new Class[args.length]; + for (int i = 0; i < args.length; i++) + argTypes[i] = args[i].getClass(); + + if (target.getClass().isArray()) + { + // FIXME: invoke may have to be used. For now, cast to Number + // and hope for the best. If caller didn't behave, we go boom + // and throw the exception. + if (methodName.equals("get") && argTypes.length == 1) + return Array.get(target, ((Number)args[0]).intValue()); + if (methodName.equals("set") && argTypes.length == 2) + { + Object obj = Array.get(target, ((Number)args[0]).intValue()); + Array.set(target, ((Number)args[0]).intValue(), args[1]); + return obj; + } + throw new NoSuchMethodException("No matching method for statement " + toString()); + } + + // If we already cached the method, just use it. + if (method != null) + return method.invoke(target, args); + else if (ctor != null) + return ctor.newInstance(args); + + // Find a matching method to call. JDK seems to go through all + // this to find the method to call. + + // if method name or length don't match, skip + // Need to go through each arg + // If arg is wrapper - check if method arg is matchable builtin + // or same type or super + // - check that method arg is same or super + + if (methodName.equals("new") && target instanceof Class) + { + Constructor ctors[] = klazz.getConstructors(); + for (int i = 0; i < ctors.length; i++) + { + // Skip methods with wrong number of args. + Class ptypes[] = ctors[i].getParameterTypes(); + System.out.println("ptypeslen = " + ptypes.length); + System.out.println("ptypes = " + ptypes); + System.out.println("ctor = " + ctors[i].getName()); + for (int j=0; j < ptypes.length; j++) { + System.out.println("param = " + ptypes[i].getName()); + + } + + + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (ctor == null) + { + ctor = ctors[i]; + continue; + } + Class mptypes[] = ctor.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + ctor = ctors[i]; + } + if (ctor == null) + throw new InstantiationException("No matching constructor for statement " + toString()); + return ctor.newInstance(args); + } + + Method methods[] = klazz.getMethods(); + + for (int i = 0; i < methods.length; i++) + { + // Skip methods with wrong name or number of args. + if (!methods[i].getName().equals(methodName)) + continue; + Class ptypes[] = methods[i].getParameterTypes(); + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (method == null) + { + method = methods[i]; + continue; + } + Class mptypes[] = method.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + method = methods[i]; + } + if (method == null) + throw new NoSuchMethodException("No matching method for statement " + toString()); + return method.invoke(target, args); + } + + + + /** Return the statement arguments. */ + public Object[] getArguments() { return arguments; } + + /** Return the statement method name. */ + public String getMethodName() { return methodName; } + + /** Return the statement object. */ + public Object getTarget() { return target; } + + /** Return a string representation. */ + public String toString() + { + String result = target.getClass().getName() + "." + methodName + "("; + String sep = ""; + for (int i = 0; i < arguments.length; i++) + { + result = result + sep + arguments[i].getClass().getName(); + sep = ", "; + } + result = result + ")"; + return result; + } +} diff --git a/libjava/classpath/java/beans/TODO b/libjava/classpath/java/beans/TODO new file mode 100644 index 00000000000..08e1d25ee5f --- /dev/null +++ b/libjava/classpath/java/beans/TODO @@ -0,0 +1,4 @@ +- add AppletStub and AppletContext to java.beans.Beans.instantiate(). +- make Introspector more efficient. +- basic Introspection tests are in, but more tests are probably in order. +- 1.2 support (waiting on java.lang.Package, mainly) diff --git a/libjava/classpath/java/beans/VetoableChangeListener.java b/libjava/classpath/java/beans/VetoableChangeListener.java new file mode 100644 index 00000000000..5107954b033 --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListener.java @@ -0,0 +1,73 @@ +/* VetoableChangeListener.java -- listen for a change which can be vetoed + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * VetoableChangeListener allows a class to monitor proposed changes to + * properties of a Bean and, if desired, prevent them from occurring. A + * vetoableChange() event will be fired <em>after</em> the property change has + * been requested, but before it is permanent. If any listener rejects the + * change by throwing the PropertyChangeException, a new vetoableChange() + * event will be fired to all listeners who received a vetoableChange() event + * in the first place, informing them to revert back to the old value. Thus, + * the listener that threw the exception the first time should be prepared + * to rethrow it the second time. The value, of course, never actually changed. + * + * <p><strong>Note:</strong> This class may not be reliably used to determine + * whether a property has actually changed. Use the PropertyChangeListener + * interface for that instead. + * + * @author John Keiser + * @see java.beans.PropertyChangeListener + * @see java.beans.VetoableChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface VetoableChangeListener extends EventListener +{ + /** + * Fired before a Bean's property changes. + * + * @param e the change (containing the old and new values) + * @throws PropertyVetoException if the change is vetoed by the listener + */ + void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException; +} // interface VetoableChangeListener diff --git a/libjava/classpath/java/beans/VetoableChangeListenerProxy.java b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java new file mode 100644 index 00000000000..56ca5a38c7f --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* VetoableChangeListenerProxy.java -- adds a name to a vetoable listener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to <code>VetoableChangeListener</code> - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class VetoableChangeListenerProxy extends EventListenerProxy + implements VetoableChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * VetoableChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public VetoableChangeListenerProxy(String propertyName, + VetoableChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + * @throws PropertyVetoException if the change is vetoed by the listener + */ + public void vetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + // Note: Sun does not filter, under the assumption that since + // VetoableChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((VetoableChangeListener) getListener()).vetoableChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class VetoableChangeListenerProxy diff --git a/libjava/classpath/java/beans/VetoableChangeSupport.java b/libjava/classpath/java/beans/VetoableChangeSupport.java new file mode 100644 index 00000000000..dce8dffd341 --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeSupport.java @@ -0,0 +1,530 @@ +/* VetoableChangeSupport.java -- support to manage vetoable change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * VetoableChangeSupport makes it easy to fire vetoable change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class VetoableChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5090210921595982017L; + + /** + * Maps property names (String) to named listeners (VetoableChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int vetoableChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered vetoable listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the <code>children</code> hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a VetoableChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public VetoableChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a VetoableChangeListener to the list of global listeners. All + * vetoable change events will be sent to this listener. The listener add + * is not unique: that is, <em>n</em> adds with the same listener will + * result in <em>n</em> events being sent to that listener for every + * vetoable change. Adding a null listener may cause a NullPointerException + * down the road. This method will unwrap a VetoableChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add + */ + public synchronized void addVetoableChangeListener(VetoableChangeListener l) + { + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + addVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a VetoableChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If <code>add()</code> has been called multiple times for a + * particular listener, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removeVetoableChangeListener(VetoableChangeListener l) + { + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + removeVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered vetoable change listeners. Those that + * were registered under a name will be wrapped in a + * <code>VetoableChangeListenerProxy</code>, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see VetoableChangeListenerProxy + * @since 1.4 + */ + public synchronized VetoableChangeListener[] getVetoableChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((VetoableChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new VetoableChangeListenerProxy + (name, (VetoableChangeListener) v.get(j))); + } + } + return (VetoableChangeListener[]) + list.toArray(new VetoableChangeListener[list.size()]); + } + + /** + * Adds a VetoableChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, <em>n</em> adds on a particular + * property for a particular listener will result in <em>n</em> events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all vetoable changes, and then you register on a particular property, + * you will receive change events for that property twice. Adding a null + * listener may cause a NullPointerException down the road. This method + * will unwrap a VetoableChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + * @throws NullPointerException if propertyName is null + */ + public synchronized void addVetoableChangeListener(String propertyName, + VetoableChangeListener l) + { + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + VetoableChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + { + s = new VetoableChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a VetoableChangeListener from listening to a specific property. + * If <code>add()</code> has been called multiple times for a particular + * listener on a property, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removeVetoableChangeListener(String propertyName, VetoableChangeListener l) + { + if (children == null) + return; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all vetoable change listeners registered under the + * given property name. If there are no registered listeners, this returns + * an empty array. + * + * @return the array of registered listeners + * @throws NullPointerException if propertyName is null + * @since 1.4 + */ + public synchronized VetoableChangeListener[] + getVetoableChangeListeners(String propertyName) + { + if (children == null) + return new VetoableChangeListener[0]; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return new VetoableChangeListener[0]; + return (VetoableChangeListener[]) + s.listeners.toArray(new VetoableChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. If the change is vetoed, a new event is fired to notify + * listeners about the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + Object oldVal, Object newVal) + throws PropertyVetoException + { + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, int oldVal, int newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + new Integer(oldVal), + new Integer(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + boolean oldVal, boolean newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. If the change is vetoed, a new event + * is fired to notify listeners about the rollback before the exception is + * thrown. + * + * @param event the event to fire + * @throws NullPointerException if event is null + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= limit) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + throw e; + } + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + VetoableChangeSupport s + = (VetoableChangeSupport) h.get(event.propertyName); + if (s != null) + { + Vector v1 = s.listeners; // Be thread-safe. + int i = v1 == null ? 0 : v1.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + i = v1.size(); + while (--i >= limit) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + throw e; + } + } + } + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return <code>true</code> if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + * @throws NullPointerException if propertyName is null + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if vetoableChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + VetoableChangeListener l = (VetoableChangeListener) s.readObject(); + while (l != null) + { + addVetoableChangeListener(l); + l = (VetoableChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + VetoableChangeSupport vcs = (VetoableChangeSupport) e.getValue(); + if (vcs.listeners == null) + vcs.listeners = new Vector(); + if (vcs.children != null) + vcs.listeners.addAll + (Arrays.asList(vcs.getVetoableChangeListeners(name))); + if (vcs.listeners.size() == 0) + iter.remove(); + else + vcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class VetoableChangeSupport diff --git a/libjava/classpath/java/beans/Visibility.java b/libjava/classpath/java/beans/Visibility.java new file mode 100644 index 00000000000..428f3a2073b --- /dev/null +++ b/libjava/classpath/java/beans/Visibility.java @@ -0,0 +1,85 @@ +/* java.beans.Visibility + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * Visibility is an interface a Bean may implement so that the environment + * can tell the Bean whether there is a GUI or not, and so that the Bean + * can tell the environment whether it needs one or can run without one. + * <P> + * + * Sun decided not to use standard Introspection patterns so that these + * methods did not get included when the Introspector made its sweep on + * the class. + * + * @author John Keiser + * @since JDK1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public interface Visibility { + /** + * Tells whether the Bean can run without a GUI or not. + * @return false if Bean can run without a GUI, else true. + */ + boolean needsGui(); + + /** + * Tells whether Bean is trying not to use the GUI. + * If needsGui() is true, this method should always return false. + * @return true if definitely not using GUI, otherwise false. + */ + boolean avoidingGui(); + + /** + * Tells the Bean not to use GUI methods. + * If needsGUI() is false, then after this method is called, + * avoidingGui() should return true. + */ + void dontUseGui(); + + /** + * Tells the Bean it may use the GUI. + * The Bean is not required to use the GUI in this case, it is + * merely being <EM>permitted</EM> to use it. If needsGui() is + * false, avoidingGui() may return true or false after this method + * is called. + */ + void okToUseGui(); +} diff --git a/libjava/classpath/java/beans/XMLDecoder.java b/libjava/classpath/java/beans/XMLDecoder.java new file mode 100644 index 00000000000..238fd6bed91 --- /dev/null +++ b/libjava/classpath/java/beans/XMLDecoder.java @@ -0,0 +1,307 @@ +/* java.beans.XMLDecoder -- + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.decoder.DefaultExceptionListener; +import gnu.java.beans.decoder.PersistenceParser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * The XMLDecoder reads XML data that is structured according to + * <a href="http://java.sun.com/products/jfc/tsc/articles/persistence3/javabeans.dtd">this</a> DTD + * and creates objects according to the content. Usually such data is generated using the + * {@link XMLEncoder} class. + * <p> + * An example XML document might look like this: + * <code> + * <java> + * <string>Hello World</string> + * <int>200</int> + * </java> + * </code> + * <p>To read the <code>String</code> and the <code>Integer</code> instance the following can be used (assume + * the XML data can be obtained from the InputStream):</p> + * <code> + * XMLDecoder decoder = new XMLDecoder(inputStreamContainingXMLData); + * String message = (String) decoder.readObject(); + * Integer number = (Integer) decoder.readObject(); + * </code> + * <p>Besides this basic functionality the <code>XMLDecoder</code> has some more features that might come + * handy in certain situations:</p> + * <p>An owner object can be set using the <code>setOwner</code> method which can then be accessed when + * decoding. This feature is only useful if the XML data is aware of the owner object. Such data may + * look like this (assume that the owner object is a JFrame instance):</p> + * <code> + * <java> + * <void method="getOwner"> + * <void method="setVisible"> + * <boolean>true<boolean> + * </void> + * </void> + * </java> + * </code> + * This accesses the <code>JFrame</code> and makes it visible using the <code>setVisible</code> method. + * <p>Please note that changing the owner <b>after</b> the having read the first object has no effect, + * because all object have been decoded then.</p> + * <p>If the <code>XMLDecoder</code> is created with no {@link ExceptionListener} instance a default one + * is used that prints an error message to <code>System.err</code> whenever a recoverable exception + * is thrown. Recovarable exceptions occur when the XML data cannot be interpreted correctly (e.g + * unknown classes or methods, invocation on null, ...). In general be very careful when the + * <code>XMLDecoder</code> provoked such exceptions because the resulting object(s) may be in an + * undesirable state.</p> + * <p>Note that changing the ExceptionListener instance after <code>readObject</code> has been called + * once has no effect because the decoding is completed then.</p> + * <p>At last one can provide a specific <code>ClassLoader</code> which is then used when <code>Class</code> + * objects are accessed. See {@link java.lang.Class#forName(String, boolean, ClassLoader)} for details + * on this.</p> + * <p>Note: If the <code>InputStream</code> instance given to any of the constructors is <code>null</code> + * the resulting <code>XMLDecoder</code> will be silently (without any exception) useless. Each call + * to <code>readObject</code> will return <code>null</code> and never throws an + * <code>ArrayIndexOutOfBoundsException</code>.</p> + * + * @author Robert Schuster + * @since 1.4 + * @status updated to 1.5 + */ +public class XMLDecoder +{ + private Object owner; + + private ExceptionListener exceptionListener; + + private InputStream inputStream; + + private boolean isStreamClosed; + + private ClassLoader classLoader; + + private Iterator iterator; + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader, a default ExceptionListener + * and no owner object is used. + * + * @param in InputStream to read XML data from. + */ + public XMLDecoder(InputStream in) + { + this(in, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader and a default ExceptionListener + * is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + */ + public XMLDecoder(InputStream in, Object owner) + { + this(in, owner, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param exceptionListener ExceptionListener instance to which exception notifications are send. + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener exceptionListener) + { + this( + in, + owner, + exceptionListener, + Thread.currentThread().getContextClassLoader()); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param exceptionListener ExceptionListener instance to which exception notifications are send. + * @param cl ClassLoader instance that is used for calls to <code>Class.forName(String, boolean, ClassLoader)</code> + * @since 1.5 + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener listener, + ClassLoader cl) + { + // initially here was a check for the validity of the InputStream argument but some + // great engineers decided that this API should silently discard this and behave rather + // odd: readObject will always return null ... + inputStream = in; + + setExceptionListener(listener); + + // validity of this object is checked in Class.forName() and therefore may be null + classLoader = cl; + + this.owner = owner; + } + + /** Closes the stream associated with this decoder. This should be done after having read all + * decoded objects. + * <p>See the description of the {@link #readObject()} for the effect caused by <code>close</code>.</p> + */ + public void close() + { + if (isStreamClosed) + { + return; + } + + try + { + inputStream.close(); + isStreamClosed = true; + } + catch (IOException e) + { + // bad style forced by original API design ... + } + } + + /** Returns the ExceptionListener instance associated with this decoder. + * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> + * + * @return Current ExceptionListener of the decoder. + */ + public ExceptionListener getExceptionListener() + { + return exceptionListener; + } + + /** Returns the owner object of the decoder. This method is usually called + * from within the parsed XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> + * + * @return The owner object of this decoder. + */ + public Object getOwner() + { + return owner; + } + + /** Returns the next available decoded object. + * <p>Note that the actual decoding takes place when the method is called for the first time.</p> + * <p>If the <code>close</code> method was already called a <code>NoSuchElementException</code> + * is thrown.</p> + * <p>If the InputStream instance used in the constructors was <code>null</code> this method + * will always return <code>null</code> itself.</p> + * + * @return The next object in a sequence decoded from XML data. + * @throws ArrayIndexOutOfBoundsException When no more objects are available. + */ + public Object readObject() throws ArrayIndexOutOfBoundsException + { + // note: the RI does it this way ... + if(inputStream == null) { + return null; + } + + // note: the original API documentation says nothing on what to do + // when the stream was closed before readObject is called but it actually + // throws a NoSuchElementException - this behaviour is imitated here + if (isStreamClosed) + { + throw new NoSuchElementException("Cannot read any objects - XMLDecoder was already closed."); + } + + // creates the PersistenceParser (doing the parsing and decoding) and returns its + // Iterator on first invocation + if (iterator == null) + { + iterator = + new PersistenceParser( + inputStream, + exceptionListener, + classLoader, + this) + .iterator(); + } + + // note: done according to the official documentation + if (!iterator.hasNext()) + { + throw new ArrayIndexOutOfBoundsException("No more objects available from this XMLDecoder."); + } + + // returns just the next object if there was no problem + return iterator.next(); + } + + /** Sets the ExceptionListener instance to which notifications of exceptions are send + * while parsing the XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the ExceptionListener.</p> + * + * @param listener + */ + public void setExceptionListener(ExceptionListener listener) + { + // uses a default implementation when null + if (listener == null) + { + listener = new DefaultExceptionListener(); + } + exceptionListener = listener; + } + + /** Sets the owner object which can be accessed from the parsed XML data. + * <p>See the description of {@link XMLDecoder} class for more information on the owner object.</p> + * + * @param newOwner + */ + public void setOwner(Object newOwner) + { + owner = newOwner; + } + +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContext.java b/libjava/classpath/java/beans/beancontext/BeanContext.java new file mode 100644 index 00000000000..3d1be7fc8e9 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContext.java @@ -0,0 +1,272 @@ +/* java.beans.beancontext.BeanContext + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.DesignMode; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; + +/** + * Acts as a container for sub-beans and as a sub-bean, + * so that an entire hierarchy of beans can be made up of + * <code>BeanContext</code>s. + * <P> + * + * Since I can't sprinkle the <code>Collections</code> interface + * documentation with special information for <code>BeanContext</code> + * implementors, I'll have to document special requirements for + * implementors of those functions here. + * <P> + * + * <code><strong>add()</strong></code> or <code>addAll()</code>: + * <br> + * <OL> + * <LI> + * May add any <code>Object</code> into the hierarchy as well as a + * <code>BeanContextChild</code>, <code>BeanContext</code> or + * <code>BeanContextProxy</code> object. + * This way, any Bean can be in the hierarchy. + * </LI> + * <LI> + * Must synchronize on <code>BeanContext.globalHierarchyLock</code>. + * </LI> + * <LI> + * Don't add the <code>Object</code> if it's already there (only once + * per <code>BeanContext</code>). + * </LI> + * <LI> + * If it is a <code>BeanContextChild</code> implementor, call + * <code>setBeanContext()</code> on it. If it's a + * <code>BeanContextProxy</code> implementor, call + * <code>getBeanContextProxy().setBeanContext()</code> on it. + * If <code>setBeanContext()</code> vetoes the change, back out + * all changes so far and throw <code>IllegalStateException</code>. + * </LI> + * <LI> + * If it (or its proxy) implements <code>Visibility</code>, call + * <code>dontUseGui()</code> or <code>okToUseGui()</code> on it, + * depending on whether you (the <code>BeanContext</code>) feel like + * allowing it to use the GUI or not. + * </LI> + * <LI> + * If it implements <code>BeanContextChild</code> or + * <code>BeanContextProxy</code>, register yourself (the + * <code>BeanContext</code>) as both a + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> on the "beanContext" + * property (it may also add itself on any other properties it wishes + * to). + * </LI> + * <LI> + * If it is a listener or event source that you (the + * <code>BeanContext</code>) are interested in, you may register + * yourself to it or register it to you. + * </LI> + * <LI> + * Fire a <code>java.beans.beancontext.BeanContextMembershipEvent</code> + * before exiting. <code>addAll()</code> should wait until everything + * is done changing before firing the event (or events) so that if a + * failure occurs, the backing-out process can proceed without any + * events being fired at all. + * </LI> + * </OL> + * <P> + * + * <code><strong>remove()</strong></code> or <code>removeAll()</code>: + * <br> + * <OL> + * <LI> + * Must synchronize on <code>BeanContext.globalHierarchyLock</code>. + * </LI> + * <LI> + * If the specified <code>Object</code> is not a child of this + * <code>BeanContext</code>, just exit without performing any actions. + * </LI> + * <LI> + * Remove the <code>Object</code> from your collection of children. + * </LI> + * <LI> + * If it is a <code>BeanContextChild</code> implementor, call + * <code>setBeanContext(null)</code> on it. If it's a + * <code>BeanContextProxy</code> implementor, call + * <code>getBeanContextProxy().setBeanContext(null)</code> on it. + * If <code>setBeanContext()</code> vetoes the change, back out + * all changes so far and throw <code>IllegalStateException</code>. + * </LI> + * <LI> + * If you registered the <code>Object</code> to listen to you or + * registered yourself as a listener on the <code>Object</code> during + * <code>add()</code> or <code>addAll()</code>, undo the registration + * bycalling the appropriate <code>removeListener()</code> method. + * </LI> + * <LI> + * Fire a <code>java.beans.beancontext.BeanContextMembershipEvent</code> + * before exiting. <code>removeAll()</code> should wait until + * everything is done changing before firing the event (or events) so + * that if a failure occurs, the backing-out process can proceed + * without any events being fired at all. + * </LI> + * </OL> + * <P> + * + * <code>addAll()</code>, <code>removeAll()</code>, + * <code>retainAll()</code> and <code>clear()</code> do not need to be + * implemented, but may be if so desired. + * <P> + * + * Similarly, <code>Visibility</code> and <code>DesignMode</code> methods + * should propagate changed values to children that implement interfaces + * of the same name. + * <P> + * + * A hierarchy of beans is mainly useful so that different sets of beans + * can be established, each with their own set of resources. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContext + extends Collection, BeanContextChild, Visibility, DesignMode { + + /** + * The global lock on changing any BeanContext hierarchy. + * It kinda sucks that there is only one lock, since there can be + * multiple hierarchies. Oh well, I didn't design, I just code. + * <P> + * + * Methods that must (or do) synchronize on the global lock: + * <BR> + * <UL> + * <LI> + * Implementors of <CODE>BeanContext.add()</CODE> and <code>addAll()</code> + * </LI> + * </UL> + * @fixme fill in the rest of the methods which use the global lock. + */ + Object globalHierarchyLock = new Object(); + + /** + * Instantiate a Bean using this Bean's <code>ClassLoader</code> + * and this <code>BeanContext</code> as the parent. + * <P> + * + * This method exists mainly so that <code>BeanContext</code> + * implementations can perform extra actions on Beans that are + * created within them. + * + * @param beanName the name of the bean to instantiate + * @return the created Bean + * + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String) + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String,java.lang.BeanContext) + * @exception IOException if there is an I/O problem during + * instantiation. + * @exception ClassNotFoundException if a serialized Bean's class + * is not found. + */ + Object instantiateChild(String beanName) + throws IOException, + ClassNotFoundException; + + /** + * Get a resource. The <code>BeanContext</code> will typically + * call <code>ClassLoader.getResource()</code>, but may do it any + * way it wants to. This allows a <code>BeanContext</code> to + * have its own set of resources separate from the rest of the + * system. + * <P> + * + * Beans should call this method on their parent rather than the + * associated <code>ClassLoader</code> method. + * <P> + * + * I am assuming, but am not entirely sure, that if a + * <code>BeanContext</code> cannot find a resource, its + * responsibility is to call the <code>getResource</code> method + * of its parent <code>BeanContext</code>. + * + * @return a URL to the requested resource. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResource(java.lang.String) + */ + URL getResource(String resourceName, BeanContextChild requestor); + + /** + * Get a resource as a stream. The <code>BeanContext</code> will + * typically call <code>ClassLoader.getResourceAsStream()</code>, + * but may do it any way it wants to. This allows a + * <code>BeanContext</code>'s children to have their own set of + * resources separate from the rest of the system. + * <P> + * + * Beans should call this method on their parent rather than the + * associated <code>ClassLoader</code> method. + * <P> + * + * I am assuming, but am not entirely sure, that if a + * <code>BeanContext</code> cannot find a resource, its + * responsibility is to call the <code>getResourceAsStream</code> + * method of its parent <code>BeanContext</code>. + * + * @return the requested resource as a stream. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) + */ + InputStream getResourceAsStream(String resourceName, BeanContextChild requestor); + + /** + * Add a listener on changes to the membership of this + * <code>BeanContext</code> object. + * @param listener the listener to add. + */ + void addBeanContextMembershipListener(BeanContextMembershipListener listener); + + /** + * Remove a listener on changes to the membership of this + * <code>BeanContext</code> object. + * @param listener the listener to remove. + */ + void removeBeanContextMembershipListener(BeanContextMembershipListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChild.java b/libjava/classpath/java/beans/beancontext/BeanContextChild.java new file mode 100644 index 00000000000..d1115efac85 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChild.java @@ -0,0 +1,174 @@ +/* java.beans.beancontext.BeanContextChild + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +/** + * Beans implement this to get information about the execution environment and + * its services and to be placed in the hierarchy. + * <P> + * + * The difference between a <code>BeanContext</code> and a + * <code>BeanContextChild</code>, mainly, is that a + * <code>BeanContext</code> may be a parent. + * <P> + * + * <code>BeanContextChild</code> instances will be serialized at some + * point in their life, but you need to make sure your bean context does + * not contain a serializable reference (directly or indirectly) to the + * parent <code>BeanContext</code>, to any of the other + * <code>BeanContext</code>s in the tree, or to any resources obtained + * via the <code>BeanContextServices</code> interface. One way to do this + * is to mark any fields that contain such references as + * <code>transient</code>. Another way is to use a custom serializer. + * <P> + * + * If you do not do this, when the <code>BeanContext</code> is serialized, + * all the other <code>BeanContext</code>s and other unnecessary things + * will be serialized along with it. + * <P> + * + * Before dying, a <code>BeanContextChild</code> should call + * <code>getBeanContext().remove(this)</code> to detach from the + * hierarchy and exit cleanly. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContext + */ + +public interface BeanContextChild { + /** + * Set the parent <code>BeanContext</code>. + * <P> + * + * This method is called from <code>BeanContext.add()</code> and + * should not be called directly. + * <P> + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + * <P> + * + * When this Object is being removed from the current + * <code>BeanContext</code>, <code>setBeanContext()</code> will + * receive the parameter <code>null</code>. + * <P> + * + * When being removed from the current <code>BeanContext</code>, + * it is the <code>BeanContextChild</code>'s responsibility to + * release all services it has obtained. + * <P> + * + * This change should generate <code>PropertyChangeEvent</code> + * and <code>VetoableChangeEvent</code>s with the property name + * "beanContext". If the change is vetoed, it must re-throw the + * exception and not change anything. In this way, the parent + * <code>BeanContextChild</code>, who has registered himself with + * you, will have a chance to remove this child from its + * collection. + * <P> + * + * If the Bean does not wish to change the parent or be removed + * from one, it may throw the <code>PropertyVetoException</code>. + * If you veto a <code>setBeanContext(null)</code> call, then you + * should try your hardest to remedy whatever problem is keeping + * you from being removed from the <code>BeanContext</code> so + * that you can <em>not</em> veto it the next time. + * Otherwise, nasty pathological recursion stuff could occur in + * certain situations. + * <P> + * + * If you do veto the change, you must first back out any changes + * you made prior to the veto. Best not to make any such changes + * prior to the veto in the first place. + * <P> + * + * This method is called from <code>BeanContext.add()</code> and + * should not be called directly. + * + * @param parent the new parent for the <code>BeanContextChild</code>, + * or <code>null</code> to signify removal from a tree. + * @exception PropertyVetoException if the + * <code>BeanContextChild</code> implementor does not + * wish to have its parent changed. + */ + void setBeanContext(BeanContext parent) + throws PropertyVetoException; + + /** + * Get the parent <code>BeanContext</code>. + * @return the parent <code>BeanContext</code>. + */ + BeanContext getBeanContext(); + + /** + * Add a listener that will be notified when a specific property changes. + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addPropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removePropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addVetoableChangeListener(String prop, VetoableChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removeVetoableChangeListener(String prop, VetoableChangeListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java new file mode 100644 index 00000000000..a8d6e34045e --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextChildComponentProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Component; + +/** + * Interface for <code>BeanContextChild</code>s which wish to associate an + * AWT component with them. The proxy is provided because the + * <code>addPropertyChangeListener()</code> method would conflict with + * <code>Component</code> if you tried to extend. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextChildComponentProxy { + /** + * Get the <code>Component</code> associated with this <code>BeanContextChild</code>. + * @return the <code>Component</code> associated with this + * <code>BeanContextChild</code>. + */ + Component getComponent(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java new file mode 100644 index 00000000000..4444ad71377 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java @@ -0,0 +1,381 @@ +/* java.beans.beancontext.BeanContextChildSupport + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.Serializable; + +/** + * Support for creating a <code>BeanContextChild</code>. + * This class contains the most common implementations of the methods in + * the <code>BeanContextChild</code> + * + * @specnote This class is not very well specified. I had to "fill in the + * blanks" in most places with what I thought was reasonable + * behavior. If there are problems, let me know. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextChild + */ +public class BeanContextChildSupport + implements BeanContextChild, BeanContextServicesListener, Serializable +{ + static final long serialVersionUID = 6328947014421475877L; + + /** + * The peer on which to perform <code>set</code> actions. + * This is here so that this class can be used as a peer. + * <P> + * + * When extending this class, this variable will be set to + * <code>this</code>. + */ + public BeanContextChild beanContextChildPeer; + + /** + * The parent <code>BeanContext</code>. + */ + protected transient BeanContext beanContext; + + /** + * If <code>setBeanContext()</code> was vetoed once before, this + * is set to <code>true</code> so that the next time, vetoes will + * be ignored. + */ + protected transient boolean rejectedSetBCOnce; + + /** + * Listeners are registered here and events are fired through here. + */ + protected PropertyChangeSupport pcSupport; + + /** + * Listeners are registered here and events are fired through here. + */ + protected VetoableChangeSupport vcSupport; + + /** + * Create a new <code>BeanContextChildSupport</code> with itself as the peer. + * This is meant to be used when you subclass + * <code>BeanContextChildSupport</code> to create your child. + */ + public BeanContextChildSupport() + { + this (null); + } + + /** + * Create a new <code>BeanContextChildSupport</code> with the specified peer. + * @param peer the peer to use, or <code>null</code> to specify + * <code>this</code>. + */ + public BeanContextChildSupport (BeanContextChild peer) + { + if (peer == null) + { + peer = this; + } + + beanContextChildPeer = peer; + pcSupport = new PropertyChangeSupport (peer); + vcSupport = new VetoableChangeSupport (peer); + } + + /** + * Set the parent <code>BeanContext</code>. + * <P> + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + * <P> + * + * When this Object is being removed from the current + * <code>BeanContext</code>, <code>setBeanContext()</code> will + * receive the parameter <code>null</code>. + * <P> + * + * Order of events: + * <OL> + * <LI> + * If the new <code>BeanContext</code> is the same as the old + * one, nothing happens. + * </LI> + * <LI> + * If the change has not been rejected or vetoed before, call + * <code>validatePendingSetBeanContext()</code>. If this call + * returns <code>false</code>, the change is rejected and a + * <code>PropertyVetoException</code> is thrown. + * </LI> + * <LI> + * If the change has not been rejected or vetoed before, + * <code>VetoableChangeEvent</code>s are fired with the name + * <code>"beanContext"</code>, using the + * <code>fireVetoableChange()</code> method. If a veto + * occurs, reversion events are fired using the same method, + * the change is rejected, and the veto is rethrown. + * </LI> + * <LI> + * <code>releaseBeanContextResources()</code> is called. + * </LI> + * <LI> + * The change is made. + * </LI> + * <LI> + * <code>PropertyChangeEvent</code>s are fired using the + * <code>firePropertyChange()</code> method. + * </LI> + * <LI> + * <code>initializeBeanContextResources()</code> is called. + * </LI> + * </OL> + * <P> + * + * @param newBeanContext the new parent for the + * <code>BeanContextChild</code>, or <code>null</code> to + * signify removal from a tree. + * @exception PropertyVetoException if the + * <code>BeanContextChild</code> implementor does not + * wish to have its parent changed. + */ + public void setBeanContext(BeanContext newBeanContext) + throws PropertyVetoException + { + synchronized (beanContextChildPeer) + { + if (newBeanContext == beanContext) + return; + + if (!rejectedSetBCOnce) + { + if (!validatePendingSetBeanContext (newBeanContext)) + { + rejectedSetBCOnce = true; + throw new PropertyVetoException ("validatePendingSetBeanContext() rejected change", + new PropertyChangeEvent(beanContextChildPeer, "beanContext", beanContext, newBeanContext)); + } + + try + { + fireVetoableChange ("beanContext", beanContext, newBeanContext); + } + catch (PropertyVetoException e) + { + rejectedSetBCOnce = true; + throw e; + } + } + + releaseBeanContextResources (); + + beanContext = newBeanContext; + rejectedSetBCOnce = false; + + firePropertyChange ("beanContext", beanContext, newBeanContext); + + initializeBeanContextResources (); + } + } + + /** + * Get the parent <code>BeanContext</code>. + * @return the parent <code>BeanContext</code>. + */ + public BeanContext getBeanContext() + { + return beanContext; + } + + /** + * Get the peer (or <code>this</code> if there is no peer). + * @return the peer, or <code>this</code> if there is no peer. + */ + public BeanContextChild getBeanContextChildPeer() { + return beanContextChildPeer; + } + + /** + * Determine whether there is a peer. + * This is true iff <code>getBeanContextChildPeer() == this</code>. + * @return whether there is a peer. + */ + public boolean isDelegated() { + return beanContextChildPeer == this; + } + + /** + * Add a listener that will be notified when a specific property changes. + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on. + * @param listener the listener listening on the property. + */ + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.addVetoableChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on + * @param listener the listener listening on the property. + */ + public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.removeVetoableChangeListener(propertyName, listener); + } + + /** + * Fire a property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + */ + public void firePropertyChange(String propertyName, Object oldVal, Object newVal) { + pcSupport.firePropertyChange(propertyName, oldVal, newVal); + } + + /** + * Fire a vetoable property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @exception PropertyVetoException if the change is vetoed. + */ + public void fireVetoableChange(String propertyName, Object oldVal, Object newVal) + throws PropertyVetoException { + vcSupport.fireVetoableChange(propertyName, oldVal, newVal); + } + + /** + * Called by <code>BeanContextServices.revokeService()</code> to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * <code>getService()</code> will no longer work on the specified + * service class after this event has been fired. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + public void serviceRevoked(BeanContextServiceRevokedEvent event) { + } + + /** + * Called by <code>BeanContextServices</code> whenever a service is made available. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + public void serviceAvailable(BeanContextServiceAvailableEvent event) { + } + + /** + * Called by <code>setBeanContext()</code> to determine whether the set should be rejected. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation simply + * returns <code>true</code>. + * + * @param newBeanContext the new parent. + * @return whether to allow the parent to be changed to the new + * value. + */ + public boolean validatePendingSetBeanContext(BeanContext newBeanContext) { + return true; + } + + /** + * Called by <code>setBeanContext()</code> to release resources of a what will soon no longer be the parent. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + */ + protected void releaseBeanContextResources() { + } + + /** + * Called by <code>setBeanContext()</code> to grab resources when the parent has been set. + * <P> + * + * <EM>This method is meant to be overriden.</EM> + * <code>BeanContextChildSupport</code>'s implementation does + * nothing. + */ + protected void initializeBeanContextResources() { + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java new file mode 100644 index 00000000000..3df91038bfe --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java @@ -0,0 +1,63 @@ +/* java.beans.beancontext.BeanContextContainerProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Container; + +/** + * Interface for <code>BeanContext</code>s which wish to associate an + * AWT container with them. The proxy is provided because the + * <code>addPropertyChangeListener()</code> and <code>add()</code> methods + * would conflict with <code>Component</code> and <code>Container</code> + * if you tried to extend. + * + * @specnote It is unclear whether anything besides <code>BeanContext</code>s + * are allowed to implement this interface. + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextContainerProxy { + /** + * Get the <code>Container</code> associated with this <code>BeanContext</code>. + * @return the <code>Container</code> associated with this + * <code>BeanContext</code>. + */ + Container getContainer(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java new file mode 100644 index 00000000000..f326541b034 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java @@ -0,0 +1,110 @@ +/* java.beans.beancontext.BeanContextEvent + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventObject; + +/** + * Generic superclass for events fired by <code>BeanContext</code>s. + * + * @author John Keiser + * @since 1.2 + */ + +public abstract class BeanContextEvent extends EventObject +{ + private static final long serialVersionUID = 7267998073569045052L; + + /** + * The <code>BeanContext</code> that most recently passed this + * event on. + */ + protected BeanContext propagatedFrom; + + /** + * Create a new event, from the specified <code>BeanContext</code>. + * <code>propagatedFrom</code> will be initialized to + * <code>null</code>. + * + * @param source the source of the event. + */ + protected BeanContextEvent(BeanContext source) + { + super(source); + } + + /** + * Get the <code>BeanContext</code> that originated this event. + * @return the originator of this event. + */ + public BeanContext getBeanContext() + { + return (BeanContext)getSource(); + } + + /** + * Get the most recent propagator of this event. + * If this value is <code>null</code>, you have received the event + * straight from the source. + * + * @return the most recent propagator of this event. + */ + public BeanContext getPropagatedFrom() + { + return propagatedFrom; + } + + /** + * Tell whether this event has been propagated. + * @return <code>true</code> iff <code>getPropagatedFrom() != null</code>. + */ + public boolean isPropagated() + { + return propagatedFrom != null; + } + + /** + * Set the most recent propagator of this event. + * @param propagator the most recent propagator of this event. + */ + public void setPropagatedFrom(BeanContext propagator) + { + propagatedFrom = propagator; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java new file mode 100644 index 00000000000..31765426622 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java @@ -0,0 +1,112 @@ +/* java.beans.beancontext.BeanContextMembershipEvent + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +/** + * Event fired when children are added to or removed from a <code>BeanContext</code>. + * Whether they were added or removed depends entirely on which method + * of the listener interface was called. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextMembershipListener + */ +public class BeanContextMembershipEvent extends BeanContextEvent { + /** + * The children that were added or removed. + */ + protected Collection children; + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Collection children) { + super(context); + this.children = children; + } + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Object[] children) { + super(context); + this.children = Arrays.asList(children); + } + + /** + * The number of children removed or added. + * @return the number of children removed or added. + */ + public int size() { + return children.size(); + } + + /** + * An iterator that will step through all the children. + * @return an iterator over all the children. + */ + public Iterator iterator() { + return children.iterator(); + } + + /** + * An array of the children. + * @return an array of the children. + */ + public Object[] toArray() { + return children.toArray(); + } + + /** + * Tell whether the <code>Object</code> is one of the children added or removed. + * @param child the child to check. + * @return whether the <code>Object</code> is added or removed. + */ + public boolean contains(Object child) { + return children.contains(child); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java new file mode 100644 index 00000000000..d39c36c4b12 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java @@ -0,0 +1,70 @@ +/* java.beans.beancontext.BeanContextMembershipListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * This is the interface to which <code>BeanContextMembershipEvent</code>s are sent. + * This happens when children are added to or removed from a + * <code>BeanContext</code>. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextMembershipListener extends EventListener { + /** + * When beans are added to a <code>BeanContext</code>, + * this method is called to fire the event. + * + * @param event the event, including which children were added. + * @see java.beans.beancontext.BeanContext#add(java.lang.Object) + */ + void childrenAdded(BeanContextMembershipEvent event); + + /** + * When beans are removed from a <code>BeanContext</code>, + * this method is called to fire the event. + * + * @param event the event, including which children were removed. + * @see java.beans.beancontext.BeanContext#remove(java.lang.Object) + */ + void childrenRemoved(BeanContextMembershipEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java new file mode 100644 index 00000000000..49dd7a77f30 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java @@ -0,0 +1,65 @@ +/* java.beans.beancontext.BeanContextProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Beans that wish to have a <code>BeanContextChild</code> or <code>BeanContext</code> associated with them + * but do not wish to implement those interfaces directly, can implement this interface. + * <P> + * + * Don't shoot yourself in the foot: if you already implement + * <code>BeanContextChild</code>, directly or indirectly, the whole + * workings of this package will be unpredictable because it is + * indeterminate as to whether the <code>BeanContextChild</code> is used + * in preference to its proxy or vice versa. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextProxy { + /** + * Return the <code>BeanContextChild</code> associated with this + * <code>Object</code>. + * + * @return the <code>BeanContextChild</code> associated with this + * <code>Object</code>. + */ + BeanContextChild getBeanContextProxy(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java new file mode 100644 index 00000000000..eea10f261b6 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java @@ -0,0 +1,95 @@ +/* java.beans.beancontext.BeanContextServiceAvailableEvent + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * Event fired when new services become available through a <code>BeanContextServices</code>. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServicesListener + */ + +public class BeanContextServiceAvailableEvent extends BeanContextEvent { + /** + * The <code>Class</code> representing the service which is now + * available. + */ + protected Class serviceClass; + + /** + * Create a new service available event. + * @param services the <code>BeanContextServices</code> through + * which the service is available. This is also the source + * of the event. + * @param serviceClass the service class that is now available. + */ + public BeanContextServiceAvailableEvent(BeanContextServices services, Class serviceClass) { + super(services); + this.serviceClass = serviceClass; + } + + /** + * Get the current service selectors of the service class. + * This is identical to <code>getSourceAsBeanContextServices().getCurrentServiceSelectors(getServiceClass())</code> + * @return the current service selectors of the service class. + */ + public Iterator getCurrentServiceSelectors() { + return getSourceAsBeanContextServices().getCurrentServiceSelectors(serviceClass); + } + + /** + * Get the newly available service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Get the <code>BeanContextServices</code> through which the new service is available. + * @return the <code>BeanContextServices</code> through which the + * new service is available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java new file mode 100644 index 00000000000..c09b5815fd7 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java @@ -0,0 +1,138 @@ +/* java.beans.beancontext.BeanContextServiceProvider + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * An actual factory for services. + * <P> + * + * It is the <code>BeanContextServiceProvider</code>'s responsibility to + * register itself with whatever <code>BeanContextServices</code> object + * it wishes to provide services through using the + * <code>addService()</code> method. + * <P> + * + * If for some reason it can no longer provide services for a particular + * class, this class must invoke + * <code>BeanContextServices.revokeService(serviceClass,this,true)</code> + * for all the places it has registered the service. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceProvider { + /** + * Get a service. + * Called from <code>BeanContextServices.getService()</code>. + * + * <p>If the requested service class is not available, or if this + * <code>BeanContextServiceProvider</code> chooses not honor the + * request for some reason, then this method will return + * <code>null</code>.</p> + * + * This method may throw unchecked exceptions, so watch out. + * + * @param services the <code>BeanContextServices</code> that wants + * to get the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the <code>Class</code> of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @return an instance of <code>serviceClass</code> (such that + * <code>instanceof</code> serviceClass is true), or + * <code>null</code>. + * @see java.beans.beancontext.BeanContextServices#getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + Object getService(BeanContextServices services, Object requestor, Class serviceClass, Object serviceSelector); + + /** + * Release the service. + * <P> + * + * Called by <code>BeanContextServices.releaseService()</code>. + * <P> + * + * Most <code>BeanContextServiceProvider</code>s won't have to do + * anything here. + * + * @param services the <code>BeanContextServices</code> that wants + * to release the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see java.beans.beancontext.BeanContextServices#releaseService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Object) + */ + void releaseService(BeanContextServices services, Object requestor, Object service); + + /** + * Get a list of valid service selectors for the specified service class. + * This method is called from + * <code>BeanContextServices.getCurrentServiceSelectors()</code>. + * <P> + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return <code>null</code>. + * If it takes a general <code>Integer</code> parameter, for + * example, you may as well return <code>null</code> or the poor + * soul who called this method will be iterating all day. + * <P> + * + * If it has no valid service selectors, it should still return an empty + * <code>Iterator</code>. + * + * @param services the <code>BeanContextServices</code> that wants + * to get the service selectors. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or <code>null</code>. + * @see java.beans.beancontext.BeanContextServices#getCurrentServiceSelectors(java.lang.Class) + */ + Iterator getCurrentServiceSelectors(BeanContextServices services, Class serviceClass); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java new file mode 100644 index 00000000000..690b94e2cc7 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextServiceProviderBeanInfo + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.BeanInfo; + +/** + * <code>BeanContextServiceProvider</code>s implement this to provide information about all of the services they provide. + * <P> + * + * This is apparently so that you can import a bunch of services into a + * RAD tool and it will know about all of them and export them to the + * user in a readable manner. + * + * @author John Keiser + * @since JDK1.2 + */ +public interface BeanContextServiceProviderBeanInfo extends BeanInfo { + /** + * Get <code>BeanInfo</code>s for all of the service classes of this <code>BeanInfoServiceProvider</code>. + * @return <code>BeanInfo</code>s for all provided service classes. + */ + BeanInfo[] getServicesBeanInfo(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java new file mode 100644 index 00000000000..dfa2b89b3ae --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java @@ -0,0 +1,110 @@ +/* java.beans.beancontext.BeanContextServiceRevokedEvent + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Event fired when services are revoked from a <code>BeanContextServices</code>. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServiceRevokedListener + */ + +public class BeanContextServiceRevokedEvent extends BeanContextEvent { + /** + * The <code>Class</code> representing the service which is now + * available. + */ + protected Class serviceClass; + private boolean invalidateRefs; + + /** + * Create a new service revoked event. + * @param services the <code>BeanContextServices</code> through + * which the service was available. This is also the source + * of the event. + * @param serviceClass the service class that is now revoked. + * @param revokeNow whether the revocation is immediate for all + * classes or just a suggestion. + */ + public BeanContextServiceRevokedEvent(BeanContextServices services, Class serviceClass, boolean revokeNow) { + super(services); + this.serviceClass = serviceClass; + invalidateRefs = revokeNow; + } + + /** + * Get the revoked service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Tell whether the revoked service class is the same as the specified class. + * Identical to <code>getServiceClass().equals(c)</code>. + * @param c the class to compare. + * @return whether the clases are equal. + */ + public boolean isServiceClass(Class c) { + return serviceClass.equals(c); + } + + /** + * Get the <code>BeanContextServices</code> through which the service was available. + * @return the <code>BeanContextServices</code> through which the + * service was available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } + + /** + * Tell whether current instances of the revoked service are usable or not. + * This is determined by whether the service was revoked + * immediately. + * + * @return whether current instances of the revoked service are + * usable. + */ + public boolean isCurrentServiceInvalidNow() { + return invalidateRefs; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java new file mode 100644 index 00000000000..101e6e191b0 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java @@ -0,0 +1,62 @@ +/* java.beans.beancontext.BeanContextServiceRevokedListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * Listens for service revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceRevokedListener extends EventListener { + /** + * Called by <code>BeanContextServices.revokeService()</code> to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * <code>getService()</code> will no longer work on the specified + * service class after this event has been fired. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + void serviceRevoked(BeanContextServiceRevokedEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServices.java b/libjava/classpath/java/beans/beancontext/BeanContextServices.java new file mode 100644 index 00000000000..cb1950360af --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServices.java @@ -0,0 +1,216 @@ +/* java.beans.beancontext.BeanContextServices + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; +import java.util.TooManyListenersException; + +/** + * Allows a <code>BeanContext</code> to provide services to its children. + * + * @specnote it is unclear whether a <code>BeanContextServices</code> + * should delegate unhandled requests to parents. I assume so. + * @author John Keiser + * @since 1.2 + */ + +public interface BeanContextServices + extends BeanContext, BeanContextServicesListener +{ + /** + * Register a service to make it available to others. + * This class may refuse to add the service based on whatever + * information it can gather, including whether the service + * provider is trusted. + * + * @param serviceClass the service class. + * @param provider the factory that will actually provide the service. + * @return whether the service was added or not. + */ + boolean addService (Class serviceClass, + BeanContextServiceProvider provider); + + /** + * Make it so that no one else can use this service. + * <P> + * + * If <code>revokeNow</code> is <code>false</code>, the only + * effect of this method is to make all subsequent calls to + * <code>getService()</code> on this service class fail. + * <P> + * + * If it is <code>true</code>, a message is also sent out to all + * listeners on the service and all references to it are released. + * + * @param serviceClass the service class to revoke. + * @param provider the service provider providing the service class. + * @param revokeNow whether to release all current references to + * the service. + */ + void revokeService (Class serviceClass, + BeanContextServiceProvider provider, + boolean revokeNow); + + /** + * Release your copy of this service. + * <P> + * + * If all copies of the service's class have been relinquished by + * the requestor, the <code>BeanContextServiceRevokedListener</code> + * previously registered by <code>getService()</code> will be + * unregistered. + * + * @param requestorChild the original <code>BeanContextChild</code> + * requesting the service. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see #getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + void releaseService (BeanContextChild requestorChild, Object requestor, + Object service); + + /** + * Get a service from this <code>BeanContextServices</code>. + * <P> + * + * The specified listener will be registered to receive a + * revocation notice for the specified serviceClass. One + * notification per service class per requestor object will be + * sent. + * <P> + * + * The listener will be unregistered when all services that were + * obtained by that requestor for that service class are released. + * <P> + * + * If the requested service class is not available, or if this + * <code>BeanContextServices</code> object chooses not honor the + * request because the service class has been revoked or for some + * other reason, then this method will return <code>null</code>. + * <P> + * + * This method may throw unchecked exceptions, so watch out. + * + * @specnote it is not specified what happens when two subsequent + * calls are made to <code>getService()</code> with the + * same requestor object and service class but different + * listeners. Which listener is to be notified? + * + * @param requestorChild the <code>BeanContextChild</code> + * associated with the requestor. Typically this will be + * the same as the requestor itself, but since any + * <code>Object</code>, even one outside the hierarchy, may + * make a request, this parameter is necessary. Only weak + * references to this will be retained, and it will never + * be changed, only queried in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the <code>Class</code> of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @param listener a listener that will be notified if the service + * being requested is revoked. + * @return an instance of <code>serviceClass</code> (such that + * <code>instanceof</code> serviceClass is true), or + * <code>null</code>. + */ + Object getService (BeanContextChild requestorChild, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener listener) + throws TooManyListenersException; + + /** + * Get a list of all service classes supported. + * <P> + * + * This method must synchronize on + * <code>BeanContext.globalHierarchyLock</code>. + * + * @return a list of all service classes supported. + * @see java.beans.beancontext.BeanContext#globalHierarchyLock + */ + Iterator getCurrentServiceClasses (); + + /** + * Get a list of valid service selectors for the specified service class. + * <P> + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return <code>null</code>. + * If it takes a general <code>Integer</code> parameter, for + * example, you may as well return <code>null</code> or the poor + * soul who called this method will be iterating all day. + * <P> + * + * If it has no valid service selectors, it should still return an empty + * <code>Iterator</code>. + * + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or <code>null</code>. + */ + Iterator getCurrentServiceSelectors (Class serviceClass); + + /** + * Tell whether the specified service class is available. + * Iff getService() could return a non-null value for the + * specified service, this method will return <code>true</code>. + * + * @param serviceClass the service class to check on. + * @return whether the specified service class is available. + */ + boolean hasService (Class serviceClass); + + /** + * Add a listener on all adds and removes of services. + * @param listener the listener to add. + */ + void addBeanContextServicesListener (BeanContextServicesListener listener); + + /** + * Remove a listener on all adds and removes of services. + * @specnote it is not certain whether this should remove this + * listener if it was specified in + * <code>getService()</code>. + * @param listener the listener to add. + */ + void removeBeanContextServicesListener (BeanContextServicesListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java new file mode 100644 index 00000000000..becc7cdb6e4 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java @@ -0,0 +1,56 @@ +/* java.beans.beancontext.BeanContextServicesListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Listens for service add and revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServicesListener extends BeanContextServiceRevokedListener { + /** + * Called by <code>BeanContextServices</code> whenever a service is made available. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + void serviceAvailable(BeanContextServiceAvailableEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java new file mode 100644 index 00000000000..b7c4a49d8a9 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java @@ -0,0 +1,300 @@ +/* BeanContextServicesSupport.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.TooManyListenersException; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class BeanContextServicesSupport + extends BeanContextSupport + implements BeanContextServices +{ + private static final long serialVersionUID = -8494482757288719206L; + + protected class BCSSChild + extends BeanContextSupport.BCSChild + { + private static final long serialVersionUID = -6848044915271367103L; + } + + protected class BCSSProxyServiceProvider + implements BeanContextServiceProvider, + BeanContextServiceRevokedListener + { + private static final long serialVersionUID = 7078212910685744490L; + + public Iterator getCurrentServiceSelectors (BeanContextServices bcs, + Class serviceClass) + { + throw new Error ("Not implemented"); + } + + public Object getService (BeanContextServices bcs, + Object requestor, + Class serviceClass, + Object serviceSelector) + { + throw new Error ("Not implemented"); + } + + public void releaseService (BeanContextServices bcs, + Object requestor, + Object service) + { + throw new Error ("Not implemented"); + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcsre) + { + throw new Error ("Not implemented"); + } + } + + protected static class BCSSServiceProvider + implements Serializable + { + private static final long serialVersionUID = 861278251667444782L; + + protected BeanContextServiceProvider serviceProvider; + + protected BeanContextServiceProvider getServiceProvider() + { + return serviceProvider; + } + } + + protected transient ArrayList bcsListeners; + + protected transient BCSSProxyServiceProvider proxy; + + protected transient int serializable; + + protected transient HashMap services; + + public BeanContextServicesSupport () + { + super(); + } + + public BeanContextServicesSupport (BeanContextServices peer) + { + super(peer); + } + + public BeanContextServicesSupport(BeanContextServices peer, Locale locale) + { + super(peer, locale); + } + + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime) + { + super(peer, locale, dtime); + } + + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime, boolean visible) + { + super(peer, locale, dtime, visible); + } + + public void addBeanContextServicesListener + (BeanContextServicesListener listener) + { + if (! bcsListeners.contains(listener)) + bcsListeners.add(listener); + } + + public boolean addService (Class serviceClass, BeanContextServiceProvider bcsp) + { + throw new Error ("Not implemented"); + } + + protected boolean addService (Class serviceClass, + BeanContextServiceProvider bcsp, + boolean fireEvent) + { + throw new Error ("Not implemented"); + } + + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + throw new Error ("Not implemented"); + } + + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + throw new Error ("Not implemented"); + } + + protected void childJustRemovedHook (Object child, + BeanContextSupport.BCSChild bcsc) + { + throw new Error ("Not implemented"); + } + + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, + Object peer) + { + throw new Error ("Not implemented"); + } + + protected BeanContextServicesSupport.BCSSServiceProvider + createBCSSServiceProvider (Class sc, BeanContextServiceProvider bcsp) + { + throw new Error ("Not implemented"); + } + + protected final void fireServiceAdded (BeanContextServiceAvailableEvent bcssae) + { + throw new Error ("Not implemented"); + } + + protected final void fireServiceAdded (Class serviceClass) + { + throw new Error ("Not implemented"); + } + + protected final void fireServiceRevoked(BeanContextServiceRevokedEvent event) + { + throw new Error ("Not implemented"); + } + + protected final void fireServiceRevoked (Class serviceClass, + boolean revokeNow) + { + throw new Error ("Not implemented"); + } + + public BeanContextServices getBeanContextServicesPeer () + { + throw new Error ("Not implemented"); + } + + protected static final BeanContextServicesListener + getChildBeanContextServicesListener (Object child) + { + throw new Error ("Not implemented"); + } + + public Iterator getCurrentServiceClasses () + { + throw new Error ("Not implemented"); + } + + public Iterator getCurrentServiceSelectors (Class serviceClass) + { + throw new Error ("Not implemented"); + } + + public Object getService (BeanContextChild child, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener bcsrl) + throws TooManyListenersException + { + throw new Error ("Not implemented"); + } + + public boolean hasService (Class serviceClass) + { + throw new Error ("Not implemented"); + } + + public void initialize () + { + super.initialize(); + + bcsListeners = new ArrayList(); + services = new HashMap(); + } + + protected void initializeBeanContextResources () + { + throw new Error ("Not implemented"); + } + + protected void releaseBeanContextResources () + { + throw new Error ("Not implemented"); + } + + public void releaseService (BeanContextChild child, Object requestor, + Object service) + { + throw new Error ("Not implemented"); + } + + public void removeBeanContextServicesListener + (BeanContextServicesListener listener) + { + int index = bcsListeners.indexOf(listener); + + if (index > -1) + bcsListeners.remove(index); + } + + public void revokeService (Class serviceClass, BeanContextServiceProvider bcsp, + boolean revokeCurrentServicesNow) + { + throw new Error ("Not implemented"); + } + + public void serviceAvailable (BeanContextServiceAvailableEvent bcssae) + { + throw new Error ("Not implemented"); + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcssre) + { + throw new Error ("Not implemented"); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java new file mode 100644 index 00000000000..7e024e23a13 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java @@ -0,0 +1,460 @@ +/* BeanContextSupport.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class BeanContextSupport extends BeanContextChildSupport + implements BeanContext, Serializable, PropertyChangeListener, + VetoableChangeListener +{ + private static final long serialVersionUID = -4879613978649577204L; + + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + throw new Error ("Not implemented"); + } + + private void writeObject (ObjectOutputStream s) + throws ClassNotFoundException, IOException + { + throw new Error ("Not implemented"); + } + + protected class BCSChild implements Serializable + { + private static final long serialVersionUID = 3289144128843950629L; + } + + protected static final class BCSIterator implements Iterator + { + public boolean hasNext () + { + throw new Error ("Not implemented"); + } + + public Object next () + { + throw new Error ("Not implemented"); + } + + public void remove () + { + // This must be a noop remove operation. + } + } + + protected transient ArrayList bcmListeners; + + protected transient HashMap children; + + protected transient boolean designTime; + + protected transient Locale locale; + + protected transient boolean okToUseGui; + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport () + { + this (null, null, true, true); + } + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport (BeanContext peer) + { + this (peer, null, true, true); + } + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport (BeanContext peer, Locale lcle) + { + this (peer, lcle, true, true); + } + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport (BeanContext peer, Locale lcle, boolean dtime) + { + this (peer, lcle, dtime, true); + } + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport (BeanContext peer, Locale lcle, boolean dtime, + boolean visible) + { + locale = lcle; + designTime = dtime; + okToUseGui = visible; + + initialize (); + } + + public boolean add (Object targetChild) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + if (children.containsKey(targetChild)) + return false; + + // FIXME: The second argument is surely wrong. + children.put(targetChild, targetChild); + return true; + } + + public boolean addAll (Collection c) + { + throw new UnsupportedOperationException(); + } + + public void addBeanContextMembershipListener + (BeanContextMembershipListener listener) + { + if (! bcmListeners.contains(listener)) + bcmListeners.add(listener); + } + + public boolean avoidingGui () + { + throw new Error ("Not implemented"); + } + + protected Iterator bcsChildren () + { + throw new Error ("Not implemented"); + } + + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + throw new Error ("Not implemented"); + } + + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + throw new Error ("Not implemented"); + } + + protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + throw new Error ("Not implemented"); + } + + protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + throw new Error ("Not implemented"); + } + + protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + throw new Error ("Not implemented"); + } + + protected static final boolean classEquals (Class first, Class second) + { + throw new Error ("Not implemented"); + } + + public void clear () + { + throw new UnsupportedOperationException(); + } + + public boolean contains (Object o) + { + throw new Error ("Not implemented"); + } + + public boolean containsAll (Collection c) + { + throw new Error ("Not implemented"); + } + + public boolean containsKey (Object o) + { + throw new Error ("Not implemented"); + } + + protected final Object[] copyChildren () + { + throw new Error ("Not implemented"); + } + + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer) + { + throw new Error ("Not implemented"); + } + + protected final void deserialize (ObjectInputStream ois, Collection coll) + throws ClassNotFoundException, IOException + { + throw new Error ("Not implemented"); + } + + public void dontUseGui () + { + throw new Error ("Not implemented"); + } + + protected final void fireChildrenAdded (BeanContextMembershipEvent bcme) + { + throw new Error ("Not implemented"); + } + + protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme) + { + throw new Error ("Not implemented"); + } + + public BeanContext getBeanContextPeer () + { + throw new Error ("Not implemented"); + } + + protected static final BeanContextChild getChildBeanContextChild (Object child) + { + throw new Error ("Not implemented"); + } + + protected static final BeanContextMembershipListener getChildBeanContextMembershipListener (Object child) + { + throw new Error ("Not implemented"); + } + + protected static final PropertyChangeListener getChildPropertyChangeListener (Object child) + { + throw new Error ("Not implemented"); + } + + protected static final Serializable getChildSerializable (Object child) + { + throw new Error ("Not implemented"); + } + + protected static final VetoableChangeListener getChildVetoableChangeListener (Object child) + { + throw new Error ("Not implemented"); + } + + protected static final Visibility getChildVisibility (Object child) + { + throw new Error ("Not implemented"); + } + + public Locale getLocale () + { + return locale; + } + + public URL getResource (String name, BeanContextChild bcc) + { + throw new Error ("Not implemented"); + } + + public InputStream getResourceAsStream (String name, BeanContextChild bcc) + { + throw new Error ("Not implemented"); + } + + protected void initialize () + { + bcmListeners = new ArrayList(); + children = new HashMap(); + } + + public Object instantiateChild (String beanName) + throws IOException, ClassNotFoundException + { + throw new Error ("Not implemented"); + } + + public boolean isDesignTime () + { + throw new Error ("Not implemented"); + } + + public boolean isEmpty () + { + throw new Error ("Not implemented"); + } + + public boolean isSerializing () + { + throw new Error ("Not implemented"); + } + + public Iterator iterator () + { + return children.keySet().iterator(); + } + + public boolean needsGui () + { + throw new Error ("Not implemented"); + } + + public void okToUseGui () + { + throw new Error ("Not implemented"); + } + + public void propertyChange (PropertyChangeEvent pce) + { + throw new Error ("Not implemented"); + } + + public final void readChildren (ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + throw new Error ("Not implemented"); + } + + public boolean remove (Object targetChild) + { + return remove(targetChild, true); + } + + protected boolean remove (Object targetChild, boolean callChildSetBC) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + throw new Error ("Not implemented"); + } + + public boolean removeAll (Collection c) + { + throw new UnsupportedOperationException(); + } + + public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml) + { + throw new Error ("Not implemented"); + } + + public boolean retainAll (Collection c) + { + throw new UnsupportedOperationException(); + } + + protected final void serialize (ObjectOutputStream oos, Collection coll) + throws IOException + { + throw new Error ("Not implemented"); + } + + public void setDesignTime (boolean dtime) + { + throw new Error ("Not implemented"); + } + + public void setLocale (Locale newLocale) + throws PropertyVetoException + { + throw new Error ("Not implemented"); + } + + public int size () + { + throw new Error ("Not implemented"); + } + + public Object[] toArray () + { + return children.keySet().toArray(); + } + + public Object[] toArray(Object[] array) + { + return children.keySet().toArray(array); + } + + protected boolean validatePendingAdd (Object targetChild) + { + throw new Error ("Not implemented"); + } + + protected boolean validatePendingRemove (Object targetChild) + { + throw new Error ("Not implemented"); + } + + public void vetoableChange (PropertyChangeEvent pce) + throws PropertyVetoException + { + throw new Error ("Not implemented"); + } + + public final void writeChildren (ObjectOutputStream oos) + throws IOException + { + throw new Error ("Not implemented"); + } +} diff --git a/libjava/classpath/java/beans/beancontext/package.html b/libjava/classpath/java/beans/beancontext/package.html new file mode 100644 index 00000000000..55e3bd723da --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.beans.beancontext package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.beans.beancontext</title></head> + +<body> +<p>Containers and execution environments for beans.</p> + +</body> +</html> diff --git a/libjava/classpath/java/beans/package.html b/libjava/classpath/java/beans/package.html new file mode 100644 index 00000000000..5ca8000371b --- /dev/null +++ b/libjava/classpath/java/beans/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.beans package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.beans</title></head> + +<body> +<p>Listeners and descriptors for managing beans.</p> + +</body> +</html> diff --git a/libjava/classpath/java/io/BufferedInputStream.java b/libjava/classpath/java/io/BufferedInputStream.java new file mode 100644 index 00000000000..6fb024614d2 --- /dev/null +++ b/libjava/classpath/java/io/BufferedInputStream.java @@ -0,0 +1,379 @@ +/* BufferedInputStream.java -- An input stream that implements buffering + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilterInputStream</code> buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 2048 bytes can be overridden by the creator of the stream. + * <p> + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input bytes, to the limits of + * system memory or the size of <code>Integer.MAX_VALUE</code> + * <p> + * Please note that this class does not properly handle character + * encodings. Consider using the <code>BufferedReader</code> class which + * does. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Jeroen Frijters (jeroen@frijters.net) + */ +public class BufferedInputStream extends FilterInputStream +{ + + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 2048; + + /** + * The buffer used for storing data from the underlying stream. + */ + protected byte[] buf; + + /** + * The number of valid bytes currently in the buffer. It is also the index + * of the buffer position one byte past the end of the valid data. + */ + protected int count; + + /** + * The index of the next character that will by read from the buffer. + * When <code>pos == count</code>, the buffer is empty. + */ + protected int pos; + + /** + * The value of <code>pos</code> when the <code>mark()</code> method was + * called. + * This is set to -1 if there is no mark set. + */ + protected int markpos = -1; + + /** + * This is the maximum number of bytes than can be read after a + * call to <code>mark()</code> before the mark can be discarded. + * After this may bytes are read, the <code>reset()</code> method + * may not be called successfully. + */ + protected int marklimit; + + /** + * This is the initial buffer size. When the buffer is grown because + * of marking requirements, it will be grown by bufferSize increments. + * The underlying stream will be read in chunks of bufferSize. + */ + private final int bufferSize; + + /** + * This method initializes a new <code>BufferedInputStream</code> that will + * read from the specified subordinate stream with a default buffer size + * of 2048 bytes + * + * @param in The subordinate stream to read from + */ + public BufferedInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedInputStream</code> that will + * read from the specified subordinate stream with a buffer size that + * is specified by the caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException when size is smaller then 1 + */ + public BufferedInputStream(InputStream in, int size) + { + super(in); + if (size <= 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + // initialize pos & count to bufferSize, to prevent refill from + // allocating a new buffer (if the caller starts out by calling mark()). + pos = count = bufferSize = size; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * The number of available bytes will be the number of read ahead bytes + * stored in the internal buffer plus the number of available bytes in + * the underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + return count - pos + super.available(); + } + + /** + * This method closes the underlying input stream and frees any + * resources associated with it. Sets <code>buf</code> to <code>null</code>. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + // Free up the array memory. + buf = null; + pos = count = 0; + markpos = -1; + super.close(); + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of bytes that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 bytes of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * <p> + * Note that the number of bytes that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the mark + * becomes invalid + */ + public synchronized void mark(int readlimit) + { + marklimit = readlimit; + markpos = pos; + } + + /** + * This method returns <code>true</code> to indicate that this class + * supports mark/reset functionality. + * + * @return <code>true</code> to indicate that mark/reset functionality is + * supported + * + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos >= count && !refill()) + return -1; // EOF + + return buf[pos++] & 0xFF; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>off</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested, but it will try + * to read the requested number of bytes by repeatedly calling the underlying + * stream as long as available() for this stream continues to return a + * non-zero value (or until the requested number of bytes have been read). + * The actual number of bytes read is returned as an int. A -1 is returned + * to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException when <code>off</code> or + * <code>len</code> are negative, or when <code>off + len</code> + * is larger then the size of <code>b</code>, + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || b.length - off < len) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + if (pos >= count && !refill()) + return -1; // No bytes were read before EOF. + + int totalBytesRead = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, totalBytesRead); + pos += totalBytesRead; + off += totalBytesRead; + len -= totalBytesRead; + + while (len > 0 && super.available() > 0 && refill()) + { + int remain = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, remain); + pos += remain; + off += remain; + len -= remain; + totalBytesRead += remain; + } + + return totalBytesRead; + } + + /** + * This method resets a stream to the point where the <code>mark()</code> + * method was called. Any bytes that were read after the mark point was + * set will be re-read during subsequent reads. + * <p> + * This method will throw an IOException if the number of bytes read from + * the stream since the call to <code>mark()</code> exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If <code>mark()</code> was never called or more + * then <code>marklimit</code> bytes were read since the last + * call to <code>mark()</code> + */ + public synchronized void reset() throws IOException + { + if (markpos == -1) + throw new IOException(buf == null ? "Stream closed." : "Invalid mark."); + + pos = markpos; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip(long n) throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + final long origN = n; + + while (n > 0L) + { + if (pos >= count && !refill()) + break; + + int numread = (int) Math.min((long) (count - pos), n); + pos += numread; + n -= numread; + } + + return origN - n; + } + + /** + * Called to refill the buffer (when count is equal to pos). + * + * @return <code>true</code> when at least one additional byte was read + * into <code>buf</code>, <code>false</code> otherwise (at EOF). + */ + private boolean refill() throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + if (markpos == -1 || count - markpos >= marklimit) + { + markpos = -1; + pos = count = 0; + } + else + { + byte[] newbuf = buf; + if (markpos < bufferSize) + { + newbuf = new byte[count - markpos + bufferSize]; + } + System.arraycopy(buf, markpos, newbuf, 0, count - markpos); + buf = newbuf; + count -= markpos; + pos -= markpos; + markpos = 0; + } + + int numread = super.read(buf, count, bufferSize); + + if (numread <= 0) // EOF + return false; + + count += numread; + return true; + } +} diff --git a/libjava/classpath/java/io/BufferedOutputStream.java b/libjava/classpath/java/io/BufferedOutputStream.java new file mode 100644 index 00000000000..ce7ebc7e938 --- /dev/null +++ b/libjava/classpath/java/io/BufferedOutputStream.java @@ -0,0 +1,192 @@ +/* BufferedOutputStream.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class accumulates bytes written in a buffer instead of immediately + * writing the data to the underlying output sink. The bytes are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedOutputStream extends FilterOutputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 512; + + /** + * This is the internal byte array used for buffering output before + * writing it. + */ + protected byte[] buf; + + /** + * This is the number of bytes that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next byte of data will be stored + */ + protected int count; + + /** + * This method initializes a new <code>BufferedOutputStream</code> instance + * that will write to the specified subordinate <code>OutputStream</code> + * and which will use a default buffer size of 512 bytes. + * + * @param out The underlying <code>OutputStream</code> to write data to + */ + public BufferedOutputStream(OutputStream out) + { + this(out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedOutputStream</code> instance + * that will write to the specified subordinate <code>OutputStream</code> + * and which will use the specified buffer size + * + * @param out The underlying <code>OutputStream</code> to write data to + * @param size The size of the internal buffer + */ + public BufferedOutputStream(OutputStream out, int size) + { + super(out); + + buf = new byte[size]; + } + + /** + * This method causes any currently buffered bytes to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public synchronized void flush() throws IOException + { + if (count == 0) + return; + + out.write(buf, 0, count); + count = 0; + out.flush(); + } + + /** + * This method flushes any remaining buffered bytes then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + public synchronized void close() throws IOException + { + flush(); + out.close(); + } + */ + + /** + * This method runs when the object is garbage collected. It is + * responsible for ensuring that all buffered bytes are written and + * for closing the underlying stream. + * + * @exception IOException If an error occurs (ignored by the Java runtime) + * + protected void finalize() throws IOException + { + close(); + } + */ + + /** + * This method writes a single byte of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param b The byte of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public synchronized void write(int b) throws IOException + { + if (count == buf.length) + flush(); + + buf[count] = (byte)(b & 0xFF); + ++count; + } + + /** + * This method writes <code>len</code> bytes from the byte array + * <code>buf</code> starting at position <code>offset</code> in the buffer. + * These bytes will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of bytes to write. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + */ + public synchronized void write(byte[] buf, int offset, int len) + throws IOException + { + // Buffer can hold everything. Note that the case where LEN < 0 + // is automatically handled by the downstream write. + if (len < (this.buf.length - count)) + { + System.arraycopy(buf, offset, this.buf, count, len); + count += len; + } + else + { + // The write was too big. So flush the buffer and write the new + // bytes directly to the underlying stream, per the JDK 1.2 + // docs. + flush(); + out.write (buf, offset, len); + } + } + +} // class BufferedOutputStream + diff --git a/libjava/classpath/java/io/BufferedReader.java b/libjava/classpath/java/io/BufferedReader.java new file mode 100644 index 00000000000..4849949c989 --- /dev/null +++ b/libjava/classpath/java/io/BufferedReader.java @@ -0,0 +1,581 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilterReader</code> buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 8192 chars can be overridden by the creator of the stream. + * <p> + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input chars, to the limits of + * system memory or the size of <code>Integer.MAX_VALUE</code> + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedReader extends Reader +{ + Reader in; + char[] buffer; + /* Index of current read position. Must be >= 0 and <= limit. */ + /* There is a special case where pos may be equal to limit+1; this + * is used as an indicator that a readLine was done with a '\r' was + * the very last char in the buffer. Since we don't want to read-ahead + * and potentially block, we set pos this way to indicate the situation + * and deal with it later. Doing it this way rather than having a + * separate boolean field to indicate the condition has the advantage + * that it is self-clearing on things like mark/reset. + */ + int pos; + /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */ + /* This can be < pos in the one special case described above. */ + int limit; + + /* The value -1 means there is no mark, or the mark has been invalidated. + Otherwise, markPos is the index in the buffer of the marked position. + Must be >= 0 and <= pos. + Note we do not explicitly store the read-limit. + The implicit read-limit is (buffer.length - markPos), which is + guaranteed to be >= the read-limit requested in the call to mark. */ + int markPos = -1; + + // The JCL book specifies the default buffer size as 8K characters. + // This is package-private because it is used by LineNumberReader. + static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * The line buffer for <code>readLine</code>. + */ + private StringBuffer sbuf = null; + + /** + * Create a new <code>BufferedReader</code> that will read from the + * specified subordinate stream with a default buffer size of 8192 chars. + * + * @param in The subordinate stream to read from + */ + public BufferedReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new <code>BufferedReader</code> that will read from the + * specified subordinate stream with a buffer size that is specified by the + * caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException if size <= 0 + */ + public BufferedReader(Reader in, int size) + { + super(in.lock); + if (size <= 0) + throw new IllegalArgumentException("Illegal buffer size: " + size); + this.in = in; + buffer = new char[size]; + } + + /** + * This method closes the underlying stream and frees any associated + * resources. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + if (in != null) + in.close(); + in = null; + buffer = null; + } + } + + /** + * Returns <code>true</code> to indicate that this class supports mark/reset + * functionality. + * + * @return <code>true</code> + */ + public boolean markSupported() + { + return true; + } + + /** + * Mark a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readLimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * <p> + * Note that the number of chars that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException if readLimit is negative. + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + checkStatus(); + // In this method we need to be aware of the special case where + // pos + 1 == limit. This indicates that a '\r' was the last char + // in the buffer during a readLine. We'll want to maintain that + // condition after we shift things around and if a larger buffer is + // needed to track readLimit, we'll have to make it one element + // larger to ensure we don't invalidate the mark too early, if the + // char following the '\r' is NOT a '\n'. This is ok because, per + // the spec, we are not required to invalidate when passing readLimit. + // + // Note that if 'pos > limit', then doing 'limit -= pos' will cause + // limit to be negative. This is the only way limit will be < 0. + + if (pos + readLimit > limit) + { + char[] old_buffer = buffer; + int extraBuffSpace = 0; + if (pos > limit) + extraBuffSpace = 1; + if (readLimit + extraBuffSpace > limit) + buffer = new char[readLimit + extraBuffSpace]; + limit -= pos; + if (limit >= 0) + { + System.arraycopy(old_buffer, pos, buffer, 0, limit); + pos = 0; + } + } + + if (limit < 0) + { + // Maintain the relationship of 'pos > limit'. + pos = 1; + limit = markPos = 0; + } + else + markPos = pos; + // Now pos + readLimit <= buffer.length. thus if we need to read + // beyond buffer.length, then we are allowed to invalidate markPos. + } + } + + /** + * Reset the stream to the point where the <code>mark()</code> method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + * <p> + * This method will throw an IOException if the number of chars read from + * the stream since the call to <code>mark()</code> exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If an error occurs; + */ + public void reset() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + + // Need to handle the extremely unlikely case where a readLine was + // done with a '\r' as the last char in the buffer; which was then + // immediately followed by a mark and a reset with NO intervening + // read of any sort. In that case, setting pos to markPos would + // lose that info and a subsequent read would thus not skip a '\n' + // (if one exists). The value of limit in this rare case is zero. + // We can assume that if limit is zero for other reasons, then + // pos is already set to zero and doesn't need to be readjusted. + if (limit > 0) + pos = markPos; + } + } + + /** + * This method determines whether or not a stream is ready to be read. If + * this method returns <code>false</code> then this stream could (but is + * not guaranteed to) block on the next read attempt. + * + * @return <code>true</code> if this stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + checkStatus(); + return pos < limit || in.ready(); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * <code>offset</code> into + * the buffer and attempts to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException If offset and count are not + * valid regarding buf. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (offset < 0 || offset + count > buf.length || count < 0) + throw new IndexOutOfBoundsException(); + + synchronized (lock) + { + checkStatus(); + // Once again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we'll + // need to skip a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + + int avail = limit - pos; + if (count > avail) + { + if (avail > 0) + count = avail; + else // pos >= limit + { + if (limit == buffer.length) + markPos = -1; // read too far - invalidate the mark. + if (pos > limit) + { + // Set a boolean and make pos == limit to simplify things. + retAtEndOfBuffer = true; + --pos; + } + if (markPos < 0) + { + // Optimization: can read directly into buf. + if (count >= buffer.length && !retAtEndOfBuffer) + return in.read(buf, offset, count); + pos = limit = 0; + } + avail = in.read(buffer, limit, buffer.length - limit); + if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n') + { + --avail; + limit++; + } + if (avail < count) + { + if (avail <= 0) + return avail; + count = avail; + } + limit += avail; + } + } + System.arraycopy(buffer, pos, buf, offset, count); + pos += count; + return count; + } + } + + /* Read more data into the buffer. Update pos and limit appropriately. + Assumes pos==limit initially. May invalidate the mark if read too much. + Return number of chars read (never 0), or -1 on eof. */ + private int fill() throws IOException + { + checkStatus(); + // Handle the special case of a readLine that has a '\r' at the end of + // the buffer. In this case, we'll need to skip a '\n' if it is the + // next char to be read. This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + if (pos > limit) + { + retAtEndOfBuffer = true; + --pos; + } + + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count > 0) + limit += count; + + if (retAtEndOfBuffer && buffer[pos] == '\n') + { + --count; + // If the mark was set to the location of the \n, then we + // must change it to fully pretend that the \n does not + // exist. + if (markPos == pos) + ++markPos; + ++pos; + } + + return count; + } + + public int read() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (pos >= limit && fill () <= 0) + return -1; + return buffer[pos++]; + } + } + + /* Return the end of the line starting at this.pos and ending at limit. + * The index returns is *before* any line terminators, or limit + * if no line terminators were found. + */ + private int lineEnd(int limit) + { + int i = pos; + for (; i < limit; i++) + { + char ch = buffer[i]; + if (ch == '\n' || ch == '\r') + break; + } + return i; + } + + /** + * This method reads a single line of text from the input stream, returning + * it as a <code>String</code>. A line is terminated by "\n", a "\r", or + * an "\r\n" sequence. The system dependent line separator is not used. + * The line termination characters are not returned in the resulting + * <code>String</code>. + * + * @return The line of text read, or <code>null</code> if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + checkStatus(); + // Handle the special case where a previous readLine (with no intervening + // reads/skips) had a '\r' at the end of the buffer. + // In this case, we'll need to skip a '\n' if it's the next char to be read. + // This special case is indicated by 'pos > limit'. + if (pos > limit) + { + int ch = read(); + if (ch < 0) + return null; + if (ch != '\n') + --pos; + } + int i = lineEnd(limit); + if (i < limit) + { + String str = String.valueOf(buffer, pos, i - pos); + pos = i + 1; + // If the last char in the buffer is a '\r', we must remember + // to check if the next char to be read after the buffer is refilled + // is a '\n'. If so, skip it. To indicate this condition, we set pos + // to be limit + 1, which normally is never possible. + if (buffer[i] == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + return str; + } + if (sbuf == null) + sbuf = new StringBuffer(200); + else + sbuf.setLength(0); + sbuf.append(buffer, pos, i - pos); + pos = i; + // We only want to return null when no characters were read before + // EOF. So we must keep track of this separately. Otherwise we + // would treat an empty `sbuf' as an EOF condition, which is wrong + // when there is just a newline. + boolean eof = false; + for (;;) + { + // readLine should block. So we must not return until a -1 is reached. + if (pos >= limit) + { + // here count == 0 isn't sufficient to give a failure. + int count = fill(); + if (count < 0) + { + eof = true; + break; + } + continue; + } + int ch = buffer[pos++]; + if (ch == '\n' || ch == '\r') + { + // Check here if a '\r' was the last char in the buffer; if so, + // mark it as in the comment above to indicate future reads + // should skip a newline that is the next char read after + // refilling the buffer. + if (ch == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + break; + } + i = lineEnd(limit); + sbuf.append(buffer, pos - 1, i - (pos - 1)); + pos = i; + } + return (sbuf.length() == 0 && eof) ? null : sbuf.toString(); + } + + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method first discards chars in the buffer, then calls the + * <code>skip</code> method on the underlying stream to skip the + * remaining chars. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs. + * @exception IllegalArgumentException If count is negative. + */ + public long skip(long count) throws IOException + { + synchronized (lock) + { + checkStatus(); + if (count < 0) + throw new IllegalArgumentException("skip value is negative"); + if (count == 0) + return 0; + // Yet again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we need + // to ignore a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit' (i.e. avail < 0). + // To simplify things, if we're dealing with the special case for + // readLine, just read the next char (since the fill method will + // skip the '\n' for us). By doing this, we'll have to back up pos. + // That's easier than trying to keep track of whether we've skipped + // one element or not. + if (pos > limit) + { + if (read() < 0) + return 0; + else + --pos; + } + + int avail = limit - pos; + + if (count < avail) + { + pos += count; + return count; + } + + pos = limit; + long todo = count - avail; + if (todo > buffer.length) + { + markPos = -1; + todo -= in.skip(todo); + } + else + { + while (todo > 0) + { + avail = fill(); + if (avail <= 0) + break; + if (avail > todo) + avail = (int) todo; + pos += avail; + todo -= avail; + } + } + return count - todo; + } + } + + private void checkStatus() throws IOException + { + if (in == null) + throw new IOException("Stream closed"); + } +} diff --git a/libjava/classpath/java/io/BufferedWriter.java b/libjava/classpath/java/io/BufferedWriter.java new file mode 100644 index 00000000000..185a5344062 --- /dev/null +++ b/libjava/classpath/java/io/BufferedWriter.java @@ -0,0 +1,262 @@ +/* BufferedWriter.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class accumulates chars written in a buffer instead of immediately + * writing the data to the underlying output sink. The chars are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 25, 1998 + */ +public class BufferedWriter extends Writer +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * This is the underlying <code>Writer</code> to which this object + * sends its output. + */ + private Writer out; + + /** + * This is the internal char array used for buffering output before + * writing it. + */ + char[] buffer; + + /** + * This is the number of chars that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next char of data will be stored + */ + int count; + + /** + * This method initializes a new <code>BufferedWriter</code> instance + * that will write to the specified subordinate <code>Writer</code> + * and which will use a default buffer size of 8192 chars. + * + * @param out The underlying <code>Writer</code> to write data to + */ + public BufferedWriter (Writer out) + { + this (out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>BufferedWriter</code> instance + * that will write to the specified subordinate <code>Writer</code> + * and which will use the specified buffer size + * + * @param out The underlying <code>Writer</code> to write data to + * @param size The size of the internal buffer + */ + public BufferedWriter (Writer out, int size) + { + super(out.lock); + this.out = out; + this.buffer = new char[size]; + this.count = 0; + } + + /** + * This method flushes any remaining buffered chars then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + * @exception IOException If an error occurs. + */ + public void close () throws IOException + { + synchronized (lock) + { + // It is safe to call localFlush even if the stream is already + // closed. + localFlush (); + out.close(); + buffer = null; + } + } + + /** + * This method causes any currently buffered chars to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + localFlush (); + out.flush(); + } + } + + /** + * This method writes out a system depedent line separator sequence. The + * actual value written is detemined from the <xmp>line.separator</xmp> + * system property. + * + * @exception IOException If an error occurs + */ + public void newLine () throws IOException + { + write (System.getProperty("line.separator")); + } + + /** + * This method writes a single char of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param oneChar The char of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public void write (int oneChar) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + buffer[count++] = (char) oneChar; + if (count == buffer.length) + localFlush (); + } + } + + /** + * This method writes <code>len</code> chars from the char array + * <code>buf</code> starting at position <code>offset</code> in the buffer. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of chars to write. + * @param offset The index into the char array to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + // Bypass buffering if there is too much incoming data. + if (count + len > buffer.length) + { + localFlush (); + out.write(buf, offset, len); + } + else + { + System.arraycopy(buf, offset, buffer, count, len); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * <code>str</code> starting at position <code>offset</code> in the string. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param str The <code>String</code> to write. + * @param offset The index into the string to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + if (count + len > buffer.length) + { + localFlush (); + out.write(str, offset, len); + } + else + { + str.getChars(offset, offset + len, buffer, count); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + // This should only be called with the lock held. + private void localFlush () throws IOException + { + if (count > 0) + { + out.write(buffer, 0, count); + count = 0; + } + } +} diff --git a/libjava/classpath/java/io/ByteArrayInputStream.java b/libjava/classpath/java/io/ByteArrayInputStream.java new file mode 100644 index 00000000000..2bbde95b724 --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayInputStream.java @@ -0,0 +1,251 @@ +/* ByteArrayInputStream.java -- Read an array as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of bytes to be read as an input stream. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ByteArrayInputStream extends InputStream +{ + /** + * The array that contains the data supplied during read operations + */ + protected byte[] buf; + + /** + * The array index of the next byte to be read from the buffer + * <code>buf</code> + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int mark; + + /** + * This indicates the maximum number of bytes that can be read from this + * stream. It is the array index of the position after the last valid + * byte in the buffer <code>buf</code> + */ + protected int count; + + /** + * Create a new ByteArrayInputStream that will read bytes from the passed + * in byte array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as <code>ByteArrayInputStream(buf, 0, buf.length)</code>. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + */ + public ByteArrayInputStream(byte[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new ByteArrayInputStream that will read bytes from the + * passed in byte array. This stream will read from position + * <code>offset</code> in the array for a length of + * <code>length</code> bytes past <code>offset</code>. If the + * stream is reset to a position before <code>offset</code> then + * more than <code>length</code> bytes can be read from the stream. + * The <code>length</code> value should be viewed as the array index + * one greater than the last position in the buffer to read. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + * @param offset The index into the buffer to start reading bytes from + * @param length The number of bytes to read from the buffer + */ + public ByteArrayInputStream(byte[] buffer, int offset, int length) + { + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + mark = pos; + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to <code>count - pos</code>. + * + * @return The number of bytes that can be read from this stream + * before blocking, which is all of them + */ + public synchronized int available() + { + return count - pos; + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the <code>readlimit</code> parameter in this + * method does nothing as this stream is always capable of + * remembering all the bytes int it. + * <p> + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readLimit The number of bytes this stream must remember. + * This parameter is ignored. + */ + public synchronized void mark(int readLimit) + { + // readLimit is ignored per Java Class Lib. book, p.220. + mark = pos; + } + + /** + * This method overrides the <code>markSupported</code> method in + * <code>InputStream</code> in order to return <code>true</code> - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return <code>true</code> to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one byte from the stream. The <code>pos</code> + * counter is advanced to the next byte to be read. The byte read is + * returned as an int in the range of 0-255. If the stream position + * is already at the end of the buffer, no byte is read and a -1 is + * returned in order to indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public synchronized int read() + { + if (pos < count) + return ((int) buf[pos++]) & 0xFF; + return -1; + } + + /** + * This method reads bytes from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading + * the number of bytes requested if the end of the stream is + * encountered first. The actual number of bytes read is returned. + * If no bytes can be read because the stream is already at the end + * of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param buffer The array into which the bytes read should be stored. + * @param offset The offset into the array to start storing bytes + * @param length The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public synchronized int read(byte[] buffer, int offset, int length) + { + if (pos >= count) + return -1; + + int numBytes = Math.min(count - pos, length); + System.arraycopy(buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the <code>pos</code> variable equal to the + * <code>mark</code> variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public synchronized void reset() + { + pos = mark; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the <code>pos</code> + * value by the specified number of bytes. It this would exceed the + * length of the buffer, then only enough bytes are skipped to + * position the stream at the end of the buffer. The actual number + * of bytes skipped is returned. + * + * @param num The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public synchronized long skip(long num) + { + // Even though the var numBytes is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numBytes might as well just be a long. + long numBytes = Math.min((long) (count - pos), num < 0 ? 0L : num); + pos += numBytes; + return numBytes; + } +} diff --git a/libjava/classpath/java/io/ByteArrayOutputStream.java b/libjava/classpath/java/io/ByteArrayOutputStream.java new file mode 100644 index 00000000000..e996ebbc70f --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayOutputStream.java @@ -0,0 +1,283 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class allows data to be written to a byte array buffer and + * and then retrieved by an application. The internal byte array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + * <p> + * The size of the internal buffer defaults to 32 and it is resized + * by doubling the size of the buffer. This default size can be + * overridden by using the + * <code>gnu.java.io.ByteArrayOutputStream.initialBufferSize</code> + * property. + * <p> + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + * <p> + * Note that this class also has methods that convert the byte array + * buffer to a <code>String</code> using either the system default or an + * application specified character encoding. Thus it can handle + * multibyte character encodings. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public class ByteArrayOutputStream extends OutputStream +{ + /** + * This method initializes a new <code>ByteArrayOutputStream</code> + * with the default buffer size of 32 bytes. If a different initial + * buffer size is desired, see the constructor + * <code>ByteArrayOutputStream(int size)</code>. For applications + * where the source code is not available, the default buffer size + * can be set using the system property + * <code>gnu.java.io.ByteArrayOutputStream.initialBufferSize</code> + */ + public ByteArrayOutputStream () + { + this (initial_buffer_size); + } + + /** + * This method initializes a new <code>ByteArrayOutputStream</code> with + * a specified initial buffer size. + * + * @param size The initial buffer size in bytes + */ + public ByteArrayOutputStream (int size) + { + buf = new byte[size]; + count = 0; + } + + /** + * This method discards all of the bytes that have been written to + * the internal buffer so far by setting the <code>count</code> + * variable to 0. The internal buffer remains at its currently + * allocated size. + */ + public synchronized void reset () + { + count = 0; + } + + /** + * This method returns the number of bytes that have been written to + * the buffer so far. This is the same as the value of the protected + * <code>count</code> variable. If the <code>reset</code> method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of bytes that have been written to it. + * + * @return The number of bytes in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a byte array containing the bytes that have been + * written to this stream so far. This array is a copy of the valid + * bytes in the internal buffer and its length is equal to the number of + * valid bytes, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public synchronized byte[] toByteArray () + { + byte[] ret = new byte[count]; + System.arraycopy(buf, 0, ret, 0, count); + return ret; + } + + /** + * Returns the bytes in the internal array as a <code>String</code>. The + * bytes in the buffer are converted to characters using the system default + * encoding. There is an overloaded <code>toString()</code> method that + * allows an application specified character encoding to be used. + * + * @return A <code>String</code> containing the data written to this + * stream so far + */ + public String toString () + { + return new String (buf, 0, count); + } + + /** + * Returns the bytes in the internal array as a <code>String</code>. The + * bytes in the buffer are converted to characters using the specified + * encoding. + * + * @param enc The name of the character encoding to use + * + * @return A <code>String</code> containing the data written to this + * stream so far + * + * @exception UnsupportedEncodingException If the named encoding is + * not available + */ + public String toString (String enc) throws UnsupportedEncodingException + { + return new String (buf, 0, count, enc); + } + + /** + * This method returns the bytes in the internal array as a + * <code>String</code>. It uses each byte in the array as the low + * order eight bits of the Unicode character value and the passed in + * parameter as the high eight bits. + * <p> + * This method does not convert bytes to characters in the proper way and + * so is deprecated in favor of the other overloaded <code>toString</code> + * methods which use a true character encoding. + * + * @param hibyte The high eight bits to use for each character in + * the <code>String</code> + * + * @return A <code>String</code> containing the data written to this + * stream so far + * + * @deprecated + */ + public String toString (int hibyte) + { + return new String (buf, 0, count, hibyte); + } + + // Resize buffer to accommodate new bytes. + private void resize (int add) + { + if (count + add > buf.length) + { + int newlen = buf.length * 2; + if (count + add > newlen) + newlen = count + add; + byte[] newbuf = new byte[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * This method writes the writes the specified byte into the internal + * buffer. + * + * @param oneByte The byte to be read passed as an int + */ + public synchronized void write (int oneByte) + { + resize (1); + buf[count++] = (byte) oneByte; + } + + /** + * This method writes <code>len</code> bytes from the passed in array + * <code>buf</code> starting at index <code>offset</code> into the + * internal buffer. + * + * @param buffer The byte array to write data from + * @param offset The index into the buffer to start writing data from + * @param add The number of bytes to write + */ + public synchronized void write (byte[] buffer, int offset, int add) + { + // If ADD < 0 then arraycopy will throw the appropriate error for + // us. + if (add >= 0) + resize (add); + System.arraycopy(buffer, offset, buf, count, add); + count += add; + } + + /** + * This method writes all the bytes that have been written to this stream + * from the internal buffer to the specified <code>OutputStream</code>. + * + * @param out The <code>OutputStream</code> to write to + * + * @exception IOException If an error occurs + */ + public synchronized void writeTo (OutputStream out) throws IOException + { + out.write(buf, 0, count); + } + + /** + * The internal buffer where the data written is stored + */ + protected byte[] buf; + + /** + * The number of bytes that have been written to the buffer + */ + protected int count; + + /** + * The default initial buffer size. Specified by the JCL. + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + // The default buffer size which can be overridden by the user. + private static final int initial_buffer_size; + + static + { + int r + = Integer.getInteger ("gnu.java.io.ByteArrayOutputStream.initialBufferSize", + DEFAULT_INITIAL_BUFFER_SIZE).intValue (); + if (r <= 0) + r = DEFAULT_INITIAL_BUFFER_SIZE; + initial_buffer_size = r; + } +} diff --git a/libjava/classpath/java/io/CharArrayReader.java b/libjava/classpath/java/io/CharArrayReader.java new file mode 100644 index 00000000000..c14fa077592 --- /dev/null +++ b/libjava/classpath/java/io/CharArrayReader.java @@ -0,0 +1,305 @@ +/* CharArrayReader.java -- Read an array of characters as a stream + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of chars to be read as an input stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class CharArrayReader extends Reader +{ + /** + * The array that contains the data supplied during read operations + */ + protected char[] buf; + + /** + * The array index of the next char to be read from the buffer + * <code>buf</code> + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int markedPos; + + /** + * This indicates the maximum number of chars that can be read from this + * stream. It is the array index of the position after the last valid + * char in the buffer <code>buf</code> + */ + protected int count; + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as <code>CharArrayReader(buf, 0, buf.length)</code>. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + */ + public CharArrayReader(char[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from position + * <code>offset</code> in the array for a length of + * <code>length</code> chars past <code>offset</code>. If the + * stream is reset to a position before <code>offset</code> then + * more than <code>length</code> chars can be read from the stream. + * The <code>length</code> value should be viewed as the array index + * one greater than the last position in the buffer to read. + * <p> + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + * @param offset The index into the buffer to start reading chars from + * @param length The number of chars to read from the buffer + */ + public CharArrayReader(char[] buffer, int offset, int length) + { + super(); + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + markedPos = pos; + } + + /** + * This method closes the stream. + */ + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the <code>readlimit</code> parameter in this + * method does nothing as this stream is always capable of + * remembering all the chars int it. + * <p> + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readAheadLimit The number of chars this stream must + * remember. This parameter is ignored. + * + * @exception IOException If an error occurs + */ + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + // readAheadLimit is ignored per Java Class Lib. book, p. 318. + markedPos = pos; + } + } + + /** + * This method overrides the <code>markSupported</code> method in + * <code>Reader</code> in order to return <code>true</code> - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return <code>true</code> to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one char from the stream. The <code>pos</code> + * counter is advanced to the next char to be read. The char read + * is returned as an int in the range of 0-65535. If the stream + * position is already at the end of the buffer, no char is read and + * a -1 is returned in order to indicate the end of the stream. + * + * @return The char read, or -1 if end of stream + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < 0) + throw new ArrayIndexOutOfBoundsException(pos); + + if (pos < count) + return ((int) buf[pos++]) & 0xFFFF; + return -1; + } + } + + /** + * This method reads chars from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> chars. This method can return before reading + * the number of chars requested if the end of the stream is + * encountered first. The actual number of chars read is returned. + * If no chars can be read because the stream is already at the end + * of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param b The array into which the chars read should be stored. + * @param off The offset into the array to start storing chars + * @param len The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + */ + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new IndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int numChars = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, numChars); + pos += numChars; + return numChars; + } + } + + /** + * Return true if more characters are available to be read. + * + * @return <code>true</code> to indicate that this stream is ready + * to be read. + * + * @specnote The JDK 1.3 API docs are wrong here. This method will + * return false if there are no more characters available. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return (pos < count); + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the <code>pos</code> variable equal to the + * <code>mark</code> variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the <code>pos</code> value by the + * specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} diff --git a/libjava/classpath/java/io/CharArrayWriter.java b/libjava/classpath/java/io/CharArrayWriter.java new file mode 100644 index 00000000000..f9b338fe0cc --- /dev/null +++ b/libjava/classpath/java/io/CharArrayWriter.java @@ -0,0 +1,274 @@ +/* CharArrayWriter.java -- Write chars to a buffer + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows data to be written to a char array buffer and + * and then retrieved by an application. The internal char array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + * <p> + * The size of the internal buffer defaults to 32 and it is resized + * in increments of 1024 chars. This behavior can be over-ridden by using the + * following two properties: + * <p> + * <ul> + * <li><xmp>gnu.java.io.CharArrayWriter.initialBufferSize</xmp></li> + * <li><xmp>gnu.java.io.CharArrayWriter.bufferIncrementSize</xmp></li> + * </ul> + * <p> + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + * <p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class CharArrayWriter extends Writer +{ + /** + * The default initial buffer size + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + /** + * This method initializes a new <code>CharArrayWriter</code> with + * the default buffer size of 32 chars. If a different initial + * buffer size is desired, see the constructor + * <code>CharArrayWriter(int size)</code>. + */ + public CharArrayWriter () + { + this (DEFAULT_INITIAL_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>CharArrayWriter</code> with + * a specified initial buffer size. + * + * @param size The initial buffer size in chars + */ + public CharArrayWriter (int size) + { + super (); + buf = new char[size]; + } + + /** + * Closes the stream. This method is guaranteed not to free the contents + * of the internal buffer, which can still be retrieved. + */ + public void close () + { + } + + /** + * This method flushes all buffered chars to the stream. + */ + public void flush () + { + } + + /** + * This method discards all of the chars that have been written to the + * internal buffer so far by setting the <code>count</code> variable to + * 0. The internal buffer remains at its currently allocated size. + */ + public void reset () + { + synchronized (lock) + { + count = 0; + } + } + + /** + * This method returns the number of chars that have been written to + * the buffer so far. This is the same as the value of the protected + * <code>count</code> variable. If the <code>reset</code> method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of chars that have been written to it. + * + * @return The number of chars in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a char array containing the chars that have been + * written to this stream so far. This array is a copy of the valid + * chars in the internal buffer and its length is equal to the number of + * valid chars, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public char[] toCharArray () + { + synchronized (lock) + { + char[] nc = new char[count]; + System.arraycopy(buf, 0, nc, 0, count); + return nc; + } + } + + /** + * Returns the chars in the internal array as a <code>String</code>. The + * chars in the buffer are converted to characters using the system default + * encoding. There is an overloaded <code>toString()</code> method that + * allows an application specified character encoding to be used. + * + * @return A <code>String</code> containing the data written to this + * stream so far + */ + public String toString () + { + synchronized (lock) + { + return new String (buf, 0, count); + } + } + + /** + * This method writes the writes the specified char into the internal + * buffer. + * + * @param oneChar The char to be read passed as an int + */ + public void write (int oneChar) + { + synchronized (lock) + { + resize (1); + buf[count++] = (char) oneChar; + } + } + + /** + * This method writes <code>len</code> chars from the passed in array + * <code>buf</code> starting at index <code>offset</code> into that buffer + * + * @param buffer The char array to write data from + * @param offset The index into the buffer to start writing data from + * @param len The number of chars to write + */ + public void write (char[] buffer, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + System.arraycopy(buffer, offset, buf, count, len); + count += len; + } + } + + /** + * This method writes <code>len</code> chars from the passed in + * <code>String</code> <code>buf</code> starting at index + * <code>offset</code> into the internal buffer. + * + * @param str The <code>String</code> to write data from + * @param offset The index into the string to start writing data from + * @param len The number of chars to write + */ + public void write (String str, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + str.getChars(offset, offset + len, buf, count); + count += len; + } + } + + /** + * This method writes all the chars that have been written to this stream + * from the internal buffer to the specified <code>Writer</code>. + * + * @param out The <code>Writer</code> to write to + * + * @exception IOException If an error occurs + */ + public void writeTo (Writer out) throws IOException + { + synchronized (lock) + { + out.write(buf, 0, count); + } + } + + /** + * This private method makes the buffer bigger when we run out of room + * by allocating a larger buffer and copying the valid chars from the + * old array into it. This is obviously slow and should be avoided by + * application programmers by setting their initial buffer size big + * enough to hold everything if possible. + */ + private void resize (int len) + { + if (count + len >= buf.length) + { + int newlen = buf.length * 2; + if (count + len > newlen) + newlen = count + len; + char[] newbuf = new char[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * The internal buffer where the data written is stored + */ + protected char[] buf; + + /** + * The number of chars that have been written to the buffer + */ + protected int count; +} diff --git a/libjava/classpath/java/io/CharConversionException.java b/libjava/classpath/java/io/CharConversionException.java new file mode 100644 index 00000000000..a7a608429ef --- /dev/null +++ b/libjava/classpath/java/io/CharConversionException.java @@ -0,0 +1,73 @@ +/* CharConversionException.java -- Character conversion exceptions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate that a problem occurred with + * an attempted character conversion. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class CharConversionException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8680016352018427031L; + + /** + * Create an exception without a descriptive error message. + */ + public CharConversionException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public CharConversionException(String message) + { + super(message); + } +} // class CharConversionException diff --git a/libjava/classpath/java/io/Closeable.java b/libjava/classpath/java/io/Closeable.java new file mode 100644 index 00000000000..b8523d79e63 --- /dev/null +++ b/libjava/classpath/java/io/Closeable.java @@ -0,0 +1,63 @@ +/* Closeable.java -- Closeable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A <code>Closeable</code> class represents a stream of + * data, which can be closed when it is no longer needed. + * Closing a stream allows the resources it uses to be + * freed for an alternate use. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Closeable +{ + + /** + * Closes the stream represented by this class, thus freeing + * system resources. In that case that the stream is already + * in the closed state, this method has no effect. + * + * @throws IOException if an I/O error occurs in closing. + */ + void close() + throws IOException; + +} diff --git a/libjava/classpath/java/io/DataInput.java b/libjava/classpath/java/io/DataInput.java new file mode 100644 index 00000000000..45cb0c13025 --- /dev/null +++ b/libjava/classpath/java/io/DataInput.java @@ -0,0 +1,456 @@ +/* DataInput.java -- Interface for reading data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. */ + +/** + * This interface is implemented by classes that can data from streams + * into Java primitive types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface DataInput +{ + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code>. If the byte is non-zero, then + * the value returned is <code>true</code>. + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the <code>writeBoolean()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before + * reading the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + boolean readBoolean() throws EOFException, IOException; + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> interface. + * <p> + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + byte readByte() throws EOFException, IOException; + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> value from + * the stream. The value returned is in the range of 0 to 255. + * <p> + * This method can read an unsigned byte written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The unsigned bytes value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + int readUnsignedByte() throws EOFException, IOException; + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to a <code>char</code> in the following manner: + * <p> + * <code>(char)((byte1 << 8) + byte2)</code> + * <p> + * This method can read a <code>char</code> written by an object implementing + * the + * <code>writeChar()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + char readChar() throws EOFException, IOException; + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>short</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to a <code>short</code> in the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing + * the <code>writeShort()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + short readShort() throws EOFException, IOException; + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent the + * first and second byte read from the stream respectively, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object implementing + * the <code>writeShort()</code> method in the + * <code>DataOutput</code> + * interface. + * + * @return The unsigned short value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading + * the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + int readUnsignedShort() throws EOFException, IOException; + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> represent + * the first four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of -2147483648 to 2147483647. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the <code>writeInt()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + int readInt() throws EOFException, IOException; + + /** + * This method reads a Java <code>long</code> value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> represent + * the first eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code>(long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * </code> + * <p> + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the <code>writeLong()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + long readLong() throws EOFException, IOException; + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an <code>int</code> value from the stream by calling the + * <code>readInt()</code> method in this interface, then converts that + * <code>int</code> to a <code>float</code> using the + * <code>intBitsToFloat</code> method in the class + * <code>java.lang.Float</code>. + * <p> + * This method can read a <code>float</code> written by an object + * implementing + * the <code>writeFloat()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the + * float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + float readFloat() throws EOFException, IOException; + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>long</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then converts that + * <code>long</code> to a <code>double</code> using the + * <code>longBitsToDouble</code> method in the class + * <code>java.lang.Double</code>. + * <p> + * This method can read a <code>double</code> written by an object + * implementing the <code>writeDouble()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading the + * double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + double readDouble() throws EOFException, IOException; + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * <code>char</code> + * values by treating the byte read as the low eight bits of the + * <code>char</code> and using 0 as the high eight bits. Because of this, + * it does not support the full 16-bit Unicode character set. + * <P> + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a + * <code>String</code>. + * A line terminator is a byte sequence consisting of either + * <code>\r</code>, <code>\n</code> or <code>\r\n</code>. These termination + * charaters are discarded and are not returned as part of the string. + * A line is also terminated by an end of file condition. + * <p> + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + */ + String readLine() throws IOException; + + /** + * This method reads a <code>String</code> from an input stream that is + * encoded in a modified UTF-8 format. This format has a leading two byte + * sequence that contains the remaining number of bytes to read. + * This two byte + * sequence is read using the <code>readUnsignedShort()</code> method of this + * interface. + * + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into <code>char</code> values. These + * <code>char</code> values are encoded in the stream using either a one, + * two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + * <p> + * If the first byte has a high order bit of 0, then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, it would + * be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> are the first + * two bytes read respectively, and the high order bits of them match the + * patterns which indicate a two byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) + (byte2 & 0x3F))</code> + * <p> + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code>, <code>byte2</code>, and + * <code>byte3</code> are the three bytes read, and the high order bits of + * them match the patterns which indicate a three byte character encoding, + * then they would be converted to a Java <code>char</code> like so: + * + * <code> + * (char)(((byte1 & 0x0F) << 12) + ((byte2 & 0x3F) + (byte3 & 0x3F)) + * </code> + * + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of <code>\<llll>u0000</code> which is encoded as two bytes. + * This is a modification of the UTF standard used to prevent C language + * style <code>NUL</code> values from appearing in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code>. + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + String readUTF() throws EOFException, UTFDataFormatException, IOException; + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param buf The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf) throws EOFException, IOException; + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code>. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf, int offset, int len) + throws EOFException, IOException; + + /** + * This method skips and discards the specified number of bytes in an + * input stream. Note that this method may skip less than the requested + * number of bytes. The actual number of bytes skipped is returned. + * No bytes are skipped if a negative number is passed to this method. + * + * @param numBytes The number of bytes to skip + * + * @return The number of bytes actually skipped, which will always be + * <code>numBytes</code> + * + * @exception EOFException If end of file is reached before all bytes can be + * skipped + * @exception IOException If any other error occurs + */ + int skipBytes(int numBytes) throws EOFException, IOException; + +} // interface DataInput diff --git a/libjava/classpath/java/io/DataInputStream.java b/libjava/classpath/java/io/DataInputStream.java new file mode 100644 index 00000000000..d2604b51ffa --- /dev/null +++ b/libjava/classpath/java/io/DataInputStream.java @@ -0,0 +1,739 @@ +/* DataInputStream.java -- FilteredInputStream that implements DataInput + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of <code>FilteredInputStream</code> implements the + * <code>DataInput</code> interface that provides method for reading primitive + * Java data types from a stream. + * + * @see DataInput + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date October 20, 1998. + */ +public class DataInputStream extends FilterInputStream implements DataInput +{ + // Byte buffer, used to make primitive read calls more efficient. + byte[] buf = new byte [8]; + + /** + * This constructor initializes a new <code>DataInputStream</code> + * to read from the specified subordinate stream. + * + * @param in The subordinate <code>InputStream</code> to read from + */ + public DataInputStream (InputStream in) + { + super (in); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to fill the buffer completely, but + * may return a short count if there is insufficient data remaining to be + * read to fill the buffer. + * + * @param b The buffer into which bytes will be read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b) throws IOException + { + return in.read (b, 0, b.length); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to read <code>len</code> bytes and + * will start storing them at position <code>off</code> into the buffer. + * This method can return a short count if there is insufficient data + * remaining to be read to complete the desired read length. + * + * @param b The buffer into which bytes will be read. + * @param off The offset into the buffer to start storing bytes. + * @param len The requested number of bytes to read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b, int off, int len) throws IOException + { + return in.read (b, off, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code>. If the byte is non-zero, then + * the value returned is <code>true</code>. + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the <code>writeBoolean()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before reading + * the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + public final boolean readBoolean () throws IOException + { + return convertToBoolean (in.read ()); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the <code>writeByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final byte readByte () throws IOException + { + return convertToByte (in.read ()); + } + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code>. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to a <code>char</code> in + * the following manner: + * <p> + * <code>(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * This method can read a <code>char</code> written by an object + * implementing the <code>writeChar()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + public final char readChar () throws IOException + { + readFully (buf, 0, 2); + return convertToChar (buf); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>long</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then converts + * that <code>long</code> to a <code>double</code> using the + * <code>longBitsToDouble</code> method in the class + * <code>java.lang.Double</code> + * <p> + * This method can read a <code>double</code> written by an object + * implementing the <code>writeDouble()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + public final double readDouble () throws IOException + { + return Double.longBitsToDouble (readLong ()); + } + + /** + * This method reads a Java float value from an input stream. It + * operates by first reading an <code>int</code> value from the + * stream by calling the <code>readInt()</code> method in this + * interface, then converts that <code>int</code> to a + * <code>float</code> using the <code>intBitsToFloat</code> method + * in the class <code>java.lang.Float</code> + * <p> + * This method can read a <code>float</code> written by an object + * implementing the <code>writeFloat()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + public final float readFloat () throws IOException + { + return Float.intBitsToFloat (readInt ()); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param b The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] b) throws IOException + { + readFully (b, 0, b.length); + } + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code>. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buf, int offset, int len) throws IOException + { + if (len < 0) + throw new IndexOutOfBoundsException("Negative length: " + len); + + while (len > 0) + { + // in.read will block until some data is available. + int numread = in.read (buf, offset, len); + if (numread < 0) + throw new EOFException (); + len -= numread; + offset += numread; + } + } + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> represent + * the first four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of -2147483648 to 2147483647. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the <code>writeInt()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + public final int readInt () throws IOException + { + readFully (buf, 0, 4); + return convertToInt (buf); + } + + /** + * This method reads the next line of text data from an input + * stream. It operates by reading bytes and converting those bytes + * to <code>char</code> values by treating the byte read as the low + * eight bits of the <code>char</code> and using 0 as the high eight + * bits. Because of this, it does not support the full 16-bit + * Unicode character set. + * <p> + * The reading of bytes ends when either the end of file or a line + * terminator is encountered. The bytes read are then returned as a + * <code>String</code> A line terminator is a byte sequence + * consisting of either <code>\r</code>, <code>\n</code> or + * <code>\r\n</code>. These termination charaters are discarded and + * are not returned as part of the string. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeLine()</code> method in <code>DataOutput</code>. + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + * + * @see DataOutput + * + * @deprecated + */ + public final String readLine() throws IOException + { + StringBuffer strb = new StringBuffer(); + + while (true) + { + int c = in.read(); + if (c == -1) // got an EOF + return strb.length() > 0 ? strb.toString() : null; + if (c == '\r') + { + int next_c = in.read(); + if (next_c != '\n' && next_c != -1) + { + if (!(in instanceof PushbackInputStream)) + in = new PushbackInputStream(in); + ((PushbackInputStream) in).unread(next_c); + } + break; + } + if (c == '\n') + break; + strb.append((char) c); + } + + return strb.length() > 0 ? strb.toString() : ""; + } + + /** + * This method reads a Java <code>long</code> value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code>. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> represent + * the first eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code>(long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * </code> + * <p> + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the <code>writeLong()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + public final long readLong () throws IOException + { + readFully (buf, 0, 8); + return convertToLong (buf); + } + + /** + * This method reads a signed 16-bit value into a Java in from the + * stream. It operates by reading two bytes from the stream and + * converting them to a single 16-bit Java <code>short</code>. The + * two bytes are stored most significant byte first (i.e., "big + * endian") regardless of the native host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to a <code>short</code>. in + * the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing the <code>writeShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final short readShort () throws IOException + { + readFully (buf, 0, 2); + return convertToShort (buf); + } + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> + * value from the stream. The value returned is in the range of 0 to + * 255. + * <p> + * This method can read an unsigned byte written by an object + * implementing the <code>writeUnsignedByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned bytes value read as a Java <code>int</code>. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final int readUnsignedByte () throws IOException + { + return convertToUnsignedByte (in.read ()); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first and second byte read from the stream + * respectively, they will be transformed to an <code>int</code> in + * the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object + * implementing the <code>writeUnsignedShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned short value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final int readUnsignedShort () throws IOException + { + readFully (buf, 0, 2); + return convertToUnsignedShort (buf); + } + + /** + * This method reads a <code>String</code> from an input stream that + * is encoded in a modified UTF-8 format. This format has a leading + * two byte sequence that contains the remaining number of bytes to + * read. This two byte sequence is read using the + * <code>readUnsignedShort()</code> method of this interface. + * <p> + * After the number of remaining bytes have been determined, these + * bytes are read an transformed into <code>char</code> values. + * These <code>char</code> values are encoded in the stream using + * either a one, two, or three byte format. The particular format + * in use can be determined by examining the first byte read. + * <p> + * If the first byte has a high order bit of 0, then that character + * consists on only one byte. This character value consists of + * seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, + * it would be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> are + * the first two bytes read respectively, and the high order bits of + * them match the patterns which indicate a two byte character + * encoding, then they would be converted to a Java + * <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))</code> + * <p> + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> <code>byte2</code> and + * <code>byte3</code> are the three bytes read, and the high order + * bits of them match the patterns which indicate a three byte + * character encoding, then they would be converted to a Java + * <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F))</code> + * <p> + * Note that all characters are encoded in the method that requires + * the fewest number of bytes with the exception of the character + * with the value of <code>\u0000</code> which is encoded as two + * bytes. This is a modification of the UTF standard used to + * prevent C language style <code>NUL</code> values from appearing + * in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code> + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading + * the String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + public final String readUTF () throws IOException + { + return readUTF (this); + } + + /** + * This method reads a String encoded in UTF-8 format from the + * specified <code>DataInput</code> source. + * + * @param in The <code>DataInput</code> source to read from + * + * @return The String read from the source + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public static final String readUTF(DataInput in) throws IOException + { + final int UTFlen = in.readUnsignedShort (); + byte[] buf = new byte [UTFlen]; + + // This blocks until the entire string is available rather than + // doing partial processing on the bytes that are available and then + // blocking. An advantage of the latter is that Exceptions + // could be thrown earlier. The former is a bit cleaner. + in.readFully (buf, 0, UTFlen); + + return convertFromUTF (buf); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * This method will not skip any bytes if passed a negative number of bytes + * to skip. + * + * @param n The requested number of bytes to skip. + * + * @return The requested number of bytes to skip. + * + * @exception IOException If an error occurs. + * @specnote The JDK docs claim that this returns the number of bytes + * actually skipped. The JCL claims that this method can throw an + * EOFException. Neither of these appear to be true in the JDK 1.3's + * implementation. This tries to implement the actual JDK behaviour. + */ + public final int skipBytes (int n) throws IOException + { + if (n <= 0) + return 0; + try + { + return (int) in.skip (n); + } + catch (EOFException x) + { + // do nothing. + } + return n; + } + + static boolean convertToBoolean (int b) throws EOFException + { + if (b < 0) + throw new EOFException (); + + return (b != 0); + } + + static byte convertToByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (byte) i; + } + + static int convertToUnsignedByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (i & 0xFF); + } + + static char convertToChar (byte[] buf) + { + return (char) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static short convertToShort (byte[] buf) + { + return (short) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static int convertToUnsignedShort (byte[] buf) + { + return (((buf [0] & 0xff) << 8) + | (buf [1] & 0xff)); + } + + static int convertToInt (byte[] buf) + { + return (((buf [0] & 0xff) << 24) + | ((buf [1] & 0xff) << 16) + | ((buf [2] & 0xff) << 8) + | (buf [3] & 0xff)); + } + + static long convertToLong (byte[] buf) + { + return (((long)(buf [0] & 0xff) << 56) | + ((long)(buf [1] & 0xff) << 48) | + ((long)(buf [2] & 0xff) << 40) | + ((long)(buf [3] & 0xff) << 32) | + ((long)(buf [4] & 0xff) << 24) | + ((long)(buf [5] & 0xff) << 16) | + ((long)(buf [6] & 0xff) << 8) | + ((long)(buf [7] & 0xff))); + } + + // FIXME: This method should be re-thought. I suspect we have multiple + // UTF-8 decoders floating around. We should use the standard charset + // converters, maybe and adding a direct call into one of the new + // NIO converters for a super-fast UTF8 decode. + static String convertFromUTF (byte[] buf) + throws EOFException, UTFDataFormatException + { + // Give StringBuffer an initial estimated size to avoid + // enlarge buffer frequently + StringBuffer strbuf = new StringBuffer (buf.length / 2 + 2); + + for (int i = 0; i < buf.length; ) + { + if ((buf [i] & 0x80) == 0) // bit pattern 0xxxxxxx + strbuf.append ((char) (buf [i++] & 0xFF)); + else if ((buf [i] & 0xE0) == 0xC0) // bit pattern 110xxxxx + { + if (i + 1 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append((char) (((buf [i++] & 0x1F) << 6) + | (buf [i++] & 0x3F))); + } + else if ((buf [i] & 0xF0) == 0xE0) // bit pattern 1110xxxx + { + if (i + 2 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80 + || (buf [i + 2] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append ((char) (((buf [i++] & 0x0F) << 12) + | ((buf [i++] & 0x3F) << 6) + | (buf [i++] & 0x3F))); + } + else // must be ((buf [i] & 0xF0) == 0xF0 || (buf [i] & 0xC0) == 0x80) + throw new UTFDataFormatException (); // bit patterns 1111xxxx or + // 10xxxxxx + } + + return strbuf.toString (); + } +} diff --git a/libjava/classpath/java/io/DataOutput.java b/libjava/classpath/java/io/DataOutput.java new file mode 100644 index 00000000000..0d436233218 --- /dev/null +++ b/libjava/classpath/java/io/DataOutput.java @@ -0,0 +1,326 @@ +/* DataOutput.java -- Interface for writing data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This interface is implemented by classes that can wrte data to streams + * from Java primitive types. This data can subsequently be read back + * by classes implementing the <code>DataInput</code> interface. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see DataInput + */ +public interface DataOutput +{ + /** + * This method writes a Java boolean value to an output stream. If + * <code>value</code> is <code>true</code>, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the <code>readBoolean</code> + * method in <code>DataInput</code>. + * + * @param value The boolean value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + void writeBoolean(boolean value) throws IOException; + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * <code>int</code> value passed. + * + * The value written can be read using the <code>readByte</code> or + * <code>readUnsignedByte</code> methods in <code>DataInput</code>. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + void writeByte(int value) throws IOException; + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readChar</code> + * method in <code>DataInput</code>. + * + * @param value The char value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + void writeChar(int value) throws IOException; + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readShort</code> and + * <code>readUnsignedShort</code> methods in <code>DataInput</code>. + * + * @param value The int value to write as a 16-bit value + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + void writeShort(int value) throws IOException; + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF000000) >> 24);<br> + * byte1 = (byte)((value & 0x00FF0000) >> 16);<br> + * byte2 = (byte)((value & 0x0000FF00) >> 8);<br> + * byte3 = (byte)(value & 0x000000FF);</code> + * <p> + * + * The value written can be read using the <code>readInt</code> + * method in <code>DataInput</code>. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + void writeInt(int value) throws IOException; + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00000000000000L) >> 56);<br> + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);<br> + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);<br> + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);<br> + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);<br> + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);<br> + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);<br> + * byte7 = (byte)(value & 0x00000000000000FFL);</code> + * <p> + * + * The value written can be read using the <code>readLong</code> + * method in <code>DataInput</code>. + * + * @param value The long value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + void writeLong(long value) throws IOException; + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * The value written can be read using the <code>readFloat</code> + * method in <code>DataInput</code>. + * + * @param value The float value to write + * + * @exception IOException If an error occurs + * + * @see #writeInt + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + void writeFloat(float value) throws IOException; + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * The value written can be read using the <code>readDouble</code> + * method in <code>DataInput</code>. + * + * @param value The double value to write + * + * @exception IOException If any other error occurs + * + * @see #writeLong + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + void writeDouble(double value) throws IOException; + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the + * <code>String</code>. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The <code>String</code> to write + * + * @exception IOException If an error occurs + */ + void writeBytes(String value) throws IOException; + + /** + * This method writes all the characters of a <code>String</code> to an + * output stream as an array of <code>char</code>'s. Each character + * is written using the method specified in the <code>writeChar</code> + * method. + * + * @param value The String to write + * + * @exception IOException If an error occurs + * + * @see #writeChar(int) + */ + void writeChars(String value) throws IOException; + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. This is written in the form of a Java + * <code>short</code> value in the same manner used by the + * <code>writeShort</code> method. Note that this is the number of + * bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <code>\u007F</code>, one byte is used. + * The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the <code>readUTF</code> + * method in <code>DataInput</code>. + * + * @param value The <code>String</code> to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + void writeUTF(String value) throws IOException; + + /** + * This method writes an 8-bit value (passed into the method as a Java + * <code>int</code>) to an output stream. The low 8 bits of the + * passed value are written. + * + * @param value The <code>byte</code> to write to the output stream + * + * @exception IOException If an error occurs + */ + void write(int value) throws IOException; + + /** + * This method writes the raw byte array passed in to the output stream. + * + * @param buf The byte array to write + * + * @exception IOException If an error occurs + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes raw bytes from the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes + * written will be exactly <code>len</code>. + * + * @param buf The buffer from which to write the data + * @param offset The offset into the buffer to start writing data from + * @param len The number of bytes to write from the buffer to the output + * stream + * + * @exception IOException If any other error occurs + */ + void write(byte[] buf, int offset, int len) throws IOException; + +} // interface DataOutput + diff --git a/libjava/classpath/java/io/DataOutputStream.java b/libjava/classpath/java/io/DataOutputStream.java new file mode 100644 index 00000000000..39f7ed1ff24 --- /dev/null +++ b/libjava/classpath/java/io/DataOutputStream.java @@ -0,0 +1,455 @@ +/* DataOutputStream.java -- Writes primitive Java datatypes to streams + Copyright (C) 1998, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class provides a mechanism for writing primitive Java datatypes + * to an <code>OutputStream</code> in a portable way. Data written to + * a stream using this class can be read back in using the + * <code>DataInputStream</code> class on any platform. + * + * @see DataInputStream + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class DataOutputStream extends FilterOutputStream implements DataOutput +{ + /** + * This is the total number of bytes that have been written to the + * stream by this object instance. + */ + protected int written; + + /** + * This method initializes an instance of <code>DataOutputStream</code> to + * write its data to the specified underlying <code>OutputStream</code> + * + * @param out The subordinate <code>OutputStream</code> to which this + * object will write + */ + public DataOutputStream (OutputStream out) + { + super (out); + written = 0; + } + + /** + * This method flushes any unwritten bytes to the underlying stream. + * + * @exception IOException If an error occurs. + */ + public void flush () throws IOException + { + out.flush(); + } + + /** + * This method returns the total number of bytes that have been written to + * the underlying output stream so far. This is the value of the + * <code>written</code> instance variable + * + * @return The number of bytes written to the stream. + */ + public final int size () + { + return written; + } + + /** + * This method writes the specified byte (passed as an <code>int</code>) + * to the underlying output stream. + * + * @param value The <code>byte</code> to write, passed as an <code>int</code>. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (int value) throws IOException + { + out.write (value); + ++written; + } + + /** + * This method writes <code>len</code> bytes from the specified byte array + * <code>buf</code> starting at position <code>offset</code> into the + * buffer to the underlying output stream. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (byte[] buf, int offset, int len) + throws IOException + { + out.write(buf, offset, len); + written += len; + } + + /** + * This method writes a Java boolean value to an output stream. If + * <code>value</code> is <code>true</code>, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the <code>readBoolean</code> + * method in <code>DataInput</code>. + * + * @param value The <code>boolean</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + public final void writeBoolean (boolean value) throws IOException + { + write (value ? 1 : 0); + } + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * <code>int</code> value passed. + * + * The value written can be read using the <code>readByte</code> or + * <code>readUnsignedByte</code> methods in <code>DataInput</code>. + * + * @param value The <code>byte</code> to write to the stream, passed as + * the low eight bits of an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + public final void writeByte (int value) throws IOException + { + write (value & 0xff); + } + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readShort</code> and + * <code>readUnsignedShort</code> methods in <code>DataInput</code>. + * + * @param value The <code>short</code> value to write to the stream, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + public final synchronized void writeShort (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the <code>int</code> + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00) >> 8);<br> + * byte1 = (byte)(value & 0x00FF);</code> + * <p> + * + * The value written can be read using the <code>readChar</code> + * method in <code>DataInput</code>. + * + * @param value The <code>char</code> value to write, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + public final synchronized void writeChar (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF000000) >> 24);<br> + * byte1 = (byte)((value & 0x00FF0000) >> 16);<br> + * byte2 = (byte)((value & 0x0000FF00) >> 8);<br> + * byte3 = (byte)(value & 0x000000FF);</code> + * <p> + * + * The value written can be read using the <code>readInt</code> + * method in <code>DataInput</code>. + * + * @param value The <code>int</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + public final synchronized void writeInt (int value) throws IOException + { + write ((byte) (0xff & (value >> 24))); + write ((byte) (0xff & (value >> 16))); + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + * <p> + * <code>byte0 = (byte)((value & 0xFF00000000000000L) >> 56);<br> + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);<br> + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);<br> + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);<br> + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);<br> + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);<br> + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);<br> + * byte7 = (byte)(value & 0x00000000000000FFL);</code> + * <p> + * + * The value written can be read using the <code>readLong</code> + * method in <code>DataInput</code>. + * + * @param value The <code>long</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + public final synchronized void writeLong (long value) throws IOException + { + write ((byte) (0xff & (value >> 56))); + write ((byte) (0xff & (value>> 48))); + write ((byte) (0xff & (value>> 40))); + write ((byte) (0xff & (value>> 32))); + write ((byte) (0xff & (value>> 24))); + write ((byte) (0xff & (value>> 16))); + write ((byte) (0xff & (value>> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * The value written can be read using the <code>readFloat</code> + * method in <code>DataInput</code>. + * + * @param value The <code>float</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeInt + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + public final void writeFloat (float value) throws IOException + { + writeInt (Float.floatToIntBits (value)); + } + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * The value written can be read using the <code>readDouble</code> + * method in <code>DataInput</code>. + * + * @param value The <code>double</code> value to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeLong + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + public final void writeDouble (double value) throws IOException + { + writeLong (Double.doubleToLongBits (value)); + } + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the + * <code>String</code>. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeByte (value.charAt(i)); + } + + /** + * This method writes all the characters of a <code>String</code> to an + * output stream as an array of <code>char</code>'s. Each character + * is written using the method specified in the <code>writeChar</code> + * method. + * + * @param value The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + * + * @see writeChar + */ + public final void writeChars (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeChar (value.charAt(i)); + } + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <\u007F>, one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the <code>readUTF</code> + * method in <code>DataInput</code>. + * + * @param value The <code>String</code> to write to the output in UTF format + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public final synchronized void writeUTF(String value) throws IOException + { + int len = value.length(); + int sum = 0; + + for (int i = 0; i < len && sum <= 65535; ++i) + { + char c = value.charAt(i); + if (c >= '\u0001' && c <= '\u007f') + sum += 1; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + sum += 2; + else + sum += 3; + } + + if (sum > 65535) + throw new UTFDataFormatException (); + + int pos = 0; + byte[] buf = new byte[sum]; + + for (int i = 0; i < len; ++i) + { + char c = value.charAt(i); + if (c >= '\u0001' && c <= '\u007f') + buf[pos++] = (byte) c; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + { + buf[pos++] = (byte) (0xc0 | (0x1f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + else + { + // JSL says the first byte should be or'd with 0xc0, but + // that is a typo. Unicode says 0xe0, and that is what is + // consistent with DataInputStream. + buf[pos++] = (byte) (0xe0 | (0x0f & (c >> 12))); + buf[pos++] = (byte) (0x80 | (0x3f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + } + + writeShort (sum); + write(buf, 0, sum); + } + +} // class DataOutputStream + diff --git a/libjava/classpath/java/io/DeleteFileHelper.java b/libjava/classpath/java/io/DeleteFileHelper.java new file mode 100644 index 00000000000..d73628c4973 --- /dev/null +++ b/libjava/classpath/java/io/DeleteFileHelper.java @@ -0,0 +1,109 @@ +/* DeleteFileHelper.java -- Helper class to delete files on VM exit + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Jeroen Frijters (jeroen@sumatra.nl) + * @author Michael Koch (konqueror@gmx.de) + */ +final class DeleteFileHelper extends Thread +{ + private static ArrayList filesToDelete; + + static synchronized void add(File file) + { + if (filesToDelete == null) + { + filesToDelete = new ArrayList(); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Runtime.getRuntime().addShutdownHook(new DeleteFileHelper()); + } + catch (IllegalStateException e) + { + // Shutdown is already in progress, so we can't + // register ours. + } + + return null; + } + }); + } + + filesToDelete.add(file); + } + + private static synchronized void deleteFiles() + { + Iterator it = filesToDelete.iterator(); + + while (it.hasNext()) + { + try + { + File file = (File) it.next(); + file.delete(); + } + catch (Exception e) + { + // Do nothing here. + } + } + } + + // Package-private to avoid a trampoline constructor. + DeleteFileHelper() + { + } + + public void run() + { + deleteFiles(); + } +} diff --git a/libjava/classpath/java/io/EOFException.java b/libjava/classpath/java/io/EOFException.java new file mode 100644 index 00000000000..cfedb7d9eb0 --- /dev/null +++ b/libjava/classpath/java/io/EOFException.java @@ -0,0 +1,76 @@ +/* EOFException.java -- unexpected end of file exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the end of the file or stream was + * encountered unexpectedly. This is not the normal way that an EOF + * condition is reported; such as a special value like -1 being returned. + * However, certain types of streams expecting certain data in a certain + * format might reach EOF before reading their expected data pattern and + * thus throw this exception. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class EOFException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6433858223774886977L; + + /** + * Create an exception without a descriptive error message. + */ + public EOFException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public EOFException(String message) + { + super(message); + } +} // class EOFException diff --git a/libjava/classpath/java/io/Externalizable.java b/libjava/classpath/java/io/Externalizable.java new file mode 100644 index 00000000000..113c19ff60f --- /dev/null +++ b/libjava/classpath/java/io/Externalizable.java @@ -0,0 +1,107 @@ +/* Externalizable.java -- Interface for saving and restoring object data + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface provides a way that classes can completely control how + * the data of their object instances are written and read to and from + * streams. It has two methods which are used to write the data to a stream + * and to read the data from a stream. The read method must read the data + * in exactly the way it was written by the write method. + * <p> + * Note that classes which implement this interface must take into account + * that all superclass data must also be written to the stream as well. + * The class implementing this interface must figure out how to make that + * happen. + * <p> + * This interface can be used to provide object persistence. When an + * object is to be stored externally, the <code>writeExternal</code> method is + * called to save state. When the object is restored, an instance is + * created using the default no-argument constructor and the + * <code>readExternal</code> method is used to restore the state. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Externalizable extends Serializable +{ + /** + * This method restores an object's state by reading in the instance data + * for the object from the passed in stream. Note that this stream is not + * a subclass of <code>InputStream</code>, but rather is a class that + * implements + * the <code>ObjectInput</code> interface. That interface provides a + * mechanism for + * reading in Java data types from a stream. + * <p> + * Note that this method must be compatible with <code>writeExternal</code>. + * It must read back the exact same types that were written by that + * method in the exact order they were written. + * <p> + * If this method needs to read back an object instance, then the class + * for that object must be found and loaded. If that operation fails, + * then this method throws a <code>ClassNotFoundException</code> + * + * @param in An <code>ObjectInput</code> instance for reading in the object + * state + * + * @exception ClassNotFoundException If the class of an object being + * restored cannot be found + * @exception IOException If any other error occurs + */ + void readExternal(ObjectInput in) + throws ClassNotFoundException, IOException; + + /** + * This method is responsible for writing the instance data of an object + * to the passed in stream. Note that this stream is not a subclass of + * <code>OutputStream</code>, but rather is a class that implements the + * <code>ObjectOutput</code> interface. That interface provides a + * number of methods + * for writing Java data values to a stream. + * <p> + * Not that the implementation of this method must be coordinated with + * the implementation of <code>readExternal</code>. + * + * @param out An <code>ObjectOutput</code> instance for writing the + * object state + * + * @exception IOException If an error occurs + */ + void writeExternal(ObjectOutput out) throws IOException; +} diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java new file mode 100644 index 00000000000..3b747e6bd03 --- /dev/null +++ b/libjava/classpath/java/io/File.java @@ -0,0 +1,1357 @@ +/* File.java -- Class representing a file on disk + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.SystemProperties; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.3. + */ + +/** + * This class represents a file or directory on a local disk. It provides + * facilities for dealing with a variety of systems that use various + * types of path separators ("/" versus "\", for example). It also + * contains method useful for creating and deleting files and directories. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class File implements Serializable, Comparable +{ + private static final long serialVersionUID = 301077366599181567L; + + /** + * This is the path separator string for the current host. This field + * contains the value of the <code>file.separator</code> system property. + * An example separator string would be "/" on the GNU system. + */ + public static final String separator = SystemProperties.getProperty("file.separator"); + private static final String dupSeparator = separator + separator; + + /** + * This is the first character of the file separator string. On many + * hosts (for example, on the GNU system), this represents the entire + * separator string. The complete separator string is obtained from the + * <code>file.separator</code>system property. + */ + public static final char separatorChar = separator.charAt(0); + + /** + * This is the string that is used to separate the host name from the + * path name in paths than include the host name. It is the value of + * the <code>path.separator</code> system property. + */ + public static final String pathSeparator + = SystemProperties.getProperty("path.separator"); + + /** + * This is the first character of the string used to separate the host name + * from the path name in paths that include a host. The separator string + * is taken from the <code>path.separator</code> system property. + */ + public static final char pathSeparatorChar = pathSeparator.charAt(0); + + /** + * This is the path to the file set when the object is created. It + * may be an absolute or relative path name. + */ + private String path; + + /** + * This method tests whether or not the current thread is allowed to + * to read the file pointed to by this object. This will be true if and + * and only if 1) the file exists and 2) the <code>SecurityManager</code> + * (if any) allows access to the file via it's <code>checkRead</code> + * method 3) the file is readable. + * + * @return <code>true</code> if reading is allowed, + * <code>false</code> otherwise + * + * @exception SecurityException If the <code>SecurityManager</code> + * does not allow access to the file + */ + public boolean canRead() + { + // Test for existence. This also does the SecurityManager check + if (!exists()) + return false; + + return VMFile.canRead(path); + } + + /** + * This method test whether or not the current thread is allowed to + * write to this object. This will be true if and only if 1) The + * <code>SecurityManager</code> (if any) allows write access to the + * file and 2) The file exists and 3) The file is writable. To determine + * whether or not a non-existent file can be created, check the parent + * directory for write access. + * + * @return <code>true</code> if writing is allowed, <code>false</code> + * otherwise + * + * @exception SecurityException If the <code>SecurityManager</code> + * does not allow access to the file + */ + public boolean canWrite() + { + // First do a SecurityCheck before doing anything else. + checkWrite(); + + // Test for existence. This is required by the spec + if (! VMFile.exists(path)) + return false; + + if (VMFile.isDirectory(path)) + return VMFile.canWriteDirectory(this); + else + return VMFile.canWrite(path); + } + + /** + * This method creates a new file of zero length with the same name as + * the path of this <code>File</code> object if an only if that file + * does not already exist. + * <p> + * A <code>SecurityManager.checkWrite</code> check is done prior + * to performing this action. + * + * @return <code>true</code> if the file was created, <code>false</code> if + * the file alread existed. + * + * @exception IOException If an I/O error occurs + * @exception SecurityException If the <code>SecurityManager</code> will + * not allow this operation to be performed. + * + * @since 1.2 + */ + public boolean createNewFile() throws IOException + { + checkWrite(); + return VMFile.create(path); + } + /** + * This method deletes the file represented by this object. If this file + * is a directory, it must be empty in order for the delete to succeed. + * + * @return <code>true</code> if the file was deleted, <code>false</code> + * otherwise + * + * @exception SecurityException If deleting of the file is not allowed + */ + public synchronized boolean delete() + { + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkDelete(path); + + return VMFile.delete(path); + } + + /** + * This method tests two <code>File</code> objects for equality by + * comparing the path of the specified <code>File</code> against the path + * of this object. The two objects are equal if an only if 1) The + * argument is not null 2) The argument is a <code>File</code> object and + * 3) The path of the <code>File</code>argument is equal to the path + * of this object. + * <p> + * The paths of the files are determined by calling the + * <code>getPath()</code> + * method on each object. + * + * @return <code>true</code> if the two objects are equal, + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof File)) + return false; + + File other = (File) obj; + + if (VMFile.IS_CASE_SENSITIVE) + return path.equals(other.path); + else + return path.equalsIgnoreCase(other.path); + } + + /** + * This method tests whether or not the file represented by the object + * actually exists on the filesystem. + * + * @return <code>true</code> if the file exists, <code>false</code>otherwise. + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean exists() + { + checkRead(); + return VMFile.exists(path); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file with the specified path. + * + * @param name The path name of the file + */ + public File(String name) + { + path = normalizePath (name); + } + + // Remove duplicate and redundant separator characters. + private String normalizePath(String p) + { + // On Windows, convert any '/' to '\'. This appears to be the same logic + // that Sun's Win32 Java performs. + if (separatorChar == '\\') + { + p = p.replace ('/', '\\'); + // We have to special case the "\c:" prefix. + if (p.length() > 2 && p.charAt(0) == '\\' && + ((p.charAt(1) >= 'a' && p.charAt(1) <= 'z') || + (p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')) && + p.charAt(2) == ':') + p = p.substring(1); + } + + int dupIndex = p.indexOf(dupSeparator); + int plen = p.length(); + + // Special case: permit Windows UNC path prefix. + if (dupSeparator.equals("\\\\") && dupIndex == 0) + dupIndex = p.indexOf(dupSeparator, 1); + + if (dupIndex == -1) + { + // Ignore trailing separator (though on Windows "a:\", for + // example, is a valid and minimal path). + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')) + return p.substring (0, plen - 1); + } + else + return p; + } + + StringBuffer newpath = new StringBuffer(plen); + int last = 0; + while (dupIndex != -1) + { + newpath.append(p.substring(last, dupIndex)); + // Ignore the duplicate path characters. + while (p.charAt(dupIndex) == separatorChar) + { + dupIndex++; + if (dupIndex == plen) + return newpath.toString(); + } + newpath.append(separatorChar); + last = dupIndex; + dupIndex = p.indexOf(dupSeparator, last); + } + + // Again, ignore possible trailing separator (except special cases + // like "a:\" on Windows). + int end; + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':') + end = plen; + else + end = plen - 1; + } + else + end = plen; + newpath.append(p.substring(last, end)); + + return newpath.toString(); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file in the specified named directory. The path name to the file + * will be the directory name plus the separator string plus the file + * name. If the directory path name ends in the separator string, another + * separator string will still be appended. + * + * @param dirPath The path to the directory the file resides in + * @param name The name of the file + */ + public File(String dirPath, String name) + { + if (name == null) + throw new NullPointerException(); + if (dirPath != null) + { + if (dirPath.length() > 0) + { + // Try to be smart about the number of separator characters. + if (dirPath.charAt(dirPath.length() - 1) == separatorChar + || name.length() == 0) + path = normalizePath(dirPath + name); + else + path = normalizePath(dirPath + separatorChar + name); + } + else + { + // If dirPath is empty, use a system dependant + // default prefix. + // Note that the leading separators in name have + // to be chopped off, to prevent them forming + // a UNC prefix on Windows. + if (separatorChar == '\\' /* TODO use ON_WINDOWS */) + { + int skip = 0; + while(name.length() > skip + && (name.charAt(skip) == separatorChar + || name.charAt(skip) == '/')) + { + skip++; + } + name = name.substring(skip); + } + path = normalizePath(separatorChar + name); + } + } + else + path = normalizePath(name); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file in the specified directory. If the <code>directory</code> + * argument is <code>null</code>, the file is assumed to be in the + * current directory as specified by the <code>user.dir</code> system + * property + * + * @param directory The directory this file resides in + * @param name The name of the file + */ + public File(File directory, String name) + { + this (directory == null ? null : directory.path, name); + } + + /** + * This method initializes a new <code>File</code> object to represent + * a file corresponding to the specified <code>file:</code> protocol URI. + * + * @param uri The uri. + */ + public File(URI uri) + { + if (uri == null) + throw new NullPointerException("uri is null"); + + if (!uri.getScheme().equals("file")) + throw new IllegalArgumentException("invalid uri protocol"); + + path = normalizePath(uri.getPath()); + } + + /** + * This method returns the path of this file as an absolute path name. + * If the path name is already absolute, then it is returned. Otherwise + * the value returned is the current directory plus the separatory + * string plus the path of the file. The current directory is determined + * from the <code>user.dir</code> system property. + * + * @return The absolute path of this file + */ + public String getAbsolutePath() + { + if (isAbsolute()) + return path; + else if (separatorChar == '\\' + && path.length() > 0 && path.charAt (0) == '\\') + { + // On Windows, even if the path starts with a '\\' it is not + // really absolute until we prefix the drive specifier from + // the current working directory to it. + return System.getProperty ("user.dir").substring (0, 2) + path; + } + else if (separatorChar == '\\' + && path.length() > 1 && path.charAt (1) == ':' + && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))) + { + // On Windows, a process has a current working directory for + // each drive and a path like "G:foo\bar" would mean the + // absolute path "G:\wombat\foo\bar" if "\wombat" is the + // working directory on the G drive. + String drvDir = null; + try + { + drvDir = new File (path.substring (0, 2)).getCanonicalPath(); + } + catch (IOException e) + { + drvDir = path.substring (0, 2) + "\\"; + } + + // Note: this would return "C:\\." for the path "C:.", if "\" + // is the working folder on the C drive, but this is + // consistent with what Sun's JRE 1.4.1.01 actually returns! + if (path.length() > 2) + return drvDir + '\\' + path.substring (2, path.length()); + else + return drvDir; + } + else + return System.getProperty ("user.dir") + separatorChar + path; + } + + /** + * This method returns a <code>File</code> object representing the + * absolute path of this object. + * + * @return A <code>File</code> with the absolute path of the object. + * + * @since 1.2 + */ + public File getAbsoluteFile() + { + return new File(getAbsolutePath()); + } + + /** + * This method returns a canonical representation of the pathname of + * this file. The actual form of the canonical representation is + * different. On the GNU system, the canonical form differs from the + * absolute form in that all relative file references to "." and ".." + * are resolved and removed. + * <p> + * Note that this method, unlike the other methods which return path + * names, can throw an IOException. This is because native method + * might be required in order to resolve the canonical path + * + * @exception IOException If an error occurs + */ + public String getCanonicalPath() throws IOException + { + // On Windows, getAbsolutePath might end up calling us, so we + // have to special case that call to avoid infinite recursion. + if (separatorChar == '\\' && path.length() == 2 && + ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || + (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && + path.charAt(1) == ':') + { + return VMFile.toCanonicalForm(path); + } + // Call getAbsolutePath first to make sure that we do the + // current directory handling, because the native code + // may have a different idea of the current directory. + return VMFile.toCanonicalForm(getAbsolutePath()); + } + + /** + * This method returns a <code>File</code> object representing the + * canonical path of this object. + * + * @return A <code>File</code> instance representing the canonical path of + * this object. + * + * @exception IOException If an error occurs. + * + * @since 1.2 + */ + public File getCanonicalFile() throws IOException + { + return new File(getCanonicalPath()); + } + + /** + * This method returns the name of the file. This is everything in the + * complete path of the file after the last instance of the separator + * string. + * + * @return The file name + */ + public String getName() + { + return VMFile.getName(path); + } + + /** + * This method returns a <code>String</code> the represents this file's + * parent. <code>null</code> is returned if the file has no parent. The + * parent is determined via a simple operation which removes the + * + * @return The parent directory of this file + */ + public String getParent() + { + String prefix = null; + int nameSeqIndex = 0; + + // The "prefix", if present, is the leading "/" on UNIX and + // either the drive specifier (e.g. "C:") or the leading "\\" + // of a UNC network path on Windows. + if (separatorChar == '/' && path.charAt (0) == '/') + { + prefix = "/"; + nameSeqIndex = 1; + } + else if (separatorChar == '\\' && path.length() > 1) + { + if ((path.charAt (0) == '\\' && path.charAt (1) == '\\') + || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z')) + && path.charAt (1) == ':')) + { + prefix = path.substring (0, 2); + nameSeqIndex = 2; + } + } + + // According to the JDK docs, the returned parent path is the + // portion of the name sequence before the last separator + // character, if found, prefixed by the prefix, otherwise null. + if (nameSeqIndex < path.length()) + { + String nameSeq = path.substring (nameSeqIndex, path.length()); + int last = nameSeq.lastIndexOf (separatorChar); + if (last == -1) + return prefix; + else if (last == (nameSeq.length() - 1)) + // Note: The path would not have a trailing separator + // except for cases like "C:\" on Windows (see + // normalizePath( )), where Sun's JRE 1.4 returns null. + return null; + else if (last == 0) + last++; + + if (prefix != null) + return prefix + nameSeq.substring (0, last); + else + return nameSeq.substring (0, last); + } + else + // Sun's JRE 1.4 returns null if the prefix is the only + // component of the path - so "/" gives null on UNIX and + // "C:", "\\", etc. return null on Windows. + return null; + } + + /** + * This method returns a <code>File</code> object representing the parent + * file of this one. + * + * @return a <code>File</code> for the parent of this object. + * <code>null</code> + * will be returned if this object does not have a parent. + * + * @since 1.2 + */ + public File getParentFile() + { + String parent = getParent(); + return parent != null ? new File(parent) : null; + } + + /** + * Returns the path name that represents this file. May be a relative + * or an absolute path name + * + * @return The pathname of this file + */ + public String getPath() + { + return path; + } + + /** + * This method returns a hash code representing this file. It is the + * hash code of the path of this file (as returned by <code>getPath()</code>) + * exclusived or-ed with the value 1234321. + * + * @return The hash code for this object + */ + public int hashCode() + { + if (VMFile.IS_CASE_SENSITIVE) + return path.hashCode() ^ 1234321; + else + return path.toLowerCase().hashCode() ^ 1234321; + } + + /** + * This method returns true if this object represents an absolute file + * path and false if it does not. The definition of an absolute path varies + * by system. As an example, on GNU systems, a path is absolute if it starts + * with a "/". + * + * @return <code>true</code> if this object represents an absolute + * file name, <code>false</code> otherwise. + */ + public boolean isAbsolute() + { + if (separatorChar == '\\') + return path.startsWith(dupSeparator) || + (path.length() > 2 && + ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || + (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && + path.charAt(1) == ':' && + path.charAt(2) == '\\'); + else + return path.startsWith(separator); + } + + /** + * This method tests whether or not the file represented by this object + * is a directory. In order for this method to return <code>true</code>, + * the file represented by this object must exist and be a directory. + * + * @return <code>true</code> if this file is a directory, <code>false</code> + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isDirectory() + { + checkRead(); + return VMFile.isDirectory(path); + } + + /** + * This method tests whether or not the file represented by this object + * is a "plain" file. A file is a plain file if and only if it 1) Exists, + * 2) Is not a directory or other type of special file. + * + * @return <code>true</code> if this is a plain file, <code>false</code> + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isFile() + { + checkRead(); + return VMFile.isFile(path); + } + + /** + * This method tests whether or not this file represents a "hidden" file. + * On GNU systems, a file is hidden if its name begins with a "." + * character. Files with these names are traditionally not shown with + * directory listing tools. + * + * @return <code>true</code> if the file is hidden, <code>false</code> + * otherwise. + * + * @since 1.2 + */ + public boolean isHidden() + { + return VMFile.isHidden(path); + } + + /** + * This method returns the last modification time of this file. The + * time value returned is an abstract value that should not be interpreted + * as a specified time value. It is only useful for comparing to other + * such time values returned on the same system. In that case, the larger + * value indicates a more recent modification time. + * <p> + * If the file does not exist, then a value of 0 is returned. + * + * @return The last modification time of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long lastModified() + { + checkRead(); + return VMFile.lastModified(path); + } + + /** + * This method returns the length of the file represented by this object, + * or 0 if the specified file does not exist. + * + * @return The length of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long length() + { + checkRead(); + return VMFile.length(path); + } + + /** + * This method returns a array of <code>String</code>'s representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * <code>null</code> is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + * <p> + * In this form of the <code>list()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FilenameFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @param filter An object which will identify files to exclude from + * the directory listing. + * + * @return An array of files in the directory, or <code>null</code> + * if this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the <code>SecurityManager</code> + */ + public String[] list(FilenameFilter filter) + { + checkRead(); + + if (!exists() || !isDirectory()) + return null; + + // Get the list of files + String files[] = VMFile.list(path); + + // Check if an error occured in listInternal(). + if (files == null) + return null; + + if (filter == null) + return files; + + // Apply the filter + int count = 0; + for (int i = 0; i < files.length; i++) + { + if (filter.accept(this, files[i])) + ++count; + else + files[i] = null; + } + + String[] retfiles = new String[count]; + count = 0; + for (int i = 0; i < files.length; i++) + if (files[i] != null) + retfiles[count++] = files[i]; + + return retfiles; + } + + /** + * This method returns a array of <code>String</code>'s representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * <code>null</code> is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of files in the directory, or <code>null</code> if + * this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the <code>SecurityManager</code> + */ + public String[] list() + { + return list(null); + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles() + { + return listFiles((FilenameFilter) null); + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * In this form of the <code>listFiles()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FilenameFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FilenameFilter filter) + { + String[] filelist = list(filter); + + if (filelist == null) + return null; + + File[] fobjlist = new File [filelist.length]; + + for (int i = 0; i < filelist.length; i++) + fobjlist [i] = new File(this, filelist [i]); + + return fobjlist; + } + + /** + * This method returns an array of <code>File</code> objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, <code>null</code> is returned. + * Each of the returned <code>File</code> object is constructed with this + * object as its parent. + * <p> + * In this form of the <code>listFiles()</code> method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The <code>FileFilter</code> specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + * <p> + * A <code>SecurityManager</code> check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of <code>File</code> objects for this directory. + * + * @exception SecurityException If the <code>SecurityManager</code> denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FileFilter filter) + { + File[] fobjlist = listFiles((FilenameFilter) null); + + if (fobjlist == null) + return null; + + if (filter == null) + return fobjlist; + + int count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + ++count; + + File[] final_list = new File[count]; + count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + { + final_list[count] = fobjlist[i]; + ++count; + } + + return final_list; + } + + /** + * This method returns a <code>String</code> that is the path name of the + * file as returned by <code>getPath</code>. + * + * @return A <code>String</code> representation of this file + */ + public String toString() + { + return path; + } + + /** + * @return A <code>URI</code> for this object. + */ + public URI toURI() + { + String abspath = getAbsolutePath(); + + if (isDirectory()) + abspath = abspath + separatorChar; + + if (separatorChar == '\\') + abspath = separatorChar + abspath; + + try + { + return new URI("file", null, null, -1, + abspath.replace(separatorChar, '/'), + null, null); + } + catch (URISyntaxException use) + { + // Can't happen. + throw (InternalError) new InternalError("Unconvertible file: " + + this).initCause(use); + } + } + + /** + * This method returns a <code>URL</code> with the <code>file:</code> + * protocol that represents this file. The exact form of this URL is + * system dependent. + * + * @return A <code>URL</code> for this object. + * + * @exception MalformedURLException If the URL cannot be created + * successfully. + */ + public URL toURL() throws MalformedURLException + { + // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt", + // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". + if (separatorChar == '\\') + return new URL ("file:/" + getAbsolutePath().replace ('\\', '/') + + (isDirectory() ? "/" : "")); + else + return new URL ("file:" + getAbsolutePath() + + (isDirectory() ? "/" : "")); + } + + + /** + * This method creates a directory for the path represented by this object. + * + * @return <code>true</code> if the directory was created, + * <code>false</code> otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdir() + { + checkWrite(); + return VMFile.mkdir(path); + } + + /** + * This method creates a directory for the path represented by this file. + * It will also create any intervening parent directories if necessary. + * + * @return <code>true</code> if the directory was created, + * <code>false</code> otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdirs() + { + String parent = getParent(); + if (parent == null) + { + return mkdir(); + } + + File f = new File(parent); + if (!f.exists()) + { + boolean rc = f.mkdirs(); + if (rc == false) + return false; + } + + return mkdir(); + } + + /** + * This method creates a temporary file in the specified directory. If + * the directory name is null, then this method uses the system temporary + * directory. The files created are guaranteed not to currently exist and + * the same file name will never be used twice in the same virtual + * machine instance. + * The system temporary directory is determined by examinging the + * <code>java.io.tmpdir</code> system property. + * <p> + * The <code>prefix</code> parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * <code>suffix</code> parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be <code>null</code> + * and if it is, the suffix defaults to ".tmp". + * <p> + * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code> + * method is used to verify that this operation is permitted. + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * @param directory The directory to create the file in, or + * <code>null</code> for the default temporary directory + * + * @exception IllegalArgumentException If the patterns is not valid + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public static File createTempFile(String prefix, String suffix, + File directory) + throws IOException + { + // Grab the system temp directory if necessary + if (directory == null) + { + String dirname = System.getProperty("java.io.tmpdir"); + if (dirname == null) + throw new IOException("Cannot determine system temporary directory"); + + directory = new File(dirname); + if (! VMFile.exists(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + " does not exist."); + if (! VMFile.isDirectory(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + + " is not really a directory."); + } + + // Check if prefix is at least 3 characters long + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix too short: " + prefix); + + // Set default value of suffix + if (suffix == null) + suffix = ".tmp"; + + // Now identify a file name and make sure it doesn't exist. + File file; + if (!VMFile.IS_DOS_8_3) + { + do + { + String filename = prefix + System.currentTimeMillis() + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + else + { + // make sure prefix is not longer than 7 characters + if (prefix.length() >= 8) + throw new IllegalArgumentException("Prefix too long: " + prefix + "(valid length 3..7)"); + + long mask = 0x000000ffffFFFFL >> (prefix.length() * 4); + do + { + int n = (int) (System.currentTimeMillis() & mask); + String filename = prefix + java.lang.Integer.toHexString(n) + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + + // Verify that we are allowed to create this file + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkWrite(file.getAbsolutePath()); + + // Now create the file and return our file object + // XXX - FIXME race condition. + VMFile.create(file.getAbsolutePath()); + return file; + } + + /** + * This method sets the file represented by this object to be read only. + * A read only file or directory cannot be modified. Please note that + * GNU systems allow read only files to be deleted if the directory it + * is contained in is writable. + * + * @return <code>true</code> if the operation succeeded, <code>false</code> + * otherwise. + * + * @exception SecurityException If the <code>SecurityManager</code> does + * not allow this operation. + * + * @since 1.2 + */ + public boolean setReadOnly() + { + // Do a security check before trying to do anything else. + checkWrite(); + + // Test for existence. + if (! VMFile.exists(path)) + return false; + + return VMFile.setReadOnly(path); + } + + /** + * This method returns an array of filesystem roots. Some operating systems + * have volume oriented filesystem. This method provides a mechanism for + * determining which volumes exist. GNU systems use a single hierarchical + * filesystem, so will have only one "/" filesystem root. + * + * @return An array of <code>File</code> objects for each filesystem root + * available. + * + * @since 1.2 + */ + public static File[] listRoots() + { + return VMFile.listRoots(); + } + + /** + * This method creates a temporary file in the system temporary directory. + * The files created are guaranteed not to currently exist and the same file + * name will never be used twice in the same virtual machine instance. The + * system temporary directory is determined by examinging the + * <code>java.io.tmpdir</code> system property. + * <p> + * The <code>prefix</code> parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * <code>suffix</code> parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be <code>null</code> + * and if it is, the suffix defaults to ".tmp". + * <p> + * If a <code>SecurityManager</code> exists, then its <code>checkWrite</code> + * method is used to verify that this operation is permitted. + * <p> + * This method is identical to calling + * <code>createTempFile(prefix, suffix, null)</code>. + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * + * @exception IllegalArgumentException If the prefix or suffix are not valid. + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + return createTempFile(prefix, suffix, null); + } + + /** + * This method compares the specified <code>File</code> to this one + * to test for equality. It does this by comparing the canonical path names + * of the files. + * <p> + * The canonical paths of the files are determined by calling the + * <code>getCanonicalPath</code> method on each object. + * <p> + * This method returns a 0 if the specified <code>Object</code> is equal + * to this one, a negative value if it is less than this one + * a positive value if it is greater than this one. + * + * @return An integer as described above + * + * @since 1.2 + */ + public int compareTo(File other) + { + if (VMFile.IS_CASE_SENSITIVE) + return path.compareTo (other.path); + else + return path.compareToIgnoreCase (other.path); + } + + /** + * This method compares the specified <code>Object</code> to this one + * to test for equality. It does this by comparing the canonical path names + * of the files. This method is identical to <code>compareTo(File)</code> + * except that if the <code>Object</code> passed to it is not a + * <code>File</code>, it throws a <code>ClassCastException</code> + * <p> + * The canonical paths of the files are determined by calling the + * <code>getCanonicalPath</code> method on each object. + * <p> + * This method returns a 0 if the specified <code>Object</code> is equal + * to this one, a negative value if it is less than this one + * a positive value if it is greater than this one. + * + * @return An integer as described above + * + * @exception ClassCastException If the passed <code>Object</code> is + * not a <code>File</code> + * + * @since 1.2 + */ + public int compareTo(Object obj) + { + return compareTo((File) obj); + } + + /** + * This method renames the file represented by this object to the path + * of the file represented by the argument <code>File</code>. + * + * @param dest The <code>File</code> object representing the target name + * + * @return <code>true</code> if the rename succeeds, <code>false</code> + * otherwise. + * + * @exception SecurityException If write access is not allowed to the + * file by the <code>SecurityMananger</code>. + */ + public synchronized boolean renameTo(File dest) + { + checkWrite(); + dest.checkWrite(); + // Call our native rename method + return VMFile.renameTo(path, dest.path); + } + + /** + * This method sets the modification time on the file to the specified + * value. This is specified as the number of seconds since midnight + * on January 1, 1970 GMT. + * + * @param time The desired modification time. + * + * @return <code>true</code> if the operation succeeded, <code>false</code> + * otherwise. + * + * @exception IllegalArgumentException If the specified time is negative. + * @exception SecurityException If the <code>SecurityManager</code> will + * not allow this operation. + * + * @since 1.2 + */ + public boolean setLastModified(long time) + { + if (time < 0) + throw new IllegalArgumentException("Negative modification time: " + time); + + checkWrite(); + return VMFile.setLastModified(path, time); + } + + private void checkWrite() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkWrite(path); + } + + private void checkRead() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkRead(path); + } + + /** + * Calling this method requests that the file represented by this object + * be deleted when the virtual machine exits. Note that this request cannot + * be cancelled. Also, it will only be carried out if the virtual machine + * exits normally. + * + * @exception SecurityException If deleting of the file is not allowed + * + * @since 1.2 + */ + public void deleteOnExit() + { + // Check the SecurityManager + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkDelete(path); + + DeleteFileHelper.add(this); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + oos.writeChar(separatorChar); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + ois.defaultReadObject(); + + // If the file was from an OS with a different dir separator, + // fixup the path to use the separator on this OS. + char oldSeparatorChar = ois.readChar(); + + if (oldSeparatorChar != separatorChar) + path = path.replace(oldSeparatorChar, separatorChar); + } + +} // class File + diff --git a/libjava/classpath/java/io/FileDescriptor.java b/libjava/classpath/java/io/FileDescriptor.java new file mode 100644 index 00000000000..d300c9cb617 --- /dev/null +++ b/libjava/classpath/java/io/FileDescriptor.java @@ -0,0 +1,139 @@ +/* FileDescriptor.java -- Opaque file handle class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.ByteChannel; +import java.nio.channels.FileChannel; + +/** + * This class represents an opaque file handle as a Java class. It should + * be used only to pass to other methods that expect an object of this + * type. No system specific information can be obtained from this object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public final class FileDescriptor +{ + /** + * A <code>FileDescriptor</code> representing the system standard input + * stream. This will usually be accessed through the + * <code>System.in</code>variable. + */ + public static final FileDescriptor in + = new FileDescriptor (FileChannelImpl.in); + + /** + * A <code>FileDescriptor</code> representing the system standard output + * stream. This will usually be accessed through the + * <code>System.out</code>variable. + */ + public static final FileDescriptor out + = new FileDescriptor (FileChannelImpl.out); + + /** + * A <code>FileDescriptor</code> representing the system standard error + * stream. This will usually be accessed through the + * <code>System.err</code>variable. + */ + public static final FileDescriptor err + = new FileDescriptor (FileChannelImpl.err); + + final ByteChannel channel; + + /** + * This method is used to initialize an invalid FileDescriptor object. + */ + public FileDescriptor() + { + channel = null; + } + + /** + * This method is used to initialize a FileDescriptor object. + */ + FileDescriptor(ByteChannel channel) + { + this.channel = channel; + } + + + /** + * This method forces all data that has not yet been physically written to + * the underlying storage medium associated with this + * <code>FileDescriptor</code> + * to be written out. This method will not return until all data has + * been fully written to the underlying device. If the device does not + * support this functionality or if an error occurs, then an exception + * will be thrown. + */ + public void sync () throws SyncFailedException + { + if (channel instanceof FileChannel) + { + try + { + ((FileChannel) channel).force(true); + } + catch (IOException ex) + { + if (ex instanceof SyncFailedException) + throw (SyncFailedException) ex; + else + throw new SyncFailedException(ex.toString()); + } + } + } + + /** + * This methods tests whether or not this object represents a valid open + * native file handle. + * + * @return <code>true</code> if this object represents a valid + * native file handle, <code>false</code> otherwise + */ + public boolean valid () + { + return channel != null && channel.isOpen(); + } +} diff --git a/libjava/classpath/java/io/FileFilter.java b/libjava/classpath/java/io/FileFilter.java new file mode 100644 index 00000000000..e57ac9fd060 --- /dev/null +++ b/libjava/classpath/java/io/FileFilter.java @@ -0,0 +1,65 @@ +/* FileFilter.java -- Filter a list of pathnames + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface has one method which is used for filtering pathnames + * returned in a pathname listing. It is currently used by the + * <code>File.listFiles(FileFilter)</code> method. + * <p> + * The method in this interface determines if a particular pathname should + * or should not be included in the pathname listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see File#listFiles(java.io.FileFilter) + */ +public interface FileFilter +{ + /** + * This method determines whether or not a given pathname should be included + * in a pathname listing. + * + * @param pathname The pathname to test + * + * @return <code>true</code> if the path should be included in the list, + * <code>false</code> otherwise. + */ + boolean accept(File pathname); +} diff --git a/libjava/classpath/java/io/FileInputStream.java b/libjava/classpath/java/io/FileInputStream.java new file mode 100644 index 00000000000..8ca38b02fc4 --- /dev/null +++ b/libjava/classpath/java/io/FileInputStream.java @@ -0,0 +1,309 @@ +/* FileInputStream.java -- An input stream that reads from disk files. + Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class is a stream that reads its bytes from a file. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FileInputStream extends InputStream +{ + /** + * This is the native file handle for the file this stream is reading from + */ + private FileDescriptor fd; + + private FileChannelImpl ch; + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified named file. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param name The name of the file this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory + */ + public FileInputStream(String name) throws FileNotFoundException + { + this(new File(name)); + } + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>File</code> object. A security check is first + * made to determine + * whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param file The <code>File</code> object this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory. + */ + public FileInputStream(File file) throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(file.getPath()); + + ch = FileChannelImpl.create(file, FileChannelImpl.READ); + } + + /** + * This method initializes a <code>FileInputStream</code> to read from the + * specified <code>FileDescriptor</code> object. A security + * check is first made to + * determine whether or not access to this file is allowed. This is done by + * calling the <code>checkRead()</code> method of the + * <code>SecurityManager</code> + * (if one exists) with the specified <code>FileDescriptor</code> + * An exception is + * thrown if reading is not allowed. + * + * @param fdObj The <code>FileDescriptor</code> object this stream + * should read from + * + * @exception SecurityException If read access to the file is not allowed + */ + public FileInputStream(FileDescriptor fdObj) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileInputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method returns the number of unread bytes remaining in the file if + * the descriptor being read from is an actual file. If this method is + * reading from a ''special'' file such a the standard input, this method + * will return the appropriate value for the stream being read. + * <p> + * Be aware that reads on plain files that do not reside locally might + * possibly block even if this method says they should not. For example, + * a remote server might crash, preventing an NFS mounted file from being + * read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return ch.available(); + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream will likely generate an IOException since the underlying file + * will be closed. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + ch.close(); + } + + protected void finalize() throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a <code>FileDescriptor</code> object representing the + * underlying native file handle of the file this stream is reading + * from + * + * @return A <code>FileDescriptor</code> for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD() throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return ch.read(); + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(buf, 0, buf.length)</code> + * + * @param buf The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * This method read bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * <code>offset</code> into + * the buffer and attempts to read <code>len</code> bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * + * @param buf The array into which the bytes read should be stored + * @param offset The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + return ch.read(buf, offset, len); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * @param numBytes The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip (long numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position (); + ch.position(oldPos + numBytes); + return ch.position() - oldPos; + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel () + { + return ch; + } + +} // class FileInputStream + diff --git a/libjava/classpath/java/io/FileNotFoundException.java b/libjava/classpath/java/io/FileNotFoundException.java new file mode 100644 index 00000000000..3c11e296072 --- /dev/null +++ b/libjava/classpath/java/io/FileNotFoundException.java @@ -0,0 +1,73 @@ +/* FileNotFoundException.java -- the requested file could not be found + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an attempt is made to access a file that + * does not exist, or is inaccessible for some other reason (such as writing + * a read-only file). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class FileNotFoundException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -897856973823710492L; + + /** + * Create an exception without a descriptive error message. + */ + public FileNotFoundException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public FileNotFoundException(String message) + { + super(message); + } +} // class FileNotFoundException diff --git a/libjava/classpath/java/io/FileOutputStream.java b/libjava/classpath/java/io/FileOutputStream.java new file mode 100644 index 00000000000..10ea6b536cb --- /dev/null +++ b/libjava/classpath/java/io/FileOutputStream.java @@ -0,0 +1,296 @@ +/* FileOutputStream.java -- Writes to a file on disk. + Copyright (C) 1998, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This classes allows a stream of data to be written to a disk file or + * any open <code>FileDescriptor</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileOutputStream extends OutputStream +{ + private FileDescriptor fd; + + private FileChannelImpl ch; + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file if + * the <code>append</code> argument is <code>false</code> or at the end + * of the file if the <code>append</code> argument is true. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * @param append <code>true</code> to append bytes to the end of the file, + * or <code>false</code> to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path, boolean append) + throws SecurityException, FileNotFoundException + { + this (new File(path), append); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path) + throws SecurityException, FileNotFoundException + { + this (path, false); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the specified <code>File</code> object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The <code>File</code> object this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file) + throws SecurityException, FileNotFoundException + { + this (file, false); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the specified <code>File</code> object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file if the <code>append</code> parameter is + * <code>false</code>. Otherwise bytes are written at the end of the + * file. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The <code>File</code> object this stream should write to + * @param append <code>true</code> to append bytes to the end of the file, + * or <code>false</code> to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file, boolean append) + throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(file.getPath()); + + ch = FileChannelImpl.create(file, (append + ? FileChannelImpl.WRITE + | FileChannelImpl.APPEND + : FileChannelImpl.WRITE)); + } + + /** + * This method initializes a <code>FileOutputStream</code> object to write + * to the file represented by the specified <code>FileDescriptor</code> + * object. This method does not create any underlying disk file or + * reposition the file pointer of the given descriptor. It assumes that + * this descriptor is ready for writing as is. + * <p> + * Before opening a file, a security check is performed by calling the + * <code>checkWrite</code> method of the <code>SecurityManager</code> (if + * one exists) with the specified <code>FileDescriptor</code> as an argument. + * An exception is thrown if writing is not allowed. + * + * @param fdObj The <code>FileDescriptor</code> this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + */ + public FileOutputStream (FileDescriptor fdObj) + throws SecurityException + { + // Hmm, no other exception but this one to throw, but if the descriptor + // isn't valid, we surely don't have "permission" to write to it. + if (!fdObj.valid()) + throw new SecurityException("Invalid FileDescriptor"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileOutputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + protected void finalize () throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a <code>FileDescriptor</code> object representing + * the file that is currently being written to + * + * @return A <code>FileDescriptor</code> object for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method writes a single byte of data to the file. + * + * @param b The byte of data to write, passed as an <code>int</code> + * + * @exception IOException If an error occurs + */ + public void write (int b) throws IOException + { + ch.write (b); + } + + /** + * This method writes all the bytes in the specified array to the + * file. + * + * @param buf The array of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf) + throws IOException + { + write (buf, 0, buf.length); + } + + /** + * This method writes <code>len</code> bytes from the byte array + * <code>buf</code> to the file starting at index <code>offset</code>. + * + * @param buf The array of bytes to write to the file + * @param offset The offset into the array to start writing bytes from + * @param len The number of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf, int offset, int len) + throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException (); + + ch.write (buf, offset, len); + } + + /** + * This method closes the underlying file. Any further attempts to + * write to this stream will likely generate an exception since the + * file is closed. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel() + { + return ch; + } + +} // class FileOutputStream + diff --git a/libjava/classpath/java/io/FilePermission.java b/libjava/classpath/java/io/FilePermission.java new file mode 100644 index 00000000000..356787bfa72 --- /dev/null +++ b/libjava/classpath/java/io/FilePermission.java @@ -0,0 +1,292 @@ +/* FilePermission.java -- + Copyright (C) 1998, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.Permission; + +public final class FilePermission extends Permission implements Serializable +{ + private static final long serialVersionUID = 7930732926638008763L; + + private static final String CURRENT_DIRECTORY = + System.getProperty("user.dir"); + + private static final String ALL_FILES = "<<ALL FILES>>"; + + private boolean readPerm = false; + private boolean writePerm = false; + private boolean executePerm = false; + private boolean deletePerm = false; + private final String actionsString; + + // Checks and caches the actions + private void checkPerms() throws IllegalArgumentException + { + String action; + int i = actionsString.indexOf(','); + int startI = 0; + while (i != -1) + { + action = actionsString.substring(startI, i).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + + startI = i + 1; + i = actionsString.indexOf(',', startI); + } + + action = actionsString.substring(startI).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + } + + /** + * Create a new FilePermission. + * + * @param pathExpression an expression specifying the paths this + * permission represents. + * @param actionsString a comma-separated list of the actions this + * permission represents. The actions must be "read", "write", + * "execute" and/or "delete". + */ + public FilePermission(String pathExpression, String actionsString) + { + // FIXME: what to do when the file string is malformed? + super(pathExpression); + if (pathExpression == null) + throw new NullPointerException("pathExpression"); + if (actionsString == null) + throw new IllegalArgumentException("actionsString"); + this.actionsString = actionsString; + checkPerms(); + } + + /** + * Get the actions this FilePermission supports. + * @return the String representing the actions this FilePermission supports. + */ + public String getActions() + { + return actionsString; + } + + /** + * Get the hash code for this Object.<P> + * FilePermission's hash code is calculated as the exclusive or of the + * target + * String's hash code and the action String's hash code. + * @specnote Sun did not specify how to calculate the hash code; + * I made this up. + * @return the hash code for this Object. + */ + public int hashCode() + { + return getName().hashCode() ^ actionsString.hashCode(); + } + + /** + * Check two FilePermissions for semantic equality. + * Two FilePermissions are exactly equivalent if they have identical path + * expressions and have exactly the same access permissions. + * @param o the Object to compare to. + * @return whether the Objects are semantically equivalent. + */ + public boolean equals(Object o) + { + if (! (o instanceof FilePermission)) + return false; + FilePermission p = (FilePermission) o; + + String f1 = getName(); + String f2 = p.getName(); + + // Compare names, taking into account if they refer to a directory + // and one has a separator and the other does not. + if (f1.length() > 0 && f1.charAt(f1.length() - 1) == File.separatorChar) + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f2.equals(f1)) + return false; + } + else + { + if (! f2.equals(f1.substring(0, f1.length() - 1))) + return false; + } + } + else + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f1.equals(f2.substring(0, f2.length() - 1))) + return false; + } + else + { + if (! f1.equals(f2)) + return false; + } + } + return (readPerm == p.readPerm + && writePerm == p.writePerm + && executePerm == p.executePerm + && deletePerm == p.deletePerm); + } + + /** + * Check to see if this permission implies another. + * Permission A implies permission B if these things are all true: + * <OL> + * <LI>A and B are both FilePermissions.</LI> + * <LI>All possible files in B are included in A + * (possibly more are in A).</LI> + * <LI>All actions B supports, A also supports.</LI> + * </OL> + * @param p the Permission to compare against. + * @return whether this Permission implies p + */ + public boolean implies(Permission p) + { + if (! (p instanceof FilePermission)) + return false; + + String f1 = getName(); + + if (f1.equals(ALL_FILES)) + return true; + + FilePermission fp = (FilePermission) p; + String f2 = fp.getName(); + + if (f1.charAt(0) != File.separatorChar) + f1 = CURRENT_DIRECTORY + f1; + if (f2.charAt(0) != File.separatorChar) + f2 = CURRENT_DIRECTORY + f2; + + String sub1; + + switch (f1.charAt(f1.length() - 1)) + { + case '*': + sub1 = f1.substring(0, f1.length() - 1); // chop off "*" + if (f2.length() <= sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. If it's the same (or length - 1), it + // could be the same directory but specifies access to + // the directory rather than the files in it. + return false; + } + else if (f2.charAt(sub1.length() - 1) == File.separatorChar) + { + // Make sure the part before the "/" is the same. + if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + // Make sure there are no subdirectories specified + // underneath this one. + if (f2.substring(sub1.length() + 1).indexOf(File.separatorChar) + != -1) + return false; + } + else + { + // Obviously not equal: f2 is either not a directory or + // is not the same directory (its name continues further + // than we want). + return false; + } + break; + case '-': + // Chop off "/-". + sub1 = f1.substring(0, f1.length() - 2); + if (f2.length() < sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. + return false; + } + else if (f2.length() > sub1.length() + && f2.charAt(sub1.length()) != File.separatorChar) + return false; + else if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + break; + + default: + if (f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f1.equals(f2.substring(0, f2.length() - 1))) + return false; + } + else if (!f1.equals(f2)) + return false; + break; + } + + if (readPerm && ! fp.readPerm) + return false; + if (writePerm && ! fp.writePerm) + return false; + if (executePerm && ! fp.executePerm) + return false; + if (deletePerm && ! fp.deletePerm) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/io/FileReader.java b/libjava/classpath/java/io/FileReader.java new file mode 100644 index 00000000000..4a1dd5ff4ce --- /dev/null +++ b/libjava/classpath/java/io/FileReader.java @@ -0,0 +1,92 @@ +/* FileReader.java -- Convenience class for reading characters from a file + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class provides a convenient way to set up a <code>Reader</code> + * to read from a file. It opens the specified file for reading and creates + * the <code>InputStreamReader</code> to read from the + * resulting <code>FileInputStream</code>. This class can only be used + * to read from files using the default character encoding. Use + * <code>InputStreamReader</code> directly to use a non-default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FileReader extends InputStreamReader +{ + /** + * This method initializes a <code>FileReader</code> instance to read from + * the specified <code>File</code> object. + * + * @param file The <code>File</code> object representing the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(File file) throws FileNotFoundException + { + super(new FileInputStream(file)); + } + + /** + * This method initializes a <code>FileReader</code> instance to read from + * this specified <code>FileDescriptor</code> object. + * + * @param fd The <code>FileDescriptor</code> to read from. + */ + public FileReader(FileDescriptor fd) + { + super(new FileInputStream(fd)); + } + + /** + * This method initializes a <code>FileReader</code> instance to read from + * the specified named file. + * + * @param name The name of the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(String name) throws FileNotFoundException + { + super(new FileInputStream(name)); + } +} // class FileReader + diff --git a/libjava/classpath/java/io/FileWriter.java b/libjava/classpath/java/io/FileWriter.java new file mode 100644 index 00000000000..b34db83231e --- /dev/null +++ b/libjava/classpath/java/io/FileWriter.java @@ -0,0 +1,137 @@ +/* FileWriter.java -- Convenience class for writing to files. + Copyright (C) 1998, 1999, 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This is a convenience class for writing to files. It creates an + * <code>FileOutputStream</code> and initializes an + * <code>OutputStreamWriter</code> to write to it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileWriter extends OutputStreamWriter +{ + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>File</code> object. + * + * @param file The <code>File</code> object to write to. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(File file) throws SecurityException, IOException + { + super(new FileOutputStream(file)); + } + + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>File</code> object. + * + * @param file The <code>File</code> object to write to. + * @param append <code>true</code> to start adding data at the end of the + * file, <code>false</code> otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(File file, boolean append) throws IOException + { + super(new FileOutputStream(file, append)); + } + + /** + * This method initializes a new <code>FileWriter</code> object to write + * to the specified <code>FileDescriptor</code> object. + * + * @param fd The <code>FileDescriptor</code> object to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + */ + public FileWriter(FileDescriptor fd) throws SecurityException + { + super(new FileOutputStream(fd)); + } + + /** + * This method intializes a new <code>FileWriter</code> object to + * write to the + * specified named file. + * + * @param name The name of the file to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(String name) throws IOException + { + super(new FileOutputStream(name)); + } + + /** + * This method intializes a new <code>FileWriter</code> object to + * write to the + * specified named file. This form of the constructor allows the caller + * to determin whether data should be written starting at the beginning or + * the end of the file. + * + * @param name The name of the file to write to + * @param append <code>true</code> to start adding data at the end of the + * file, <code>false</code> otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * <code>SecurityManager</code>. + * @throws IOException If any other error occurs + */ + public FileWriter(String name, boolean append) throws IOException + { + super(new FileOutputStream(name, append)); + } +} diff --git a/libjava/classpath/java/io/FilenameFilter.java b/libjava/classpath/java/io/FilenameFilter.java new file mode 100644 index 00000000000..57b4d3b182c --- /dev/null +++ b/libjava/classpath/java/io/FilenameFilter.java @@ -0,0 +1,76 @@ +/* FilenameFilter.java -- Filter a list of filenames + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to 1.1. + */ + +/** + * This interface has one method which is used for filtering filenames + * returned in a directory listing. It is currently used by the + * <code>File.list(FilenameFilter)</code> method and by the filename + * dialog in AWT. + * <p> + * The method in this interface determines if a particular file should + * or should not be included in the file listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see File#listFiles(java.io.FilenameFilter) + * @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter) + */ +public interface FilenameFilter +{ + /** + * This method determines whether or not a given file should be included + * in a directory listing. + * + * @param dir The <code>File</code> instance for the directory being read + * @param name The name of the file to test + * + * @return <code>true</code> if the file should be included in the list, + * <code>false</code> otherwise. + */ + boolean accept(File dir, String name); + +} // interface FilenameFilter + diff --git a/libjava/classpath/java/io/FilterInputStream.java b/libjava/classpath/java/io/FilterInputStream.java new file mode 100644 index 00000000000..d3cb9e4f71f --- /dev/null +++ b/libjava/classpath/java/io/FilterInputStream.java @@ -0,0 +1,203 @@ +/* FilterInputStream.java -- Base class for classes that filter input + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying <code>InputStream</code> + * and simply redirects calls made to it to the subordinate InputStream + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + * <p> + * This class is not abstract. However, since it only redirects calls + * to a subordinate <code>InputStream</code> without adding any functionality + * on top of it, this class should not be used directly. Instead, various + * subclasses of this class should be used. This is enforced with a + * protected constructor. Do not try to hack around it. + * <p> + * When creating a subclass of <code>FilterInputStream</code>, override the + * appropriate methods to implement the desired filtering. However, note + * that the <code>read(byte[])</code> method does not need to be overridden + * as this class redirects calls to that method to + * <code>read(byte[], int, int)</code> instead of to the subordinate + * <code>InputStream read(byte[])</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FilterInputStream extends InputStream +{ + /** + * This is the subordinate <code>InputStream</code> to which method calls + * are redirected + */ + protected InputStream in; + + /** + * Create a <code>FilterInputStream</code> with the specified subordinate + * <code>InputStream</code>. + * + * @param in The subordinate <code>InputStream</code> + */ + protected FilterInputStream(InputStream in) + { + this.in = in; + } + + /** + * Calls the <code>in.mark(int)</code> method. + * + * @param readlimit The parameter passed to <code>in.mark(int)</code> + */ + public void mark(int readlimit) + { + in.mark(readlimit); + } + + /** + * Calls the <code>in.markSupported()</code> method. + * + * @return <code>true</code> if mark/reset is supported, <code>false</code> + * otherwise + */ + public boolean markSupported() + { + return in.markSupported(); + } + + /** + * Calls the <code>in.reset()</code> method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the <code>in.available()</code> method. + * + * @return The value returned from <code>in.available()</code> + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return in.available(); + } + + /** + * Calls the <code>in.skip(long)</code> method + * + * @param numBytes The requested number of bytes to skip. + * + * @return The value returned from <code>in.skip(long)</code> + * + * @exception IOException If an error occurs + */ + public long skip(long numBytes) throws IOException + { + return in.skip(numBytes); + } + + /** + * Calls the <code>in.read()</code> method + * + * @return The value returned from <code>in.read()</code> + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return in.read(); + } + + /** + * Calls the <code>read(byte[], int, int)</code> overloaded method. + * Note that + * this method does not redirect its call directly to a corresponding + * method in <code>in</code>. This allows subclasses to override only the + * three argument version of <code>read</code>. + * + * @param buf The buffer to read bytes into + * + * @return The value retured from <code>in.read(byte[], int, int)</code> + * + * @exception IOException If an error occurs + */ + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * Calls the <code>in.read(byte[], int, int)</code> method. + * + * @param buf The buffer to read bytes into + * @param offset The index into the buffer to start storing bytes + * @param len The maximum number of bytes to read. + * + * @return The value retured from <code>in.read(byte[], int, int)</code> + * + * @exception IOException If an error occurs + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + return in.read(buf, offset, len); + } + + /** + * This method closes the input stream by closing the input stream that + * this object is filtering. Future attempts to access this stream may + * throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } +} diff --git a/libjava/classpath/java/io/FilterOutputStream.java b/libjava/classpath/java/io/FilterOutputStream.java new file mode 100644 index 00000000000..4c2dfc04a65 --- /dev/null +++ b/libjava/classpath/java/io/FilterOutputStream.java @@ -0,0 +1,150 @@ +/* FilterOutputStream.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output stream classes that + * filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * <code>OutputStream</code>. This class simply overrides all the + * methods in <code>OutputStream</code> to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FilterOutputStream extends OutputStream +{ + /** + * This is the subordinate <code>OutputStream</code> that this class + * redirects its method calls to. + */ + protected OutputStream out; + + /** + * This method initializes an instance of <code>FilterOutputStream</code> + * to write to the specified subordinate <code>OutputStream</code>. + * + * @param out The <code>OutputStream</code> to write to + */ + public FilterOutputStream(OutputStream out) + { + this.out = out; + } + + /** + * This method closes the underlying <code>OutputStream</code>. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + flush(); + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single byte of output to the underlying + * <code>OutputStream</code>. + * + * @param b The byte to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes all the bytes in the specified array to the underlying + * <code>OutputStream</code>. It does this by calling the three parameter + * version of this method - <code>write(byte[], int, int)</code> in this + * class instead of writing to the underlying <code>OutputStream</code> + * directly. This allows most subclasses to avoid overriding this method. + * + * @param buf The byte array to write bytes from + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf) throws IOException + { + // Don't do checking here, per Java Lang Spec. + write(buf, 0, buf.length); + } + + /** + * This method calls the <code>write(int)</code> method <code>len</code> + * times for all bytes from the array <code>buf</code> starting at index + * <code>offset</code>. Subclasses should overwrite this method to get a + * more efficient implementation. + * + * @param buf The byte array to write bytes from + * @param offset The index into the array to start writing bytes from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf, int offset, int len) throws IOException + { + // Don't do checking here, per Java Lang Spec. + for (int i=0; i < len; i++) + write(buf[offset + i]); + + } + +} // class FilterOutputStream + diff --git a/libjava/classpath/java/io/FilterReader.java b/libjava/classpath/java/io/FilterReader.java new file mode 100644 index 00000000000..2bd040a7f72 --- /dev/null +++ b/libjava/classpath/java/io/FilterReader.java @@ -0,0 +1,185 @@ +/* FilterReader.java -- Base class for char stream classes that filter input + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying <code>Reader</code> + * and simply redirects calls made to it to the subordinate Reader + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + * <p> + * When creating a subclass of <code>FilterReader</code>, override the + * appropriate methods to implement the desired filtering. However, note + * that the <code>read(char[])</code> method does not need to be overridden + * as this class redirects calls to that method to + * <code>read(yte[], int, int)</code> instead of to the subordinate + * <code>Reader} read(yte[])</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class FilterReader extends Reader +{ + /** + * This is the subordinate <code>Reader</code> to which method calls + * are redirected + */ + protected Reader in; + + /** + * Create a <code>FilterReader</code> with the specified subordinate + * <code>Reader</code>. + * The <code>lock</code> of the new <code>FilterReader</code> will be set + * to <code>in.lock</code>. + * + * @param in The subordinate <code>Reader</code> + */ + protected FilterReader(Reader in) + { + super(in.lock); + this.in = in; + } + + /** + * Calls the <code>in.mark(int)</code> method. + * + * @param readlimit The parameter passed to <code>in.mark(int)</code> + * + * @exception IOException If an error occurs + */ + public void mark(int readlimit) throws IOException + { + in.mark(readlimit); + } + + /** + * Calls the <code>in.markSupported()</code> method. + * + * @return <code>true</code> if mark/reset is supported, + * <code>false</code> otherwise + */ + public boolean markSupported() + { + return(in.markSupported()); + } + + /** + * Calls the <code>in.reset()</code> method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the <code>in.read()</code> method. + * + * @return The value returned from <code>in.available()</code> + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return(in.ready()); + } + + /** + * Calls the <code>in.skip(long)</code> method + * + * @param numBytes The requested number of chars to skip. + * + * @return The value returned from <code>in.skip(long)</code> + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + return(in.skip(num_chars)); + } + + /** + * Calls the <code>in.read()</code> method + * + * @return The value returned from <code>in.read()</code> + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return(in.read()); + } + + /** + * Calls the <code>in.read(char[], int, int)</code> method. + * + * @param buf The buffer to read chars into + * @param offset The index into the buffer to start storing chars + * @param len The maximum number of chars to read. + * + * @return The value retured from <code>in.read(char[], int, int)</code> + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int len) throws IOException + { + return(in.read(buf, offset, len)); + } + + /** + * This method closes the stream by calling the <code>close()</code> method + * of the underlying stream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } + +} // class FilterReader + diff --git a/libjava/classpath/java/io/FilterWriter.java b/libjava/classpath/java/io/FilterWriter.java new file mode 100644 index 00000000000..9b9ce33f9f6 --- /dev/null +++ b/libjava/classpath/java/io/FilterWriter.java @@ -0,0 +1,147 @@ +/* FilterWriter.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output character stream classes + * that filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * <code>Writer</code>. This class simply overrides all the + * methods in <code>Writer</code> to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class FilterWriter extends Writer +{ + /** + * This is the subordinate <code>Writer</code> that this class + * redirects its method calls to. + */ + protected Writer out; + + /** + * This method initializes an instance of <code>FilterWriter</code> + * to write to the specified subordinate <code>Writer</code>. + * The given <code>Writer</code> will be used as <code>lock</code> for + * the newly created <code>FilterWriter</code>. + * + * @param out The <code>Writer</code> to write to + */ + protected FilterWriter(Writer out) + { + super(out.lock); + this.out = out; + } + + /** + * This method closes the underlying <code>Writer</code>. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single char of output to the underlying + * <code>Writer</code>. + * + * @param b The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes <code>len</code> chars from the array <code>buf</code> + * starting at index <code>offset</code> to the underlying + * <code>Writer</code>. + * + * @param buf The char array to write chars from + * @param offset The index into the array to start writing chars from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf, int offset, int len) throws IOException + { + out.write(buf, offset, len); + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * starting at position <code>offset</code>. + * + * @param str The <code>String</code> that is to be written + * @param offset The character offset into the <code>String</code> + * to start writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + out.write(str, offset, len); + } + +} // class FilterWriter + diff --git a/libjava/classpath/java/io/Flushable.java b/libjava/classpath/java/io/Flushable.java new file mode 100644 index 00000000000..e9718d60a9b --- /dev/null +++ b/libjava/classpath/java/io/Flushable.java @@ -0,0 +1,62 @@ +/* Flushable.java -- Flushable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A <code>Flushable</code> class represents a stream of + * data, for which internally buffered data can be `flushed'. + * Flushing such a stream causes the buffered data to be + * written to the stream. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Flushable +{ + + /** + * Flushes the stream represented by this class, + * so that any buffered data is written to the stream. + * + * @throws IOException if an I/O error occurs in flushing. + */ + void flush() + throws IOException; + +} diff --git a/libjava/classpath/java/io/IOException.java b/libjava/classpath/java/io/IOException.java new file mode 100644 index 00000000000..cf3ad194633 --- /dev/null +++ b/libjava/classpath/java/io/IOException.java @@ -0,0 +1,74 @@ +/* IOException.java -- Generic input/output exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate an I/O problem of some sort + * occurred. Since this is a fairly generic exception, often a subclass + * of IOException will actually be thrown in order to provide a more + * detailed indication of what happened. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IOException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 7818375828146090155L; + + /** + * Create an exception without a descriptive error message. + */ + public IOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public IOException(String message) + { + super(message); + } +} // class IOException diff --git a/libjava/classpath/java/io/InputStream.java b/libjava/classpath/java/io/InputStream.java new file mode 100644 index 00000000000..86d1cd74914 --- /dev/null +++ b/libjava/classpath/java/io/InputStream.java @@ -0,0 +1,272 @@ +/* InputStream.java -- Base class for input + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of bytes. It provides a common set of methods for + * reading bytes from streams. Subclasses implement and extend these + * methods to read bytes from a particular input source such as a file + * or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class InputStream +{ + /** + * Default, no-arg, public constructor + */ + public InputStream() + { + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method always returns 0 in this class + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return 0; + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream may generate an <code>IOException</code> + * <p> + * This method does nothing in this class, but subclasses may override + * this method in order to provide additional functionality. + * + * @exception IOException If an error occurs, which can only happen + * in a subclass + */ + public void close() throws IOException + { + // Do nothing + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" by calling the <code>reset()</code> method. The + * parameter @code{readlimit} is the number of bytes that can be read + * from the stream after setting the mark before the mark becomes + * invalid. For example, if <code>mark()</code> is called with a + * read limit of 10, then when 11 bytes of data are read from the + * stream before the <code>reset()</code> method is called, then the + * mark is invalid and the stream object instance is not required to + * remember the mark. + * <p> + * This method does nothing in this class, but subclasses may override it + * to provide mark/reset functionality. + * + * @param readLimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readLimit) + { + // Do nothing + } + + /** + * This method returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + * <p> + * This method always returns <code>false</code> in this class, but + * subclasses can override this method to return <code>true</code> + * if they support mark/reset functionality. + * + * @return <code>true</code> if mark/reset functionality is + * supported, <code>false</code> otherwise + */ + public boolean markSupported() + { + return false; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public abstract int read() throws IOException; + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(b, 0, b.length)</code> + * + * @param b The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>off</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling the single byte <code>read()</code> method + * in a loop until the desired number of bytes are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a bytes fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new IndexOutOfBoundsException(); + if (b.length == 0) + return 0; + + int i, ch; + + for (i = 0; i < len; ++i) + try + { + if ((ch = read()) < 0) + return i == 0 ? -1 : i; // EOF + b[off + i] = (byte) ch; + } + catch (IOException ex) + { + // Only reading the first byte should cause an IOException. + if (i == 0) + throw ex; + return i; + } + + return i; + } + + /** + * This method resets a stream to the point where the + * <code>mark()</code> method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + * <p> + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("mark/reset not supported"); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * This method reads and discards bytes into a byte array until the + * specified number of bytes were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this metho to provide a more efficient implementation where + * one exists. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long n) throws IOException + { + // Throw away n bytes by reading them into a temp byte[]. + // Limit the temp array to 2Kb so we don't grab too much memory. + final int buflen = n > 2048 ? 2048 : (int) n; + byte[] tmpbuf = new byte[buflen]; + final long origN = n; + + while (n > 0L) + { + int numread = read(tmpbuf, 0, n > buflen ? buflen : (int) n); + if (numread <= 0) + break; + n -= numread; + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/InputStreamReader.java b/libjava/classpath/java/io/InputStreamReader.java new file mode 100644 index 00000000000..315af83e1a4 --- /dev/null +++ b/libjava/classpath/java/io/InputStreamReader.java @@ -0,0 +1,438 @@ +/* InputStreamReader.java -- Reader than transforms bytes to chars + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.CharBuffer; +import java.nio.ByteBuffer; +import gnu.java.nio.charset.EncodingHelper; + +/** + * This class reads characters from a byte input stream. The characters + * read are converted from bytes in the underlying stream by a + * decoding layer. The decoding layer transforms bytes to chars according + * to an encoding standard. There are many available encodings to choose + * from. The desired encoding can either be specified by name, or if no + * encoding is selected, the system default encoding will be used. The + * system default encoding name is determined from the system property + * <code>file.encoding</code>. The only encodings that are guaranteed to + * be availalbe are "8859_1" (the Latin-1 character set) and "UTF8". + * Unforunately, Java does not provide a mechanism for listing the + * ecodings that are supported in a given implementation. + * <p> + * Here is a list of standard encoding names that may be available: + * <p> + * <ul> + * <li>8859_1 (ISO-8859-1/Latin-1)</li> + * <li>8859_2 (ISO-8859-2/Latin-2)</li> + * <li>8859_3 (ISO-8859-3/Latin-3)</li> + * <li>8859_4 (ISO-8859-4/Latin-4)</li> + * <li>8859_5 (ISO-8859-5/Latin-5)</li> + * <li>8859_6 (ISO-8859-6/Latin-6)</li> + * <li>8859_7 (ISO-8859-7/Latin-7)</li> + * <li>8859_8 (ISO-8859-8/Latin-8)</li> + * <li>8859_9 (ISO-8859-9/Latin-9)</li> + * <li>ASCII (7-bit ASCII)</li> + * <li>UTF8 (UCS Transformation Format-8)</li> + * <li>More later</li> + * </ul> + * <p> + * It is recommended that applications do not use + * <code>InputStreamReader</code>'s + * directly. Rather, for efficiency purposes, an object of this class + * should be wrapped by a <code>BufferedReader</code>. + * <p> + * Due to a deficiency the Java class library design, there is no standard + * way for an application to install its own byte-character encoding. + * + * @see BufferedReader + * @see InputStream + * + * @author Robert Schuster + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 22, 1998. + */ +public class InputStreamReader extends Reader +{ + /** + * The input stream. + */ + private InputStream in; + + /** + * The charset decoder. + */ + private CharsetDecoder decoder; + + /** + * End of stream reached. + */ + private boolean isDone = false; + + /** + * Need this. + */ + private float maxBytesPerChar; + + /** + * Buffer holding surplus loaded bytes (if any) + */ + private ByteBuffer byteBuffer; + + /** + * java.io canonical name of the encoding. + */ + private String encoding; + + /** + * We might decode to a 2-char UTF-16 surrogate, which won't fit in the + * output buffer. In this case we need to save the surrogate char. + */ + private char savedSurrogate; + private boolean hasSavedSurrogate = false; + + /** + * This method initializes a new instance of <code>InputStreamReader</code> + * to read from the specified stream using the default encoding. + * + * @param in The <code>InputStream</code> to read from + */ + public InputStreamReader(InputStream in) + { + if (in == null) + throw new NullPointerException(); + this.in = in; + try + { + encoding = System.getProperty("file.encoding"); + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + Charset cs = EncodingHelper.getCharset(encoding); + decoder = cs.newDecoder(); + encoding = EncodingHelper.getOldCanonical(cs.name()); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } catch(UnsupportedEncodingException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * This method initializes a new instance of <code>InputStreamReader</code> + * to read from the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param in The <code>InputStream</code> to read from + * @param encoding_name The name of the encoding scheme to use + * + * @exception UnsupportedEncodingException If the encoding scheme + * requested is not available. + */ + public InputStreamReader(InputStream in, String encoding_name) + throws UnsupportedEncodingException + { + if (in == null + || encoding_name == null) + throw new NullPointerException(); + + this.in = in; + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_name)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + try { + Charset cs = EncodingHelper.getCharset(encoding_name); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder = cs.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + + // The encoding should be the old name, if such exists. + encoding = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * Creates an InputStreamReader that uses a decoder of the given + * charset to decode the bytes in the InputStream into + * characters. + */ + public InputStreamReader(InputStream in, Charset charset) { + this.in = in; + decoder = charset.newDecoder(); + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(charset.name()); + } + + /** + * Creates an InputStreamReader that uses the given charset decoder + * to decode the bytes in the InputStream into characters. + */ + public InputStreamReader(InputStream in, CharsetDecoder decoder) { + this.in = in; + this.decoder = decoder; + + try { + maxBytesPerChar = decoder.charset().newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(decoder.charset().name()); + } + + /** + * This method closes this stream, as well as the underlying + * <code>InputStream</code>. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + // Makes sure all intermediate data is released by the decoder. + if (decoder != null) + decoder.reset(); + if (in != null) + in.close(); + in = null; + isDone = true; + decoder = null; + } + } + + /** + * This method returns the name of the encoding that is currently in use + * by this object. If the stream has been closed, this method is allowed + * to return <code>null</code>. + * + * @return The current encoding name + */ + public String getEncoding() + { + return in != null ? encoding : null; + } + + /** + * This method checks to see if the stream is ready to be read. It + * will return <code>true</code> if is, or <code>false</code> if it is not. + * If the stream is not ready to be read, it could (although is not required + * to) block on the next read attempt. + * + * @return <code>true</code> if the stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return in.available() != 0; + } + + /** + * This method reads up to <code>length</code> characters from the stream into + * the specified array starting at index <code>offset</code> into the + * array. + * + * @param buf The character array to recieve the data read + * @param offset The offset into the array to start storing characters + * @param length The requested number of characters to read. + * + * @return The actual number of characters read, or -1 if end of stream. + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int length) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + if (isDone) + return -1; + if(decoder != null){ + int totalBytes = (int)((double)length * maxBytesPerChar); + byte[] bytes = new byte[totalBytes]; + + int remaining = 0; + if(byteBuffer != null) + { + remaining = byteBuffer.remaining(); + byteBuffer.get(bytes, 0, remaining); + } + int read; + if(totalBytes - remaining > 0) + { + read = in.read(bytes, remaining, totalBytes - remaining); + if(read == -1){ + read = remaining; + isDone = true; + } else + read += remaining; + } else + read = remaining; + byteBuffer = ByteBuffer.wrap(bytes, 0, read); + CharBuffer cb = CharBuffer.wrap(buf, offset, length); + int startPos = cb.position(); + + if(hasSavedSurrogate){ + hasSavedSurrogate = false; + cb.put(savedSurrogate); + read++; + } + + CoderResult cr = decoder.decode(byteBuffer, cb, isDone); + decoder.reset(); + // 1 char remains which is the first half of a surrogate pair. + if(cr.isOverflow() && cb.hasRemaining()){ + CharBuffer overflowbuf = CharBuffer.allocate(2); + cr = decoder.decode(byteBuffer, overflowbuf, isDone); + overflowbuf.flip(); + if(overflowbuf.hasRemaining()) + { + cb.put(overflowbuf.get()); + savedSurrogate = overflowbuf.get(); + hasSavedSurrogate = true; + isDone = false; + } + } + + if(byteBuffer.hasRemaining()) { + byteBuffer.compact(); + byteBuffer.flip(); + isDone = false; + } else + byteBuffer = null; + + read = cb.position() - startPos; + return (read <= 0) ? -1 : read; + } else { + byte[] bytes = new byte[length]; + int read = in.read(bytes); + for(int i=0;i<read;i++) + buf[offset+i] = (char)(bytes[i]&0xFF); + return read; + } + } + + /** + * Reads an char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return super.skip(count); + } +} diff --git a/libjava/classpath/java/io/InterruptedIOException.java b/libjava/classpath/java/io/InterruptedIOException.java new file mode 100644 index 00000000000..96ec83649f8 --- /dev/null +++ b/libjava/classpath/java/io/InterruptedIOException.java @@ -0,0 +1,94 @@ +/* InterruptedIOException.java -- an I/O operation was interrupted + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a in process I/O operation is interrupted + * for some reason. The field bytesTransferred will contain the number of + * bytes that were read/written prior to the interruption. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread#interrupt() + * @status updated to 1.4 + */ +public class InterruptedIOException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4020568460727500567L; + + /** + * The number of bytes read/written prior to the interruption. + * + * @serial count of bytes successfully transferred + */ + public int bytesTransferred; + + /** + * Create an extends without a descriptive error message. + */ + public InterruptedIOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public InterruptedIOException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message and count of + * bytes transferred. + * + * @param message the descriptive error message + * @param bytesTransferred number of bytes tranferred before interruption + */ + InterruptedIOException(String message, int bytesTransferred) + { + super(message); + this.bytesTransferred = bytesTransferred; + } +} // class InterruptedIOException diff --git a/libjava/classpath/java/io/InvalidClassException.java b/libjava/classpath/java/io/InvalidClassException.java new file mode 100644 index 00000000000..c71b0c67fc7 --- /dev/null +++ b/libjava/classpath/java/io/InvalidClassException.java @@ -0,0 +1,111 @@ +/* InvalidClassException.java -- deserializing a class failed + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is some sort of problem with a + * class during a serialization operation. This could be:<br><ul> + * <li>the serial version of the class doesn't match</li> + * <li>the class contains unknown datatypes</li> + * <li>the class does not have an accessible no-arg constructor</li> + * </ul>. + * + * <p>The field <code>classname</code> will contain the name of the + * class that caused the problem if known. The getMessage() method + * for this exception will always include the name of that class + * if known. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidClassException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4333316296251054416L; + + /** + * The name of the class which encountered the error. + * + * @serial the classname causing the error + */ + public String classname; + + /** + * Create an exception with a descriptive error message, but a null + * classname. + * + * @param message the descriptive error message + */ + public InvalidClassException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message, and the name of + * the class that caused the problem. + * + * @param classname the name of the faulty class + * @param message the descriptive error message + */ + public InvalidClassException(String classname, String message) + { + super(message); + this.classname = classname; + } + + /** + * Returns the descriptive error message for this exception. It will + * include the class name that caused the problem if known, in the format: + * <code>[classname][; ][super.getMessage()]</code>. + * + * @return A descriptive error message, may be null + */ + public String getMessage() + { + String msg = super.getMessage(); + if (msg == null) + return classname; + return (classname == null ? "" : classname + "; ") + msg; + } +} + diff --git a/libjava/classpath/java/io/InvalidObjectException.java b/libjava/classpath/java/io/InvalidObjectException.java new file mode 100644 index 00000000000..deee876db27 --- /dev/null +++ b/libjava/classpath/java/io/InvalidObjectException.java @@ -0,0 +1,66 @@ +/* InvalidObjectException.java -- deserialization failed verification + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an object fails a validation test + * during serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidObjectException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3233174318281839583L; + + /** + * Create an exception with a descriptive error message String. This should + * be the cause of the verification failure. + * + * @param message the descriptive error message + */ + public InvalidObjectException(String message) + { + super(message); + } +} // class InvalidObjectException diff --git a/libjava/classpath/java/io/LineNumberInputStream.java b/libjava/classpath/java/io/LineNumberInputStream.java new file mode 100644 index 00000000000..da43097f28c --- /dev/null +++ b/libjava/classpath/java/io/LineNumberInputStream.java @@ -0,0 +1,315 @@ +/* LineNumberInputStream.java -- An input stream which counts line numbers + Copyright (C) 1998, 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class functions like a standard <code>InputStream</code> + * except that it counts line numbers, and canonicalizes newline + * characters. As data is read, whenever the byte sequences "\r", + * "\n", or "\r\n" are encountered, the running line count is + * incremeted by one. Additionally, the whatever line termination + * sequence was encountered will be converted to a "\n" byte. Note + * that this class numbers lines from 0. When the first line + * terminator is encountered, the line number is incremented to 1, and + * so on. + * <p> + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + * <p> + * Note that since this class operates as a filter on an underlying + * stream, it has the same mark/reset functionality as the underlying + * stream. The <code>mark()</code> and <code>reset()</code> methods + * in this class handle line numbers correctly. Calling + * <code>reset()</code> resets the line number to the point at which + * <code>mark()</code> was called if the subordinate stream supports + * that functionality. + * <p> + * @deprecated This class is deprecated in favor if + * <code>LineNumberReader</code> because it operates on ASCII bytes + * instead of an encoded character stream. This class is for backward + * compatibility only and should not be used in new applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class LineNumberInputStream extends FilterInputStream +{ + /** The current line number. */ + private int lineNumber = 0; + + /** The line number when the stream was marked. */ + private int markLineNumber = 0; + + /** Flag to indicate a '\r' was just read so that an immediately + * subsequent '\n' can be ignored. */ + private boolean justReadReturnChar = false; + + /** + * Create a new <code>LineNumberInputStream</code> that reads from the + * specified subordinate <code>InputStream</code> + * + * @param in The subordinate <code>InputStream</code> to read from + */ + public LineNumberInputStream(InputStream in) + { + super(in); + } + + /** + * This method returns the number of bytes that can be read from the + * stream before the stream can block. This method is tricky + * because the subordinate <code>InputStream</code> might return + * only "\r\n" characters, which are replaced by a single "\n" + * character by the <code>read()</code> method of this class. So + * this method can only guarantee that <code>in.available() / + * 2</code> bytes can actually be read before blocking. In + * practice, considerably more bytes might be read before blocking + * <p> + * Note that the stream may not block if additional bytes beyond the count + * returned by this method are read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + // We can only guarantee half the characters that might be available + // without blocking because "\r\n" is treated as a single character. + return in.available() / 2; + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" byte calling the <code>reset()</code> method. The + * parameter <code>readlimit</code> is the number of bytes that can + * be read from the stream after setting the mark before the mark + * becomes invalid. For example, if <code>mark()</code> is called + * with a read limit of 10, then when 11 bytes of data are read from + * the stream before the <code>reset()</code> method is called, then + * the mark is invalid and the stream object instance is not + * required to remember the mark. + * <p> + * In this class, this method will remember the current line number + * as well as the current position in the stream. When the + * <code>reset()</code> method is called, the line number will be + * restored to the saved line number in addition to the stream + * position. + * <p> + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readlimit) + { + in.mark(readlimit); + markLineNumber = lineNumber; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method will return -1 if the + * end of the stream has been reached. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two bytes from the subordinate + * stream instead of just one. + * <p> + * Note that this method will block until a byte of data is available + * to be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + // Treat "\r\n" as a single character. A '\r' may have been read by + // a previous call to read so we keep an internal flag to avoid having + // to read ahead. + + int ch = in.read(); + + if (ch == '\n') + if (justReadReturnChar) + { + ch = in.read(); + justReadReturnChar = false; + } + else + lineNumber++; + else if (ch == '\r') + { + ch = '\n'; + justReadReturnChar = true; + lineNumber++; + } + else + justReadReturnChar = false; + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing data at index <code>offset</code> into + * the buffer and attemps to read <code>len</code> bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicated the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * byte is counted towards the number of bytes read in this case. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + // This case always succeeds. + if (len == 0) + return 0; + + // The simplest, though not necessarily the most time efficient thing + // to do is simply call read(void) len times. Since this is a deprecated + // class, that should be ok. + final int origOff = off; + while (len-- > 0) + { + int ch = read(); + if (ch < 0) + break; + + b[off++] = (byte) ch; + } + + // This is safe since we already know that some bytes were + // actually requested. + return off == origOff ? -1 : off - origOff; + } + + /** + * This method resets a stream to the point where the + * <code>mark()</code> method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + * <p> + * In this class, this method will also restore the line number that was + * current when the <code>mark()</code> method was called. + * <p> + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + lineNumber = markLineNumber; + justReadReturnChar = false; + } + + /** + * This method sets the current line number to the specified value. + * + * @param lineNumber The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method skips up to the requested number of bytes in the + * input stream. The actual number of bytes skipped is returned. If the + * desired number of bytes to skip is negative, no bytes are skipped. + * + * @param n requested number of bytes to skip. + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs. + */ + public long skip(long n) throws IOException + { + if (n <= 0) + return 0L; + + final long origN = n; + + do + { + int ch = read(); + if (ch < 0) + break; + if (ch == '\n' || ch == '\r') + lineNumber++; + } + while (--n > 0); + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/LineNumberReader.java b/libjava/classpath/java/io/LineNumberReader.java new file mode 100644 index 00000000000..ea418a5e4d6 --- /dev/null +++ b/libjava/classpath/java/io/LineNumberReader.java @@ -0,0 +1,417 @@ +/* LineNumberReader.java -- A character input stream which counts line numbers + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This class functions like a standard <code>Reader</code> except that it + * counts line numbers, and canonicalizes newline characters. As data + * is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered, + * the running line count is incremeted by one. Additionally, the whatever + * line termination sequence was encountered will be converted to a "\n" + * char. Note that this class numbers lines from 0. When the first + * line terminator is encountered, the line number is incremented to 1, and + * so on. Also note that actual "\r" and "\n" characters are looked for. + * The system dependent line separator sequence is ignored. + * <p> + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @date December 28, 2003. + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * + * This implementation has the feature that if '\r' is read, it + * does not look for a '\n', but immediately returns '\n'. + * On the next read(), if a '\n' is read, it is skipped. + * This has the advantage that we do not read (and hang) unnecessarily. + * + * This implementation is also minimal in the number of fields it uses. + */ +public class LineNumberReader extends BufferedReader +{ + /** The current line number. */ + private int lineNumber; + /** Whether we already found a new line in the former call. */ + private boolean matchedNewLine; + /** The saved line number when calling mark() */ + private int savedLineNumber; + + /** + * Create a new <code>LineNumberReader</code> that reads from the + * specified subordinate <code>Reader</code>. A default 8K char sized + * buffer will be used for reads. + * + * @param in The subordinate <code>Reader</code> to read from + */ + public LineNumberReader(Reader in) + { + super(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>LineNumberReader</code> to read + * from the specified subordinate <code>Reader</code> using the specified + * read buffer size. + * + * @param in The subordinate <code>Reader</code> to read from + * @param size The buffer size to use for reading + */ + public LineNumberReader(Reader in, int size) + { + super(in, size); + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method sets the current line number to the specified value. + * + * @param line_number The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" char calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, + * then when + * 11 chars of data are read from the stream before the <code>reset()</code> + * method is called, then the mark is invalid and the stream object + * instance is not required to remember the mark. + * <p> + * In this class, this method will remember the current line number as well + * as the current position in the stream. When the <code>reset()</code> + * method + * is called, the line number will be restored to the saved line number in + * addition to the stream position. + * + * @param readlimit The number of chars that can be read before the + * mark becomes invalid + * + * @exception IOException If an error occurs + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + // This is basically the same as BufferedReader.mark. + // However, if the previous character was a '\r', we need to + // save that 'r', in case the next character is a '\n'. + if (pos + readLimit > limit) + { + int saveCR = matchedNewLine ? 1 : 0; + char[] old_buffer = buffer; + if (readLimit > limit) + buffer = new char[saveCR + readLimit]; + int copy_start = pos - saveCR; + savedLineNumber = lineNumber; + limit -= copy_start; + System.arraycopy(old_buffer, copy_start, buffer, 0, limit); + pos = saveCR; + } + markPos = pos; + } + } + + /** + * This method resets a stream to the point where the <code>mark()</code> + * method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + * <p> + * In this class, this method will also restore the line number that was + * current when the <code>mark()</code> method was called. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + lineNumber = savedLineNumber; + pos = markPos; + matchedNewLine = (markPos > 0 && buffer[markPos-1] == '\r'); + } + } + + /** + * This private method fills the input buffer whatever pos is. + * Consequently pos should be checked before calling this method. + * + * @return the number of bytes actually read from the input stream or + * -1 if end of stream. + * @exception IOException If an error occurs. + */ + private int fill() throws IOException + { + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count <= 0) + return -1; + limit += count; + + return count; + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method will return -1 if the + * end of the stream has been reached. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two chars from the subordinate + * stream instead of just one. + * <p> + * Note that this method will block until a char of data is available + * to be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + skipRedundantLF(); + if (pos >= limit && fill() < 0) + return -1; + char ch = buffer[pos++]; + + if ((matchedNewLine = (ch == '\r')) || ch == '\n') + { + lineNumber++; + return '\n'; + } + matchedNewLine = false; + return (int) ch; + } + } + + /** + * This method reads chars from a stream and stores them into a caller + * supplied buffer. It starts storing data at index <code>offset</code> into + * the buffer and attemps to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicated the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * char is counted towards the number of chars read in this case. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param len The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream + * + * @exception IOException If an error occurs. + * @exception NullPointerException If buf is null (in any case). + * @exception IndexOutOfBoundsException If buffer parameters (offset and + * count) lies outside of the buffer capacity. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (buf == null) + throw new NullPointerException(); + + if (offset + count > buf.length || offset < 0) + throw new IndexOutOfBoundsException(); + + if (count <= 0) + { + if (count < 0) + throw new IndexOutOfBoundsException(); + return 0; + } + + synchronized (lock) + { + if (pos >= limit && fill() < 0) + return -1; + + int start_offset = offset; + boolean matched = matchedNewLine; + + while (count-- > 0 && pos < limit) + { + char ch = buffer[pos++]; + if (ch == '\r') + { + lineNumber++; + matched = true; + } + else if (ch == '\n' && !matched) + lineNumber++; + else + matched = false; + + buf[offset++] = ch; + } + + matchedNewLine = matched; + return offset - start_offset; + } + } + + private void skipRedundantLF() throws IOException + { + if (pos > 0 && matchedNewLine) + { + if (pos < limit) + { // fast case + if (buffer[pos] == '\n') + pos++; + } + else + { // check whether the next buffer begins with '\n'. + // in that case kill the '\n'. + if (fill() <= 0) + return; + if (buffer[pos] == '\n') + pos++; + } + matchedNewLine = true; + } + } + + /** + * This method reads a line of text from the input stream and returns + * it as a <code>String</code>. A line is considered to be terminated + * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line + * separator. + * + * @return The line read as a <code>String</code> or <code>null</code> + * if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + // BufferedReader.readLine already does this. Shouldn't need to keep + // track of newlines (since the read method deals with this for us). + // But if the buffer is large, we may not call the read method at all + // and super.readLine can't increment lineNumber itself. + // Though it may seem kludgy, the safest thing to do is to save off + // lineNumber and increment it explicitly when we're done (iff we + // ended with a '\n' or '\r' as opposed to EOF). + // + // Also, we need to undo the special casing done by BufferedReader.readLine + // when a '\r' is the last char in the buffer. That situation is marked + // by 'pos > limit'. + int tmpLineNumber = lineNumber; + skipRedundantLF(); + String str = super.readLine(); + if (pos > limit) + --pos; + + // The only case where you mustn't increment the line number is you are + // at the EOS. + if (str != null) + lineNumber = tmpLineNumber + 1; + + return str; + } + + /** + * This method skips over characters in the stream. This method will + * skip the specified number of characters if possible, but is not required + * to skip them all. The actual number of characters skipped is returned. + * This method returns 0 if the specified number of chars is less than 1. + * + * @param count The specified number of chars to skip. + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip (long count) throws IOException + { + if (count < 0) + throw new IllegalArgumentException("skip() value is negative"); + if (count == 0) + return 0; + + int skipped; + char[] buf = new char[1]; + + for (skipped = 0; skipped < count; skipped++) + { + int ch = read(buf, 0, 1); + + if (ch < 0) + break; + } + + return skipped; + } +} + diff --git a/libjava/classpath/java/io/NotActiveException.java b/libjava/classpath/java/io/NotActiveException.java new file mode 100644 index 00000000000..949ba8eca52 --- /dev/null +++ b/libjava/classpath/java/io/NotActiveException.java @@ -0,0 +1,72 @@ +/* NotActiveException.java -- thrown when serialization is not active + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs due to the fact that + * serialization is not active. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotActiveException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3893467273049808895L; + + /** + * Create an exception without a descriptive error message. + */ + public NotActiveException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public NotActiveException(String message) + { + super(message); + } +} // class NotActiveException diff --git a/libjava/classpath/java/io/NotSerializableException.java b/libjava/classpath/java/io/NotSerializableException.java new file mode 100644 index 00000000000..d49c939e31d --- /dev/null +++ b/libjava/classpath/java/io/NotSerializableException.java @@ -0,0 +1,74 @@ +/* NotSerializableException.java -- a Serializable class that isn't + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a class implements Serializable because + * of a superclass, but should not be serialized. The descriptive message + * will consist of the name of the class in question. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotSerializableException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2906642554793891381L; + + /** + * Create an exception without a descriptive error message. + */ + public NotSerializableException() + { + } + + /** + * Create an exception with a descriptive error message, which should + * be the name of the class. + * + * @param message the descriptive error message + */ + public NotSerializableException(String message) + { + super(message); + } +} // class NotSerializableException diff --git a/libjava/classpath/java/io/ObjectInput.java b/libjava/classpath/java/io/ObjectInput.java new file mode 100644 index 00000000000..175b60f9dc0 --- /dev/null +++ b/libjava/classpath/java/io/ObjectInput.java @@ -0,0 +1,140 @@ +/* ObjectInput.java -- Read object data from a stream + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends the <code>DataInput</code> interface to provide a + * facility to read objects as well as primitive types from a stream. It + * also has methods that allow input to be done in a manner similar to + * <code>InputStream</code> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataInput + */ +public interface ObjectInput extends DataInput +{ + /** + * This method returns the number of bytes that can be read without + * blocking. + * + * @return The number of bytes available before blocking + * + * @exception IOException If an error occurs + */ + int available() throws IOException; + + /** + * This method reading a byte of data from a stream. It returns that byte + * as an <code>int</code>. This method blocks if no data is available + * to be read. + * + * @return The byte of data read + * + * @exception IOException If an error occurs + */ + int read() throws IOException; + + /** + * This method reads raw bytes and stores them them a byte array buffer. + * Note that this method will block if no data is available. However, + * it will not necessarily block until it fills the entire buffer. That is, + * a "short count" is possible. + * + * @param buf The byte array to receive the data read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf) throws IOException; + + /** + * This method reads raw bytes and stores them in a byte array buffer + * <code>buf</code> starting at position <code>offset</code> into the + * buffer. A + * maximum of <code>len</code> bytes will be read. Note that this method + * blocks if no data is available, but will not necessarily block until + * it can read <code>len</code> bytes of data. That is, a "short count" is + * possible. + * + * @param buf The byte array to receive the data read + * @param offset The offset into <code>buf</code> to start storing data + * @param len The maximum number of bytes to read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf, int offset, int len) throws IOException; + + /** + * Reads an object instance and returns it. If the class for the object + * being read cannot be found, then a <code>ClassNotFoundException</code> + * will be thrown. + * + * @return The object instance that was read + * + * @exception ClassNotFoundException If a class for the object cannot be + * found + * @exception IOException If any other error occurs + */ + Object readObject() + throws ClassNotFoundException, IOException; + + /** + * This method causes the specified number of bytes to be read and + * discarded. It is possible that fewer than the requested number of bytes + * will actually be skipped. + * + * @param numBytes The number of bytes to skip + * + * @return The actual number of bytes skipped + * + * @exception IOException If an error occurs + */ + long skip(long numBytes) throws IOException; + + /** + * This method closes the input source + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java new file mode 100644 index 00000000000..05776a7fcdd --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -0,0 +1,1956 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import gnu.java.io.ObjectIdentityWrapper; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Vector; + +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + * Creates a new <code>ObjectInputStream</code> that will do all of + * its reading from <code>in</code>. This method also checks + * the stream by reading the header information (stream magic number + * and stream version). + * + * @exception IOException Reading stream header from underlying + * stream cannot be completed. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + * + * @see #readStreamHeader() + */ + public ObjectInputStream(InputStream in) + throws IOException, StreamCorruptedException + { + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (dump == false && val != null && !val.equals("")) + { + dump = true; + System.out.println ("Serialization debugging enabled"); + } + else if (dump == true && (val == null || val.equals(""))) + { + dump = false; + System.out.println ("Serialization debugging disabled"); + } + } + + this.resolveEnabled = false; + this.isDeserializing = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream(this); + this.realInputStream = new DataInputStream(in); + this.nextOID = baseWireHandle; + this.objectLookupTable = new Hashtable(); + this.validators = new Vector(); + this.classLookupTable = new Hashtable(); + setBlockDataMode(true); + readStreamHeader(); + } + + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * <code>private void readObject (ObjectInputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * <code>InputStream</code>. + */ + public final Object readObject() throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride(); + + boolean was_deserializing; + + Object ret_val; + was_deserializing = this.isDeserializing; + + boolean is_consumed = false; + boolean old_mode = setBlockDataMode(false); + + this.isDeserializing = true; + + byte marker = this.realInputStream.readByte(); + + depth += 2; + + if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); + + try + { + switch (marker) + { + case TC_ENDBLOCKDATA: + { + ret_val = null; + is_consumed = true; + break; + } + + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + { + if (marker == TC_BLOCKDATALONG) + { if(dump) dumpElementln("BLOCKDATALONG"); } + else + { if(dump) dumpElementln("BLOCKDATA"); } + readNextBlock(marker); + throw new StreamCorruptedException("Unexpected blockData"); + } + + case TC_NULL: + { + if(dump) dumpElementln("NULL"); + ret_val = null; + break; + } + + case TC_REFERENCE: + { + if(dump) dumpElement("REFERENCE "); + Integer oid = new Integer(this.realInputStream.readInt()); + if(dump) dumpElementln(Integer.toHexString(oid.intValue())); + ret_val = ((ObjectIdentityWrapper) + this.objectLookupTable.get(oid)).object; + break; + } + + case TC_CLASS: + { + if(dump) dumpElementln("CLASS"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + assignNewHandle(clazz); + ret_val = clazz; + break; + } + + case TC_PROXYCLASSDESC: + { + if(dump) dumpElementln("PROXYCLASS"); + int n_intf = this.realInputStream.readInt(); + String[] intfs = new String[n_intf]; + for (int i = 0; i < n_intf; i++) + { + intfs[i] = this.realInputStream.readUTF(); + System.out.println(intfs[i]); + } + + boolean oldmode = setBlockDataMode(true); + Class cl = resolveProxyClass(intfs); + setBlockDataMode(oldmode); + + ObjectStreamClass osc = lookupClass(cl); + assignNewHandle(osc); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + ObjectStreamClass superosc = (ObjectStreamClass)readObject(); + osc.setSuperclass(superosc); + ret_val = osc; + break; + } + + case TC_CLASSDESC: + { + ObjectStreamClass osc = readClassDescriptor(); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + + osc.setSuperclass ((ObjectStreamClass)readObject()); + ret_val = osc; + break; + } + + case TC_STRING: + case TC_LONGSTRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTF(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s)); + break; + } + + case TC_ARRAY: + { + if(dump) dumpElementln("ARRAY"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class componentType = osc.forClass().getComponentType(); + if(dump) dumpElement("ARRAY LENGTH="); + int length = this.realInputStream.readInt(); + if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); + Object array = Array.newInstance(componentType, length); + int handle = assignNewHandle(array); + readArrayElements(array, componentType); + if(dump) + for (int i = 0, len = Array.getLength(array); i < len; i++) + dumpElementln(" ELEMENT[" + i + "]=" + Array.get(array, i)); + ret_val = processResolution(null, array, handle); + break; + } + + case TC_OBJECT: + { + if(dump) dumpElementln("OBJECT"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + + if (!osc.realClassIsSerializable) + throw new NotSerializableException + (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (osc.realClassIsExternalizable) + { + Externalizable obj = osc.newInstance(); + + int handle = assignNewHandle(obj); + + boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); + + boolean oldmode = this.readDataFromBlock; + if (read_from_blocks) + setBlockDataMode(true); + + obj.readExternal(this); + + if (read_from_blocks) + { + setBlockDataMode(oldmode); + if (!oldmode) + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); + } + + ret_val = processResolution(osc, obj, handle); + break; + } // end if (osc.realClassIsExternalizable) + + Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); + + int handle = assignNewHandle(obj); + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + + this.currentObject = obj; + ObjectStreamClass[] hierarchy = + inputGetObjectStreamClasses(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + + if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + + Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; + if (readObjectMethod != null) + { + fieldsAlreadyRead = false; + boolean oldmode = setBlockDataMode(true); + callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); + setBlockDataMode(oldmode); + } + else + { + readFields(obj, currentObjectStreamClass); + } + + if (this.currentObjectStreamClass.hasWriteMethod()) + { + if(dump) dumpElement("ENDBLOCKDATA? "); + try + { + // FIXME: XXX: This try block is to + // catch EOF which is thrown for some + // objects. That indicates a bug in + // the logic. + + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException + ("No end of block data seen for class with readObject (ObjectInputStream) method."); + if(dump) dumpElementln("yes"); + } +// catch (EOFException e) +// { +// if(dump) dumpElementln("no, got EOFException"); +// } + catch (IOException e) + { + if(dump) dumpElementln("no, got IOException"); + } + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + ret_val = processResolution(osc, obj, handle); + + break; + } + + case TC_RESET: + if(dump) dumpElementln("RESET"); + clearHandles(); + ret_val = readObject(); + break; + + case TC_EXCEPTION: + { + if(dump) dumpElement("EXCEPTION="); + Exception e = (Exception)readObject(); + if(dump) dumpElementln(e.toString()); + clearHandles(); + throw new WriteAbortedException("Exception thrown during writing of stream", e); + } + + default: + throw new IOException("Unknown marker on stream: " + marker); + } + } + finally + { + setBlockDataMode(old_mode); + + this.isDeserializing = was_deserializing; + + depth -= 2; + + if (! was_deserializing) + { + if (validators.size() > 0) + invokeValidators(); + } + } + + return ret_val; + } + + /** + * This method makes a partial check of types for the fields + * contained given in arguments. It checks primitive types of + * fields1 against non primitive types of fields2. This method + * assumes the two lists has already been sorted according to + * the Java specification. + * + * @param name Name of the class owning the given fields. + * @param fields1 First list to check. + * @param fields2 Second list to check. + * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present + * in the non primitive part in fields2. + */ + private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) + throws InvalidClassException + { + int nonPrimitive = 0; + + for (nonPrimitive = 0; + nonPrimitive < fields1.length + && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) + { + } + + if (nonPrimitive == fields1.length) + return; + + int i = 0; + ObjectStreamField f1; + ObjectStreamField f2; + + while (i < fields2.length + && nonPrimitive < fields1.length) + { + f1 = fields1[nonPrimitive]; + f2 = fields2[i]; + + if (!f2.isPrimitive()) + break; + + int compVal = f1.getName().compareTo (f2.getName()); + + if (compVal < 0) + { + nonPrimitive++; + } + else if (compVal > 0) + { + i++; + } + else + { + throw new InvalidClassException + ("invalid field type for " + f2.getName() + + " in class " + name); + } + } + } + + /** + * This method reads a class descriptor from the real input stream + * and use these data to create a new instance of ObjectStreamClass. + * Fields are sorted and ordered for the real read which occurs for + * each instance of the described class. Be aware that if you call that + * method you must ensure that the stream is synchronized, in the other + * case it may be completely desynchronized. + * + * @return A new instance of ObjectStreamClass containing the freshly + * created descriptor. + * @throws ClassNotFoundException if the required class to build the + * descriptor has not been found in the system. + * @throws IOException An input/output error occured. + * @throws InvalidClassException If there was a compatibility problem + * between the class present in the system and the serialized class. + */ + protected ObjectStreamClass readClassDescriptor() + throws ClassNotFoundException, IOException + { + if(dump) dumpElement("CLASSDESC NAME="); + String name = this.realInputStream.readUTF(); + if(dump) dumpElement(name + "; UID="); + long uid = this.realInputStream.readLong (); + if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); + byte flags = this.realInputStream.readByte (); + if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); + short field_count = this.realInputStream.readShort(); + if(dump) dumpElementln(Short.toString(field_count)); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + ObjectStreamClass osc = new ObjectStreamClass(name, uid, + flags, fields); + assignNewHandle(osc); + + if (callersClassLoader == null) + callersClassLoader = currentLoader(); + + for (int i = 0; i < field_count; i++) + { + if(dump) dumpElement(" TYPE CODE="); + char type_code = (char)this.realInputStream.readByte(); + if(dump) dumpElement(type_code + "; FIELD NAME="); + String field_name = this.realInputStream.readUTF(); + if(dump) dumpElementln(field_name); + String class_name; + + // If the type code is an array or an object we must + // decode a String here. In the other case we convert + // the type code and pass it to ObjectStreamField. + // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject(); + else + class_name = String.valueOf(type_code); + + fields[i] = + new ObjectStreamField(field_name, class_name, callersClassLoader); + } + + /* Now that fields have been read we may resolve the class + * (and read annotation if needed). */ + Class clazz; + try + { + clazz = resolveClass(osc); + } + catch (ClassNotFoundException cnfe) + { + // Maybe it was an primitive class? + if (name.equals("void")) + clazz = Void.TYPE; + else if (name.equals("boolean")) + clazz = Boolean.TYPE; + else if (name.equals("byte")) + clazz = Byte.TYPE; + else if (name.equals("short")) + clazz = Short.TYPE; + else if (name.equals("char")) + clazz = Character.TYPE; + else if (name.equals("int")) + clazz = Integer.TYPE; + else if (name.equals("long")) + clazz = Long.TYPE; + else if (name.equals("float")) + clazz = Float.TYPE; + else if (name.equals("double")) + clazz = Double.TYPE; + else + throw cnfe; + } + + boolean oldmode = setBlockDataMode(true); + osc.setClass(clazz, lookupClass(clazz.getSuperclass())); + classLookupTable.put(clazz, osc); + setBlockDataMode(oldmode); + + // find the first non-serializable, non-abstract + // class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass(); + // Maybe it is a primitive class, those don't have a super class, + // or Object itself. Otherwise we can keep getting the superclass + // till we hit the Object class, or some other non-serializable class. + + if (first_nonserial == null) + first_nonserial = clazz; + else + while (Serializable.class.isAssignableFrom(first_nonserial) + || Modifier.isAbstract(first_nonserial.getModifiers())) + first_nonserial = first_nonserial.getSuperclass(); + + final Class local_constructor_class = first_nonserial; + + osc.firstNonSerializableParentConstructor = + (Constructor)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Constructor c = local_constructor_class. + getDeclaredConstructor(new Class[0]); + if (Modifier.isPrivate(c.getModifiers())) + return null; + return c; + } + catch (NoSuchMethodException e) + { + // error will be reported later, in newObject() + return null; + } + } + }); + + osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); + osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); + + ObjectStreamField[] stream_fields = osc.fields; + ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; + ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; + + int stream_idx = 0; + int real_idx = 0; + int map_idx = 0; + + /* + * Check that there is no type inconsistencies between the lists. + * A special checking must be done for the two groups: primitive types and + * not primitive types. + */ + checkTypeConsistency(name, real_fields, stream_fields); + checkTypeConsistency(name, stream_fields, real_fields); + + + while (stream_idx < stream_fields.length + || real_idx < real_fields.length) + { + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + + if (stream_idx == stream_fields.length) + { + real_field = real_fields[real_idx++]; + } + else if (real_idx == real_fields.length) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + int comp_val = + real_fields[real_idx].compareTo (stream_fields[stream_idx]); + + if (comp_val < 0) + { + real_field = real_fields[real_idx++]; + } + else if (comp_val > 0) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + stream_field = stream_fields[stream_idx++]; + real_field = real_fields[real_idx++]; + if (stream_field.getType() != real_field.getType()) + throw new InvalidClassException + ("invalid field type for " + real_field.getName() + + " in class " + name); + } + } + + /* If some of stream_fields does not correspond to any of real_fields, + * or the opposite, then fieldmapping will go short. + */ + if (map_idx == fieldmapping.length) + { + ObjectStreamField[] newfieldmapping = + new ObjectStreamField[fieldmapping.length + 2]; + System.arraycopy(fieldmapping, 0, + newfieldmapping, 0, fieldmapping.length); + fieldmapping = newfieldmapping; + } + fieldmapping[map_idx++] = stream_field; + fieldmapping[map_idx++] = real_field; + } + osc.fieldMapping = fieldmapping; + + return osc; + } + + /** + * Reads the current objects non-transient, non-static fields from + * the current class from the underlying output stream. + * + * This method is intended to be called from within a object's + * <code>private void readObject (ObjectInputStream)</code> + * method. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * <code>private void readObject (ObjectInputStream)</code> + * method. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public void defaultReadObject() + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("defaultReadObject called by non-active" + + " class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException("defaultReadObject called but fields " + + "already read from stream (by " + + "defaultReadObject or readFields)"); + + boolean oldmode = setBlockDataMode(false); + readFields(this.currentObject, this.currentObjectStreamClass); + setBlockDataMode(oldmode); + + fieldsAlreadyRead = true; + } + + + /** + * Registers a <code>ObjectInputValidation</code> to be carried out + * on the object graph currently being deserialized before it is + * returned to the original caller of <code>readObject ()</code>. + * The order of validation for multiple + * <code>ObjectInputValidation</code>s can be controled using + * <code>priority</code>. Validators with higher priorities are + * called first. + * + * @see java.io.ObjectInputValidation + * + * @exception InvalidObjectException <code>validator</code> is + * <code>null</code> + * + * @exception NotActiveException an attempt was made to add a + * validator outside of the <code>readObject</code> method of the + * object currently being deserialized + */ + public void registerValidation(ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("registerValidation called by non-active " + + "class and/or object"); + + if (validator == null) + throw new InvalidObjectException("attempt to add a null " + + "ObjectInputValidation object"); + + this.validators.addElement(new ValidatorAndPriority (validator, + priority)); + } + + + /** + * Called when a class is being deserialized. This is a hook to + * allow subclasses to read in information written by the + * <code>annotateClass (Class)</code> method of an + * <code>ObjectOutputStream</code>. + * + * This implementation looks up the active call stack for a + * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, + * it is used to load the class associated with <code>osc</code>, + * otherwise, the default system <code>ClassLoader</code> is used. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass(ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { + if (callersClassLoader == null) + { + callersClassLoader = currentLoader (); + if (DEBUG && dump) + { + dumpElementln ("CallersClassLoader = " + callersClassLoader); + } + } + + return Class.forName(osc.getName(), true, callersClassLoader); + } + + /** + * Returns the most recent user defined ClassLoader on the execution stack + * or null if none is found. + */ + private ClassLoader currentLoader() + { + return VMObjectInputStream.currentClassLoader(); + } + + /** + * Lookup a class stored in the local hashtable. If it is not + * use the global lookup function in ObjectStreamClass to build + * the ObjectStreamClass. This method is requested according to + * the behaviour detected in the JDK by Kaffe's team. + * + * @param clazz Class to lookup in the hash table or for which + * we must build a descriptor. + * @return A valid instance of ObjectStreamClass corresponding + * to the specified class. + */ + private ObjectStreamClass lookupClass(Class clazz) + { + if (clazz == null) + return null; + + ObjectStreamClass oclazz; + oclazz = (ObjectStreamClass)classLookupTable.get(clazz); + if (oclazz == null) + return ObjectStreamClass.lookup(clazz); + else + return oclazz; + } + + /** + * Reconstruct class hierarchy the same way + * {@link java.io.ObjectStreamClass.getObjectStreamClasses(java.lang.Class)} does + * but using lookupClass instead of ObjectStreamClass.lookup. This + * dup is necessary localize the lookup table. Hopefully some future + * rewritings will be able to prevent this. + * + * @param clazz This is the class for which we want the hierarchy. + * + * @return An array of valid {@link java.io.ObjectStreamClass} instances which + * represent the class hierarchy for clazz. + */ + private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz) + { + ObjectStreamClass osc = lookupClass(clazz); + + if (osc == null) + return new ObjectStreamClass[0]; + else + { + Vector oscs = new Vector(); + + while (osc != null) + { + oscs.addElement(osc); + osc = osc.getSuper(); + } + + int count = oscs.size(); + ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count]; + + for (int i = count - 1; i >= 0; i--) + sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i); + + return sorted_oscs; + } + } + + /** + * Allows subclasses to resolve objects that are read from the + * stream with other objects to be returned in their place. This + * method is called the first time each object is encountered. + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see #enableResolveObject(boolean) + */ + protected Object resolveObject(Object obj) throws IOException + { + return obj; + } + + + protected Class resolveProxyClass(String[] intfs) + throws IOException, ClassNotFoundException + { + ClassLoader cl = currentLoader(); + + Class[] clss = new Class[intfs.length]; + if(cl == null) + { + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i]); + cl = ClassLoader.getSystemClassLoader(); + } + else + for (int i = 0; i < intfs.length; i++) + clss[i] = cl.loadClass(intfs[i]); + try + { + return Proxy.getProxyClass(cl, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); + } + } + + /** + * If <code>enable</code> is <code>true</code> and this object is + * trusted, then <code>resolveObject (Object)</code> will be called + * in subsequent calls to <code>readObject (Object)</code>. + * Otherwise, <code>resolveObject (Object)</code> will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + /** + * Reads stream magic and stream version information from the + * underlying stream. + * + * @exception IOException Exception from underlying stream. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + */ + protected void readStreamHeader() + throws IOException, StreamCorruptedException + { + if(dump) dumpElement("STREAM MAGIC "); + if (this.realInputStream.readShort() != STREAM_MAGIC) + throw new StreamCorruptedException("Invalid stream magic number"); + + if(dump) dumpElementln("STREAM VERSION "); + if (this.realInputStream.readShort() != STREAM_VERSION) + throw new StreamCorruptedException("Invalid stream version number"); + } + + public int read() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock(); + return (this.blockData[this.blockDataPosition++] & 0xff); + } + else + return this.realInputStream.read(); + } + + public int read(byte[] data, int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition + length > this.blockDataBytes) + { + int remain = this.blockDataBytes - this.blockDataPosition; + if (remain != 0) + { + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, remain); + offset += remain; + length -= remain; + } + readNextBlock (); + } + + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, length); + this.blockDataPosition += length; + + return length; + } + else + return this.realInputStream.read(data, offset, length); + } + + public int available() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available(); + } + + public void close() throws IOException + { + this.realInputStream.close(); + } + + public boolean readBoolean() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode (true); + boolean value = this.dataInputStream.readBoolean (); + if (switchmode) + setBlockDataMode (oldmode); + return value; + } + + public byte readByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + byte value = this.dataInputStream.readByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public short readShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + short value = this.dataInputStream.readShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public char readChar() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + char value = this.dataInputStream.readChar(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readInt() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readInt(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public long readLong() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + long value = this.dataInputStream.readLong(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public float readFloat() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + float value = this.dataInputStream.readFloat(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public double readDouble() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + double value = this.dataInputStream.readDouble(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public void readFully(byte data[]) throws IOException + { + this.dataInputStream.readFully(data); + } + + public void readFully(byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully(data, offset, size); + } + + public int skipBytes(int len) throws IOException + { + return this.dataInputStream.skipBytes(len); + } + + /** + * @deprecated + * @see java.io.DataInputStream#readLine () + */ + public String readLine() throws IOException + { + return this.dataInputStream.readLine(); + } + + public String readUTF() throws IOException + { + return this.dataInputStream.readUTF(); + } + + /** + * This class allows a class to specify exactly which fields should + * be read, and what values should be read for these fields. + * + * XXX: finish up comments + */ + public abstract static class GetField + { + public abstract ObjectStreamClass getObjectStreamClass(); + + public abstract boolean defaulted(String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get(String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get(String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get(String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get(String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get(String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get(String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + /** + * This method should be called by a method called 'readObject' in the + * deserializing class (if present). It cannot (and should not)be called + * outside of it. Its goal is to read all fields in the real input stream + * and keep them accessible through the {@link #GetField} class. Calling + * this method will not alter the deserializing object. + * + * @return A valid freshly created 'GetField' instance to get access to + * the deserialized stream. + * @throws IOException An input/output exception occured. + * @throws ClassNotFoundException + * @throws NotActiveException + */ + public GetField readFields() + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("readFields called by non-active class and/or object"); + + if (prereadFields != null) + return prereadFields; + + if (fieldsAlreadyRead) + throw new NotActiveException("readFields called but fields already read from" + + " stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + + // Apparently Block data is not used with GetField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + readFully(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject(); + setBlockDataMode(oldmode); + + prereadFields = new GetField() + { + public ObjectStreamClass getObjectStreamClass() + { + return clazz; + } + + public boolean defaulted(String name) + throws IOException, IllegalArgumentException + { + ObjectStreamField f = clazz.getField(name); + + /* First if we have a serialized field use the descriptor */ + if (f != null) + { + /* It is in serialPersistentFields but setClass tells us + * it should not be set. This value is defaulted. + */ + if (f.isPersistent() && !f.isToSet()) + return true; + + return false; + } + + /* This is not a serialized field. There should be + * a default value only if the field really exists. + */ + try + { + return (clazz.forClass().getDeclaredField (name) != null); + } + catch (NoSuchFieldException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + public boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()] == 0 ? false : true; + } + + public char get(String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()]; + } + + public short get(String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get(String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get(String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (long)(((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get(String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get(String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Double.longBitsToDouble + ( (long) (((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = + getField(name, defvalue == null ? null : defvalue.getClass ()); + + if (field == null) + return defvalue; + + return objs[field.getOffset()]; + } + + private ObjectStreamField getField(String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField(name); + boolean illegal = false; + + try + { + try + { + Class field_type = field.getType(); + + if (type == field_type || + (type == null && !field_type.isPrimitive())) + { + /* See defaulted */ + return field; + } + + illegal = true; + throw new IllegalArgumentException + ("Field requested is of type " + + field_type.getName() + + ", but requested type was " + + (type == null ? "Object" : type.getName())); + } + catch (NullPointerException _) + { + /* Here we catch NullPointerException, because it may + only come from the call 'field.getType()'. If field + is null, we have to return null and classpath ethic + say we must try to avoid 'if (xxx == null)'. + */ + } + catch (IllegalArgumentException e) + { + throw e; + } + + return null; + } + finally + { + /* If this is an unassigned field we should return + * the default value. + */ + if (!illegal && field != null && !field.isToSet() && field.isPersistent()) + return null; + + /* We do not want to modify transient fields. They should + * be left to 0. + */ + try + { + Field f = clazz.forClass().getDeclaredField(name); + if (Modifier.isTransient(f.getModifiers())) + throw new IllegalArgumentException + ("no such field (non transient) " + name); + if (field == null && f.getType() != type) + throw new IllegalArgumentException + ("Invalid requested type for field " + name); + } + catch (NoSuchFieldException e) + { + if (field == null) + throw new IllegalArgumentException(e.getMessage()); + } + + } + } + }; + + fieldsAlreadyRead = true; + return prereadFields; + } + + /** + * Protected constructor that allows subclasses to override + * deserialization. This constructor should be called by subclasses + * that wish to override <code>readObject (Object)</code>. This + * method does a security check <i>NOTE: currently not + * implemented</i>, then sets a flag that informs + * <code>readObject (Object)</code> to call the subclasses + * <code>readObjectOverride (Object)</code> method. + * + * @see #readObjectOverride() + */ + protected ObjectInputStream() + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager(); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + /** + * This method allows subclasses to override the default + * de serialization mechanism provided by + * <code>ObjectInputStream</code>. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from their constructor. + * + * @see #ObjectInputStream() + */ + protected Object readObjectOverride() + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + /** + * Assigns the next available handle to <code>obj</code>. + * + * @param obj The object for which we want a new handle. + * @return A valid handle for the specified object. + */ + private int assignNewHandle(Object obj) + { + this.objectLookupTable.put(new Integer(this.nextOID), + new ObjectIdentityWrapper(obj)); + return this.nextOID++; + } + + private Object processResolution(ObjectStreamClass osc, Object obj, int handle) + throws IOException + { + if (osc != null && obj instanceof Serializable) + { + try + { + Method m = osc.readResolveMethod; + if(m != null) + { + obj = m.invoke(obj, new Object[] {}); + } + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException ignore) + { + } + } + + if (this.resolveEnabled) + obj = resolveObject(obj); + + this.objectLookupTable.put(new Integer(handle), + new ObjectIdentityWrapper(obj)); + + return obj; + } + + private void clearHandles() + { + this.objectLookupTable.clear(); + this.nextOID = baseWireHandle; + } + + private void readNextBlock() throws IOException + { + readNextBlock(this.realInputStream.readByte()); + } + + private void readNextBlock(byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { + if(dump) dumpElement("BLOCK DATA SIZE="); + this.blockDataBytes = this.realInputStream.readUnsignedByte(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else if (marker == TC_BLOCKDATALONG) + { + if(dump) dumpElement("BLOCK DATA LONG SIZE="); + this.blockDataBytes = this.realInputStream.readInt(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else + { + throw new EOFException("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean(); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte(); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar(); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble(); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat(); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt(); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong(); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort(); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject(); + } + } + + private void readFields (Object obj, ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + ObjectStreamField[] fields = stream_osc.fieldMapping; + + for (int i = 0; i < fields.length; i += 2) + { + ObjectStreamField stream_field = fields[i]; + ObjectStreamField real_field = fields[i + 1]; + boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); + boolean set_value = (real_field != null && real_field.isToSet()); + String field_name; + char type; + + if (stream_field != null) + { + field_name = stream_field.getName(); + type = stream_field.getTypeCode(); + } + else + { + field_name = real_field.getName(); + type = real_field.getTypeCode(); + } + + switch(type) + { + case 'Z': + { + boolean value = + read_value ? this.realInputStream.readBoolean() : false; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setBooleanField(obj, value); + break; + } + case 'B': + { + byte value = + read_value ? this.realInputStream.readByte() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setByteField(obj, value); + break; + } + case 'C': + { + char value = + read_value ? this.realInputStream.readChar(): 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setCharField(obj, value); + break; + } + case 'D': + { + double value = + read_value ? this.realInputStream.readDouble() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setDoubleField(obj, value); + break; + } + case 'F': + { + float value = + read_value ? this.realInputStream.readFloat() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setFloatField(obj, value); + break; + } + case 'I': + { + int value = + read_value ? this.realInputStream.readInt() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setIntField(obj, value); + break; + } + case 'J': + { + long value = + read_value ? this.realInputStream.readLong() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setLongField(obj, value); + break; + } + case 'S': + { + short value = + read_value ? this.realInputStream.readShort() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setShortField(obj, value); + break; + } + case 'L': + case '[': + { + Object value = + read_value ? readObject() : null; + if (set_value) + real_field.setObjectField(obj, value); + break; + } + default: + throw new InternalError("Invalid type code: " + type); + } + } + } + + // Toggles writing primitive data to block-data buffer. + private boolean setBlockDataMode (boolean on) + { + boolean oldmode = this.readDataFromBlock; + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + return oldmode; + } + + // returns a new instance of REAL_CLASS that has been constructed + // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Constructor constructor) + throws ClassNotFoundException, IOException + { + if (constructor == null) + throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); + try + { + return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); + } + catch (InstantiationException e) + { + throw new ClassNotFoundException + ("Instance of " + real_class + " could not be created"); + } + } + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators() throws InvalidObjectException + { + Object[] validators = new Object[this.validators.size()]; + this.validators.copyInto (validators); + Arrays.sort (validators); + + try + { + for (int i=0; i < validators.length; i++) + ((ObjectInputValidation)validators[i]).validateObject(); + } + finally + { + this.validators.removeAllElements(); + } + } + + private void callReadMethod (Method readObject, Class klass, Object obj) + throws ClassNotFoundException, IOException + { + try + { + readObject.invoke(obj, new Object[] { this }); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + if (exception instanceof ClassNotFoundException) + throw (ClassNotFoundException) exception; + + throw new IOException("Exception thrown from readObject() on " + + klass + ": " + exception.getClass().getName()); + } + catch (Exception x) + { + throw new IOException("Failure invoking readObject() on " + + klass + ": " + x.getClass().getName()); + } + + // Invalidate fields which has been read through readFields. + prereadFields = null; + } + + private static final int BUFFER_SIZE = 1024; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Hashtable objectLookupTable; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private boolean readDataFromBlock; + private boolean isDeserializing; + private boolean fieldsAlreadyRead; + private Vector validators; + private Hashtable classLookupTable; + private GetField prereadFields; + + private ClassLoader callersClassLoader; + private static boolean dump; + + // The nesting depth for debugging output + private int depth = 0; + + private static final boolean DEBUG = false; + + private void dumpElement (String msg) + { + System.out.print(msg); + } + + private void dumpElementln (String msg) + { + System.out.println(msg); + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javaio"); + } + } + + // used to keep a prioritized list of object validators + private static final class ValidatorAndPriority implements Comparable + { + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } + } +} + diff --git a/libjava/classpath/java/io/ObjectInputValidation.java b/libjava/classpath/java/io/ObjectInputValidation.java new file mode 100644 index 00000000000..4fdb8414f63 --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputValidation.java @@ -0,0 +1,67 @@ +/* ObjectInputValidation.java -- Validate an object + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows an object to validate that it is valid after + * deserialization has run completely for it and all dependent objects. + * This allows an object to determine if it is invalid even if all + * state data was correctly deserialized from the stream. It can also + * be used to perform re-initialization type activities on an object + * after it has been completely deserialized. + * + * Since this method functions as a type of callback, it must be + * registered through <code>ObjectInputStream.registerValidation</code> + * in order to be invoked. This is typically done in the + * <code>readObject</code> method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see ObjectInputStream#registerValidation + */ +public interface ObjectInputValidation +{ + /** + * This method is called to validate an object after serialization + * is complete. If the object is invalid an exception is thrown. + * + * @exception InvalidObjectException If the object is invalid + */ + void validateObject() throws InvalidObjectException; +} diff --git a/libjava/classpath/java/io/ObjectOutput.java b/libjava/classpath/java/io/ObjectOutput.java new file mode 100644 index 00000000000..d35a09c3acb --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutput.java @@ -0,0 +1,111 @@ +/* ObjectOutput.java -- Interface for writing objects to a stream + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends <code>DataOutput</code> to provide the additional + * facility of writing object instances to a stream. It also adds some + * additional methods to make the interface more + * <code>OutputStream</code> like. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataOutput + */ +public interface ObjectOutput extends DataOutput +{ + /** + * This method writes the specified byte to the output stream. + * + * @param b The byte to write. + * + * @exception IOException If an error occurs. + */ + void write(int b) throws IOException; + + /** + * This method writes all the bytes in the specified byte array to the + * output stream. + * + * @param buf The array of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes <code>len</code> bytes from the specified array + * starting at index <code>offset</code> into that array. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf, int offset, int len) + throws IOException; + + /** + * This method writes a object instance to a stream. The format of the + * data written is determined by the actual implementation of this method + * + * @param obj The object to write + * + * @exception IOException If an error occurs + */ + void writeObject(Object obj) throws IOException; + + /** + * This method causes any buffered data to be flushed out to the underlying + * stream + * + * @exception IOException If an error occurs + */ + void flush() throws IOException; + + /** + * This method closes the underlying stream. + * + * @exception IOException If an error occurs + */ + void close() throws IOException; + +} // interface ObjectOutput + diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java new file mode 100644 index 00000000000..5e754c5ec7a --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutputStream.java @@ -0,0 +1,1578 @@ +/* ObjectOutputStream.java -- Class used to write serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Configuration; +import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.util.Hashtable; + +/** + * An <code>ObjectOutputStream</code> can be used to write objects + * as well as primitive data in a platform-independent manner to an + * <code>OutputStream</code>. + * + * The data produced by an <code>ObjectOutputStream</code> can be read + * and reconstituted by an <code>ObjectInputStream</code>. + * + * <code>writeObject (Object)</code> is used to write Objects, the + * <code>write<type></code> methods are used to write primitive + * data (as in <code>DataOutputStream</code>). Strings can be written + * as objects or as primitive data. + * + * Not all objects can be written out using an + * <code>ObjectOutputStream</code>. Only those objects that are an + * instance of <code>java.io.Serializable</code> can be written. + * + * Using default serialization, information about the class of an + * object is written, all of the non-transient, non-static fields of + * the object are written, if any of these fields are objects, they are + * written out in the same manner. + * + * An object is only written out the first time it is encountered. If + * the object is encountered later, a reference to it is written to + * the underlying stream. Thus writing circular object graphs + * does not present a problem, nor are relationships between objects + * in a graph lost. + * + * Example usage: + * <pre> + * Hashtable map = new Hashtable (); + * map.put ("one", new Integer (1)); + * map.put ("two", new Integer (2)); + * + * ObjectOutputStream oos = + * new ObjectOutputStream (new FileOutputStream ("numbers")); + * oos.writeObject (map); + * oos.close (); + * + * ObjectInputStream ois = + * new ObjectInputStream (new FileInputStream ("numbers")); + * Hashtable newmap = (Hashtable)ois.readObject (); + * + * System.out.println (newmap); + * </pre> + * + * The default serialization can be overriden in two ways. + * + * By defining a method <code>private void + * writeObject (ObjectOutputStream)</code>, a class can dictate exactly + * how information about itself is written. + * <code>defaultWriteObject ()</code> may be called from this method to + * carry out default serialization. This method is not + * responsible for dealing with fields of super-classes or subclasses. + * + * By implementing <code>java.io.Externalizable</code>. This gives + * the class complete control over the way it is written to the + * stream. If this approach is used the burden of writing superclass + * and subclass data is transfered to the class implementing + * <code>java.io.Externalizable</code>. + * + * @see java.io.DataOutputStream + * @see java.io.Externalizable + * @see java.io.ObjectInputStream + * @see java.io.Serializable + */ +public class ObjectOutputStream extends OutputStream + implements ObjectOutput, ObjectStreamConstants +{ + /** + * Creates a new <code>ObjectOutputStream</code> that will do all of + * its writing onto <code>out</code>. This method also initializes + * the stream by writing the header information (stream magic number + * and stream version). + * + * @exception IOException Writing stream header to underlying + * stream cannot be completed. + * + * @see #writeStreamHeader() + */ + public ObjectOutputStream (OutputStream out) throws IOException + { + realOutput = new DataOutputStream(out); + blockData = new byte[ BUFFER_SIZE ]; + blockDataCount = 0; + blockDataOutput = new DataOutputStream(this); + setBlockDataMode(true); + replacementEnabled = false; + isSerializing = false; + nextOID = baseWireHandle; + OIDLookupTable = new Hashtable(); + protocolVersion = defaultProtocolVersion; + useSubclassMethod = false; + writeStreamHeader(); + + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (val != null && !val.equals("")) + dump = true; + } + } + + /** + * Writes a representation of <code>obj</code> to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * <code>private void writeObject (ObjectOutputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @exception NotSerializableException An attempt was made to + * serialize an <code>Object</code> that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public final void writeObject(Object obj) throws IOException + { + if (useSubclassMethod) + { + if (dump) + dumpElementln ("WRITE OVERRIDE: " + obj); + + writeObjectOverride(obj); + return; + } + + if (dump) + dumpElementln ("WRITE: " + obj); + + depth += 2; + + boolean was_serializing = isSerializing; + boolean old_mode = setBlockDataMode(false); + try + { + isSerializing = true; + boolean replaceDone = false; + Object replacedObject = null; + + while (true) + { + if (obj == null) + { + realOutput.writeByte(TC_NULL); + break; + } + + Integer handle = findHandle(obj); + if (handle != null) + { + realOutput.writeByte(TC_REFERENCE); + realOutput.writeInt(handle.intValue()); + break; + } + + if (obj instanceof Class) + { + Class cl = (Class)obj; + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl); + realOutput.writeByte(TC_CLASS); + if (!osc.isProxyClass) + { + writeObject (osc); + } + else + { + realOutput.writeByte(TC_PROXYCLASSDESC); + Class[] intfs = cl.getInterfaces(); + realOutput.writeInt(intfs.length); + for (int i = 0; i < intfs.length; i++) + realOutput.writeUTF(intfs[i].getName()); + + boolean oldmode = setBlockDataMode(true); + annotateProxyClass(cl); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + + writeObject(osc.getSuper()); + } + assignNewHandle(obj); + break; + } + + if (obj instanceof ObjectStreamClass) + { + writeClassDescriptor((ObjectStreamClass) obj); + break; + } + + Class clazz = obj.getClass(); + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz); + if (osc == null) + throw new NotSerializableException(clazz.getName()); + + if ((replacementEnabled || obj instanceof Serializable) + && ! replaceDone) + { + replacedObject = obj; + + if (obj instanceof Serializable) + { + try + { + Method m = osc.writeReplaceMethod; + if (m != null) + obj = m.invoke(obj, new Object[0]); + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException ignore) + { + } + } + + if (replacementEnabled) + obj = replaceObject(obj); + + replaceDone = true; + continue; + } + + if (obj instanceof String) + { + realOutput.writeByte(TC_STRING); + assignNewHandle(obj); + realOutput.writeUTF((String)obj); + break; + } + + if (clazz.isArray ()) + { + realOutput.writeByte(TC_ARRAY); + writeObject(osc); + assignNewHandle(obj); + writeArraySizeAndElements(obj, clazz.getComponentType()); + break; + } + + realOutput.writeByte(TC_OBJECT); + writeObject(osc); + + if (replaceDone) + assignNewHandle(replacedObject); + else + assignNewHandle(obj); + + if (obj instanceof Externalizable) + { + if (protocolVersion == PROTOCOL_VERSION_2) + setBlockDataMode(true); + + ((Externalizable)obj).writeExternal(this); + + if (protocolVersion == PROTOCOL_VERSION_2) + { + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + } + + break; + } + + if (obj instanceof Serializable) + { + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + currentObject = obj; + ObjectStreamClass[] hierarchy = + ObjectStreamClass.getObjectStreamClasses(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + currentObjectStreamClass = hierarchy[i]; + + fieldsAlreadyWritten = false; + if (currentObjectStreamClass.hasWriteMethod()) + { + if (dump) + dumpElementln ("WRITE METHOD CALLED FOR: " + obj); + setBlockDataMode(true); + callWriteMethod(obj, currentObjectStreamClass); + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + if (dump) + dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj); + } + else + { + if (dump) + dumpElementln ("WRITE FIELDS CALLED FOR: " + obj); + writeFields(obj, currentObjectStreamClass); + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + currentPutField = null; + break; + } + + throw new NotSerializableException(clazz.getName ()); + } // end pseudo-loop + } + catch (ObjectStreamException ose) + { + // Rethrow these are fatal. + throw ose; + } + catch (IOException e) + { + realOutput.writeByte(TC_EXCEPTION); + reset(true); + + setBlockDataMode(false); + try + { + if (DEBUG) + { + e.printStackTrace(System.out); + } + writeObject(e); + } + catch (IOException ioe) + { + StreamCorruptedException ex = + new StreamCorruptedException + (ioe + " thrown while exception was being written to stream."); + if (DEBUG) + { + ex.printStackTrace(System.out); + } + throw ex; + } + + reset (true); + + } + finally + { + isSerializing = was_serializing; + setBlockDataMode(old_mode); + depth -= 2; + + if (dump) + dumpElementln ("END: " + obj); + } + } + + protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException + { + realOutput.writeByte(TC_CLASSDESC); + realOutput.writeUTF(osc.getName()); + realOutput.writeLong(osc.getSerialVersionUID()); + assignNewHandle(osc); + + int flags = osc.getFlags(); + + if (protocolVersion == PROTOCOL_VERSION_2 + && osc.isExternalizable()) + flags |= SC_BLOCK_DATA; + + realOutput.writeByte(flags); + + ObjectStreamField[] fields = osc.fields; + realOutput.writeShort(fields.length); + + ObjectStreamField field; + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + realOutput.writeByte(field.getTypeCode ()); + realOutput.writeUTF(field.getName ()); + + if (! field.isPrimitive()) + writeObject(field.getTypeString()); + } + + boolean oldmode = setBlockDataMode(true); + annotateClass(osc.forClass()); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + + if (osc.isSerializable() || osc.isExternalizable()) + writeObject(osc.getSuper()); + else + writeObject(null); + } + + /** + * Writes the current objects non-transient, non-static fields from + * the current class to the underlying output stream. + * + * This method is intended to be called from within a object's + * <code>private void writeObject (ObjectOutputStream)</code> + * method. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * <code>private void writeObject (ObjectOutputStream)</code> + * method. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + public void defaultWriteObject() + throws IOException, NotActiveException + { + markFieldsWritten(); + writeFields(currentObject, currentObjectStreamClass); + } + + + private void markFieldsWritten() throws IOException + { + if (currentObject == null || currentObjectStreamClass == null) + throw new NotActiveException + ("defaultWriteObject called by non-active class and/or object"); + + if (fieldsAlreadyWritten) + throw new IOException + ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once"); + + fieldsAlreadyWritten = true; + } + + /** + * Resets stream to state equivalent to the state just after it was + * constructed. + * + * Causes all objects previously written to the stream to be + * forgotten. A notification of this reset is also written to the + * underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code> or reset called while serialization is + * in progress. + */ + public void reset() throws IOException + { + reset(false); + } + + + private void reset(boolean internal) throws IOException + { + if (!internal) + { + if (isSerializing) + throw new IOException("Reset called while serialization in progress"); + + realOutput.writeByte(TC_RESET); + } + + clearHandles(); + } + + + /** + * Informs this <code>ObjectOutputStream</code> to write data + * according to the specified protocol. There are currently two + * different protocols, specified by <code>PROTOCOL_VERSION_1</code> + * and <code>PROTOCOL_VERSION_2</code>. This implementation writes + * data using <code>PROTOCOL_VERSION_2</code> by default, as is done + * by the JDK 1.2. + * + * A non-portable method, <code>setDefaultProtocolVersion (int + * version)</code> is provided to change the default protocol + * version. + * + * For an explination of the differences beween the two protocols + * see XXX: the Java ObjectSerialization Specification. + * + * @exception IOException if <code>version</code> is not a valid + * protocol + * + * @see #setDefaultProtocolVersion(int) + */ + public void useProtocolVersion(int version) throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException("Invalid protocol version requested."); + + protocolVersion = version; + } + + + /** + * <em>GNU $classpath specific</em> + * + * Changes the default stream protocol used by all + * <code>ObjectOutputStream</code>s. There are currently two + * different protocols, specified by <code>PROTOCOL_VERSION_1</code> + * and <code>PROTOCOL_VERSION_2</code>. The default default is + * <code>PROTOCOL_VERSION_1</code>. + * + * @exception IOException if <code>version</code> is not a valid + * protocol + * + * @see #useProtocolVersion(int) + */ + public static void setDefaultProtocolVersion(int version) + throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException("Invalid protocol version requested."); + + defaultProtocolVersion = version; + } + + + /** + * An empty hook that allows subclasses to write extra information + * about classes to the stream. This method is called the first + * time each class is seen, and after all of the standard + * information about the class has been written. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass) + */ + protected void annotateClass(Class cl) throws IOException + { + } + + protected void annotateProxyClass(Class cl) throws IOException + { + } + + /** + * Allows subclasses to replace objects that are written to the + * stream with other objects to be written in their place. This + * method is called the first time each object is encountered + * (modulo reseting of the stream). + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * + * @see #enableReplaceObject(boolean) + */ + protected Object replaceObject(Object obj) throws IOException + { + return obj; + } + + + /** + * If <code>enable</code> is <code>true</code> and this object is + * trusted, then <code>replaceObject (Object)</code> will be called + * in subsequent calls to <code>writeObject (Object)</code>. + * Otherwise, <code>replaceObject (Object)</code> will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableReplaceObject(boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = replacementEnabled; + replacementEnabled = enable; + return old_val; + } + + + /** + * Writes stream magic and stream version information to the + * underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + protected void writeStreamHeader() throws IOException + { + realOutput.writeShort(STREAM_MAGIC); + realOutput.writeShort(STREAM_VERSION); + } + + /** + * Protected constructor that allows subclasses to override + * serialization. This constructor should be called by subclasses + * that wish to override <code>writeObject (Object)</code>. This + * method does a security check <i>NOTE: currently not + * implemented</i>, then sets a flag that informs + * <code>writeObject (Object)</code> to call the subclasses + * <code>writeObjectOverride (Object)</code> method. + * + * @see #writeObjectOverride(Object) + */ + protected ObjectOutputStream() throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + useSubclassMethod = true; + } + + + /** + * This method allows subclasses to override the default + * serialization mechanism provided by + * <code>ObjectOutputStream</code>. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from there constructor. + * + * @see #ObjectOutputStream() + * + * @exception NotActiveException Subclass has arranged for this + * method to be called, but did not implement this method. + */ + protected void writeObjectOverride(Object obj) throws NotActiveException, + IOException + { + throw new NotActiveException + ("Subclass of ObjectOutputStream must implement writeObjectOverride"); + } + + + /** + * @see DataOutputStream#write(int) + */ + public void write (int data) throws IOException + { + if (writeDataAsBlocks) + { + if (blockDataCount == BUFFER_SIZE) + drain(); + + blockData[ blockDataCount++ ] = (byte)data; + } + else + realOutput.write(data); + } + + + /** + * @see DataOutputStream#write(byte[]) + */ + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } + + + /** + * @see DataOutputStream#write(byte[],int,int) + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (writeDataAsBlocks) + { + if (len < 0) + throw new IndexOutOfBoundsException(); + + if (blockDataCount + len < BUFFER_SIZE) + { + System.arraycopy(b, off, blockData, blockDataCount, len); + blockDataCount += len; + } + else + { + drain(); + writeBlockDataHeader(len); + realOutput.write(b, off, len); + } + } + else + realOutput.write(b, off, len); + } + + + /** + * @see DataOutputStream#flush() + */ + public void flush () throws IOException + { + drain(); + realOutput.flush(); + } + + + /** + * Causes the block-data buffer to be written to the underlying + * stream, but does not flush underlying stream. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + */ + protected void drain() throws IOException + { + if (blockDataCount == 0) + return; + + if (writeDataAsBlocks) + writeBlockDataHeader(blockDataCount); + realOutput.write(blockData, 0, blockDataCount); + blockDataCount = 0; + } + + + /** + * @see java.io.DataOutputStream#close () + */ + public void close() throws IOException + { + flush(); + realOutput.close(); + } + + + /** + * @see java.io.DataOutputStream#writeBoolean (boolean) + */ + public void writeBoolean(boolean data) throws IOException + { + blockDataOutput.writeBoolean(data); + } + + + /** + * @see java.io.DataOutputStream#writeByte (int) + */ + public void writeByte(int data) throws IOException + { + blockDataOutput.writeByte(data); + } + + + /** + * @see java.io.DataOutputStream#writeShort (int) + */ + public void writeShort (int data) throws IOException + { + blockDataOutput.writeShort(data); + } + + + /** + * @see java.io.DataOutputStream#writeChar (int) + */ + public void writeChar(int data) throws IOException + { + blockDataOutput.writeChar(data); + } + + + /** + * @see java.io.DataOutputStream#writeInt (int) + */ + public void writeInt(int data) throws IOException + { + blockDataOutput.writeInt(data); + } + + + /** + * @see java.io.DataOutputStream#writeLong (long) + */ + public void writeLong(long data) throws IOException + { + blockDataOutput.writeLong(data); + } + + + /** + * @see java.io.DataOutputStream#writeFloat (float) + */ + public void writeFloat(float data) throws IOException + { + blockDataOutput.writeFloat(data); + } + + + /** + * @see java.io.DataOutputStream#writeDouble (double) + */ + public void writeDouble(double data) throws IOException + { + blockDataOutput.writeDouble(data); + } + + + /** + * @see java.io.DataOutputStream#writeBytes (java.lang.String) + */ + public void writeBytes(String data) throws IOException + { + blockDataOutput.writeBytes(data); + } + + + /** + * @see java.io.DataOutputStream#writeChars (java.lang.String) + */ + public void writeChars(String data) throws IOException + { + dataOutput.writeChars(data); + } + + + /** + * @see java.io.DataOutputStream#writeUTF (java.lang.String) + */ + public void writeUTF(String data) throws IOException + { + dataOutput.writeUTF(data); + } + + + /** + * This class allows a class to specify exactly which fields should + * be written, and what values should be written for these fields. + * + * XXX: finish up comments + */ + public abstract static class PutField + { + public abstract void put (String name, boolean value); + public abstract void put (String name, byte value); + public abstract void put (String name, char value); + public abstract void put (String name, double value); + public abstract void put (String name, float value); + public abstract void put (String name, int value); + public abstract void put (String name, long value); + public abstract void put (String name, short value); + public abstract void put (String name, Object value); + + /** + * @deprecated + */ + public abstract void write (ObjectOutput out) throws IOException; + } + + public PutField putFields() throws IOException + { + if (currentPutField != null) + return currentPutField; + + currentPutField = new PutField() + { + private byte[] prim_field_data + = new byte[currentObjectStreamClass.primFieldSize]; + private Object[] objs + = new Object[currentObjectStreamClass.objectFieldCount]; + + private ObjectStreamField getField (String name) + { + ObjectStreamField field + = currentObjectStreamClass.getField(name); + + if (field == null) + throw new IllegalArgumentException("no such serializable field " + name); + + return field; + } + + public void put(String name, boolean value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'Z'); + prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); + } + + public void put(String name, byte value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'B'); + prim_field_data[field.getOffset()] = value; + } + + public void put(String name, char value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'C'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, double value) + { + ObjectStreamField field = getField (name); + + checkType(field, 'D'); + int off = field.getOffset(); + long l_value = Double.doubleToLongBits (value); + prim_field_data[off++] = (byte)(l_value >>> 52); + prim_field_data[off++] = (byte)(l_value >>> 48); + prim_field_data[off++] = (byte)(l_value >>> 40); + prim_field_data[off++] = (byte)(l_value >>> 32); + prim_field_data[off++] = (byte)(l_value >>> 24); + prim_field_data[off++] = (byte)(l_value >>> 16); + prim_field_data[off++] = (byte)(l_value >>> 8); + prim_field_data[off] = (byte)l_value; + } + + public void put(String name, float value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'F'); + int off = field.getOffset(); + int i_value = Float.floatToIntBits(value); + prim_field_data[off++] = (byte)(i_value >>> 24); + prim_field_data[off++] = (byte)(i_value >>> 16); + prim_field_data[off++] = (byte)(i_value >>> 8); + prim_field_data[off] = (byte)i_value; + } + + public void put(String name, int value) + { + ObjectStreamField field = getField(name); + checkType(field, 'I'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, long value) + { + ObjectStreamField field = getField(name); + checkType(field, 'J'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 52); + prim_field_data[off++] = (byte)(value >>> 48); + prim_field_data[off++] = (byte)(value >>> 40); + prim_field_data[off++] = (byte)(value >>> 32); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, short value) + { + ObjectStreamField field = getField(name); + checkType(field, 'S'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, Object value) + { + ObjectStreamField field = getField(name); + + if (value != null && + ! field.getType().isAssignableFrom(value.getClass ())) + throw new IllegalArgumentException("Class " + value.getClass() + + " cannot be cast to " + field.getType()); + objs[field.getOffset()] = value; + } + + public void write(ObjectOutput out) throws IOException + { + // Apparently Block data is not used with PutField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + out.write(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + out.writeObject(objs[i]); + setBlockDataMode(oldmode); + } + + private void checkType(ObjectStreamField field, char type) + throws IllegalArgumentException + { + if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0) + != type) + throw new IllegalArgumentException(); + } + }; + // end PutFieldImpl + + return currentPutField; + } + + + public void writeFields() throws IOException + { + if (currentPutField == null) + throw new NotActiveException("writeFields can only be called after putFields has been called"); + + markFieldsWritten(); + currentPutField.write(this); + } + + + // write out the block-data buffer, picking the correct header + // depending on the size of the buffer + private void writeBlockDataHeader(int size) throws IOException + { + if (size < 256) + { + realOutput.writeByte(TC_BLOCKDATA); + realOutput.write(size); + } + else + { + realOutput.writeByte(TC_BLOCKDATALONG); + realOutput.writeInt(size); + } + } + + + // lookup the handle for OBJ, return null if OBJ doesn't have a + // handle yet + private Integer findHandle(Object obj) + { + return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj)); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle(Object obj) + { + OIDLookupTable.put(new ObjectIdentityWrapper(obj), + new Integer(nextOID)); + return nextOID++; + } + + + // resets mapping from objects to handles + private void clearHandles() + { + nextOID = baseWireHandle; + OIDLookupTable.clear(); + } + + + // write out array size followed by each element of the array + private void writeArraySizeAndElements(Object array, Class clazz) + throws IOException + { + int length = Array.getLength(array); + + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeBoolean(cast_array[i]); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + realOutput.writeInt(length); + realOutput.write(cast_array, 0, length); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeChar(cast_array[i]); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeDouble(cast_array[i]); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeFloat(cast_array[i]); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeInt(cast_array[i]); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeLong(cast_array[i]); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeShort(cast_array[i]); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + writeObject(cast_array[i]); + } + } + + + // writes out FIELDS of OBJECT for the specified ObjectStreamClass. + // FIELDS are already in canonical order. + private void writeFields(Object obj, ObjectStreamClass osc) + throws IOException + { + ObjectStreamField[] fields = osc.fields; + boolean oldmode = setBlockDataMode(false); + String field_name; + Class type; + + for (int i = 0; i < fields.length; i++) + { + field_name = fields[i].getName(); + type = fields[i].getType(); + + if (dump) + dumpElementln ("WRITE FIELD: " + field_name + " type=" + type); + + if (type == Boolean.TYPE) + realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name)); + else if (type == Byte.TYPE) + realOutput.writeByte(getByteField(obj, osc.forClass(), field_name)); + else if (type == Character.TYPE) + realOutput.writeChar(getCharField(obj, osc.forClass(), field_name)); + else if (type == Double.TYPE) + realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name)); + else if (type == Float.TYPE) + realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name)); + else if (type == Integer.TYPE) + realOutput.writeInt(getIntField(obj, osc.forClass(), field_name)); + else if (type == Long.TYPE) + realOutput.writeLong(getLongField(obj, osc.forClass(), field_name)); + else if (type == Short.TYPE) + realOutput.writeShort(getShortField(obj, osc.forClass(), field_name)); + else + writeObject(getObjectField(obj, osc.forClass(), field_name, + fields[i].getTypeString ())); + } + setBlockDataMode(oldmode); + } + + + // Toggles writing primitive data to block-data buffer. + // Package-private to avoid a trampoline constructor. + boolean setBlockDataMode(boolean on) throws IOException + { + if (on == writeDataAsBlocks) + return on; + + drain(); + boolean oldmode = writeDataAsBlocks; + writeDataAsBlocks = on; + + if (on) + dataOutput = blockDataOutput; + else + dataOutput = realOutput; + + return oldmode; + } + + + private void callWriteMethod(Object obj, ObjectStreamClass osc) + throws IOException + { + currentPutField = null; + try + { + Object args[] = {this}; + osc.writeObjectMethod.invoke(obj, args); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + + IOException ioe + = new IOException("Exception thrown from writeObject() on " + + osc.forClass().getName() + ": " + + exception.getClass().getName()); + ioe.initCause(exception); + throw ioe; + } + catch (Exception x) + { + IOException ioe + = new IOException("Failure invoking writeObject() on " + + osc.forClass().getName() + ": " + + x.getClass().getName()); + ioe.initCause(x); + throw ioe; + } + } + + private boolean getBooleanField(Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField(klass, field_name); + boolean b = f.getBoolean(obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private byte getByteField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + byte b = f.getByte (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private char getCharField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + char b = f.getChar (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private double getDoubleField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + double b = f.getDouble (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private float getFloatField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + float b = f.getFloat (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private int getIntField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + int b = f.getInt (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private long getLongField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + long b = f.getLong (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private short getShortField (Object obj, Class klass, String field_name) + throws IOException + { + try + { + Field f = getField (klass, field_name); + short b = f.getShort (obj); + return b; + } + catch (IllegalArgumentException _) + { + throw new InvalidClassException + ("invalid requested type for field " + field_name + " in class " + klass.getName()); + } + catch (IOException e) + { + throw e; + } + catch (Exception _) + { + throw new IOException("Unexpected exception " + _); + } + } + + private Object getObjectField (Object obj, Class klass, String field_name, + String type_code) throws IOException + { + try + { + Field f = getField (klass, field_name); + ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType()); + + /* if of is primitive something went wrong + * in the check for primitive classes in writeFields. + */ + if (of.isPrimitive()) + throw new InvalidClassException + ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive"); + + if (!of.getTypeString().equals(type_code)) + throw new InvalidClassException + ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code); + + Object o = f.get (obj); + // FIXME: We should check the type_code here + return o; + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new IOException (); + } + } + + private Field getField (Class klass, String name) + throws java.io.InvalidClassException + { + try + { + final Field f = klass.getDeclaredField(name); + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + return f; + } + catch (java.lang.NoSuchFieldException e) + { + throw new InvalidClassException + ("no field called " + name + " in class " + klass.getName()); + } + } + + private void dumpElementln (String msg) + { + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + System.out.println(msg); + } + + // this value comes from 1.2 spec, but is used in 1.1 as well + private static final int BUFFER_SIZE = 1024; + + private static int defaultProtocolVersion = PROTOCOL_VERSION_2; + + private DataOutputStream dataOutput; + private boolean writeDataAsBlocks; + private DataOutputStream realOutput; + private DataOutputStream blockDataOutput; + private byte[] blockData; + private int blockDataCount; + private Object currentObject; + // Package-private to avoid a trampoline. + ObjectStreamClass currentObjectStreamClass; + private PutField currentPutField; + private boolean fieldsAlreadyWritten; + private boolean replacementEnabled; + private boolean isSerializing; + private int nextOID; + private Hashtable OIDLookupTable; + private int protocolVersion; + private boolean useSubclassMethod; + private SetAccessibleAction setAccessible = new SetAccessibleAction(); + + // The nesting depth for debugging output + private int depth = 0; + + // Set if we're generating debugging dumps + private boolean dump = false; + + private static final boolean DEBUG = false; + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javaio"); + } + } +} diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java new file mode 100644 index 00000000000..b7bd1271324 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamClass.java @@ -0,0 +1,976 @@ +/* ObjectStreamClass.java -- Class used to write class information + about serialized objects. + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.io.NullOutputStream; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; +import gnu.java.security.provider.Gnu; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.security.Security; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Vector; + +public class ObjectStreamClass implements Serializable +{ + /** + * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. + * If <code>cl</code> is null, or is not <code>Serializable</code>, + * null is returned. <code>ObjectStreamClass</code>'s are memorized; + * later calls to this method with the same class will return the + * same <code>ObjectStreamClass</code> object and no recalculation + * will be done. + * + * @see java.io.Serializable + */ + public static ObjectStreamClass lookup(Class cl) + { + if (cl == null) + return null; + if (! (Serializable.class).isAssignableFrom(cl)) + return null; + + return lookupForClassObject(cl); + } + + /** + * This lookup for internal use by ObjectOutputStream. Suppose + * we have a java.lang.Class object C for class A, though A is not + * serializable, but it's okay to serialize C. + */ + static ObjectStreamClass lookupForClassObject(Class cl) + { + if (cl == null) + return null; + + ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl); + + if (osc != null) + return osc; + else + { + osc = new ObjectStreamClass(cl); + classLookupTable.put(cl, osc); + return osc; + } + } + + /** + * Returns the name of the class that this + * <code>ObjectStreamClass</code> represents. + * + * @return the name of the class. + */ + public String getName() + { + return name; + } + + /** + * Returns the class that this <code>ObjectStreamClass</code> + * represents. Null could be returned if this + * <code>ObjectStreamClass</code> was read from an + * <code>ObjectInputStream</code> and the class it represents cannot + * be found or loaded. + * + * @see java.io.ObjectInputStream + */ + public Class forClass() + { + return clazz; + } + + /** + * Returns the serial version stream-unique identifier for the class + * represented by this <code>ObjectStreamClass</code>. This SUID is + * either defined by the class as <code>static final long + * serialVersionUID</code> or is calculated as specified in + * Javasoft's "Object Serialization Specification" XXX: add reference + * + * @return the serial version UID. + */ + public long getSerialVersionUID() + { + return uid; + } + + /** + * Returns the serializable (non-static and non-transient) Fields + * of the class represented by this ObjectStreamClass. The Fields + * are sorted by name. + * + * @return the fields. + */ + public ObjectStreamField[] getFields() + { + ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; + System.arraycopy(fields, 0, copy, 0, fields.length); + return copy; + } + + // XXX doc + // Can't do binary search since fields is sorted by name and + // primitiveness. + public ObjectStreamField getField (String name) + { + for (int i = 0; i < fields.length; i++) + if (fields[i].getName().equals(name)) + return fields[i]; + return null; + } + + /** + * Returns a textual representation of this + * <code>ObjectStreamClass</code> object including the name of the + * class it represents as well as that class's serial version + * stream-unique identifier. + * + * @see #getSerialVersionUID() + * @see #getName() + */ + public String toString() + { + return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; + } + + // Returns true iff the class that this ObjectStreamClass represents + // has the following method: + // + // private void writeObject (ObjectOutputStream) + // + // This method is used by the class to override default + // serialization behavior. + boolean hasWriteMethod() + { + return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; + } + + // Returns true iff the class that this ObjectStreamClass represents + // implements Serializable but does *not* implement Externalizable. + boolean isSerializable() + { + return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isExternalizable() + { + return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + } + + + // Returns the <code>ObjectStreamClass</code> that represents the + // class that is the superclass of the class this + // <code>ObjectStreamClass</code> represents. If the superclass is + // not Serializable, null is returned. + ObjectStreamClass getSuper() + { + return superClass; + } + + + // returns an array of ObjectStreamClasses that represent the super + // classes of CLAZZ and CLAZZ itself in order from most super to + // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ + // that is serializable. + static ObjectStreamClass[] getObjectStreamClasses(Class clazz) + { + ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); + + if (osc == null) + return new ObjectStreamClass[0]; + else + { + Vector oscs = new Vector(); + + while (osc != null) + { + oscs.addElement (osc); + osc = osc.getSuper(); + } + + int count = oscs.size(); + ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; + + for (int i = count - 1; i >= 0; i--) + sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); + + return sorted_oscs; + } + } + + + // Returns an integer that consists of bit-flags that indicate + // properties of the class represented by this ObjectStreamClass. + // The bit-flags that could be present are those defined in + // ObjectStreamConstants that begin with `SC_' + int getFlags() + { + return flags; + } + + + ObjectStreamClass(String name, long uid, byte flags, + ObjectStreamField[] fields) + { + this.name = name; + this.uid = uid; + this.flags = flags; + this.fields = fields; + } + + /** + * This method builds the internal description corresponding to a Java Class. + * As the constructor only assign a name to the current ObjectStreamClass instance, + * that method sets the serial UID, chose the fields which will be serialized, + * and compute the position of the fields in the serialized stream. + * + * @param cl The Java class which is used as a reference for building the descriptor. + * @param superClass The descriptor of the super class for this class descriptor. + * @throws InvalidClassException if an incompatibility between computed UID and + * already set UID is found. + */ + void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException + { + this.clazz = cl; + + cacheMethods(); + + long class_uid = getClassUID(cl); + if (uid == 0) + uid = class_uid; + else + { + // Check that the actual UID of the resolved class matches the UID from + // the stream. + if (uid != class_uid) + { + String msg = cl + + ": Local class not compatible: stream serialVersionUID=" + + uid + ", local serialVersionUID=" + class_uid; + throw new InvalidClassException (msg); + } + } + + isProxyClass = clazz != null && Proxy.isProxyClass(clazz); + this.superClass = superClass; + calculateOffsets(); + + try + { + ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); + + if (exportedFields == null) + return; + + ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; + int i, j, k; + + /* We now check the import fields against the exported fields. + * There should not be contradiction (e.g. int x and String x) + * but extra virtual fields can be added to the class. + */ + + Arrays.sort(exportedFields); + + i = 0; j = 0; k = 0; + while (i < fields.length && j < exportedFields.length) + { + int comp = fields[i].compareTo(exportedFields[j]); + + if (comp < 0) + { + newFieldList[k] = fields[i]; + fields[i].setPersistent(false); + fields[i].setToSet(false); + i++; + } + else if (comp > 0) + { + /* field not found in imported fields. We add it + * in the list of supported fields. + */ + newFieldList[k] = exportedFields[j]; + newFieldList[k].setPersistent(true); + newFieldList[k].setToSet(false); + try + { + newFieldList[k].lookupField(clazz); + newFieldList[k].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + j++; + } + else + { + try + { + exportedFields[j].lookupField(clazz); + exportedFields[j].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + + if (!fields[i].getType().equals(exportedFields[j].getType())) + throw new InvalidClassException + ("serialPersistentFields must be compatible with" + + " imported fields (about " + fields[i].getName() + ")"); + newFieldList[k] = fields[i]; + fields[i].setPersistent(true); + i++; + j++; + } + k++; + } + + if (i < fields.length) + for (;i<fields.length;i++,k++) + { + fields[i].setPersistent(false); + fields[i].setToSet(false); + newFieldList[k] = fields[i]; + } + else + if (j < exportedFields.length) + for (;j<exportedFields.length;j++,k++) + { + exportedFields[j].setPersistent(true); + exportedFields[j].setToSet(false); + newFieldList[k] = exportedFields[j]; + } + + fields = new ObjectStreamField[k]; + System.arraycopy(newFieldList, 0, fields, 0, k); + } + catch (NoSuchFieldException ignore) + { + return; + } + catch (IllegalAccessException ignore) + { + return; + } + } + + void setSuperclass (ObjectStreamClass osc) + { + superClass = osc; + } + + void calculateOffsets() + { + int i; + ObjectStreamField field; + primFieldSize = 0; + int fcount = fields.length; + for (i = 0; i < fcount; ++ i) + { + field = fields[i]; + + if (! field.isPrimitive()) + break; + + field.setOffset(primFieldSize); + switch (field.getTypeCode()) + { + case 'B': + case 'Z': + ++ primFieldSize; + break; + case 'C': + case 'S': + primFieldSize += 2; + break; + case 'I': + case 'F': + primFieldSize += 4; + break; + case 'D': + case 'J': + primFieldSize += 8; + break; + } + } + + for (objectFieldCount = 0; i < fcount; ++ i) + fields[i].setOffset(objectFieldCount++); + } + + private Method findMethod(Method[] methods, String name, Class[] params, + Class returnType, boolean mustBePrivate) + { +outer: + for (int i = 0; i < methods.length; i++) + { + final Method m = methods[i]; + int mods = m.getModifiers(); + if (Modifier.isStatic(mods) + || (mustBePrivate && !Modifier.isPrivate(mods))) + { + continue; + } + + if (m.getName().equals(name) + && m.getReturnType() == returnType) + { + Class[] mp = m.getParameterTypes(); + if (mp.length == params.length) + { + for (int j = 0; j < mp.length; j++) + { + if (mp[j] != params[j]) + { + continue outer; + } + } + AccessController.doPrivileged(new SetAccessibleAction(m)); + return m; + } + } + } + return null; + } + + private static boolean inSamePackage(Class c1, Class c2) + { + String name1 = c1.getName(); + String name2 = c2.getName(); + + int id1 = name1.lastIndexOf('.'); + int id2 = name2.lastIndexOf('.'); + + // Handle the default package + if (id1 == -1 || id2 == -1) + return id1 == id2; + + String package1 = name1.substring(0, id1); + String package2 = name2.substring(0, id2); + + return package1.equals(package2); + } + + final static Class[] noArgs = new Class[0]; + + private static Method findAccessibleMethod(String name, Class from) + { + for (Class c = from; c != null; c = c.getSuperclass()) + { + try + { + Method res = c.getDeclaredMethod(name, noArgs); + int mods = res.getModifiers(); + + if (c != from + && (Modifier.isPrivate(mods) + || ! Modifier.isPublic(mods) && ! inSamePackage(c, from))) + continue; + + AccessController.doPrivileged(new SetAccessibleAction(res)); + return res; + } + catch (NoSuchMethodException e) + { + } + } + + return null; + } + + private void cacheMethods() + { + Method[] methods = forClass().getDeclaredMethods(); + + readObjectMethod = findMethod(methods, "readObject", + new Class[] { ObjectInputStream.class }, + Void.TYPE, true); + writeObjectMethod = findMethod(methods, "writeObject", + new Class[] { ObjectOutputStream.class }, + Void.TYPE, true); + + // readResolve and writeReplace can be in parent classes, as long as they + // are accessible from this class. + readResolveMethod = findAccessibleMethod("readResolve", forClass()); + writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); + } + + private ObjectStreamClass(Class cl) + { + uid = 0; + flags = 0; + isProxyClass = Proxy.isProxyClass(cl); + + clazz = cl; + cacheMethods(); + name = cl.getName(); + setFlags(cl); + setFields(cl); + // to those class nonserializable, its uid field is 0 + if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) + uid = getClassUID(cl); + superClass = lookup(cl.getSuperclass()); + } + + + // Sets bits in flags according to features of CL. + private void setFlags(Class cl) + { + if ((java.io.Externalizable.class).isAssignableFrom(cl)) + flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + else if ((java.io.Serializable.class).isAssignableFrom(cl)) + // only set this bit if CL is NOT Externalizable + flags |= ObjectStreamConstants.SC_SERIALIZABLE; + + if (writeObjectMethod != null) + flags |= ObjectStreamConstants.SC_WRITE_METHOD; + } + + + // Sets fields to be a sorted array of the serializable fields of + // clazz. + private void setFields(Class cl) + { + SetAccessibleAction setAccessible = new SetAccessibleAction(); + + if (!isSerializable() || isExternalizable()) + { + fields = NO_FIELDS; + return; + } + + try + { + final Field f = + cl.getDeclaredField("serialPersistentFields"); + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + int modifiers = f.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && Modifier.isPrivate(modifiers)) + { + fields = getSerialPersistentFields(cl); + if (fields != null) + { + Arrays.sort (fields); + // Retrieve field reference. + for (int i=0; i < fields.length; i++) + { + try + { + fields[i].lookupField(cl); + } + catch (NoSuchFieldException _) + { + fields[i].setToSet(false); + } + } + + calculateOffsets(); + return; + } + } + } + catch (NoSuchFieldException ignore) + { + } + catch (IllegalAccessException ignore) + { + } + + int num_good_fields = 0; + Field[] all_fields = cl.getDeclaredFields(); + + int modifiers; + // set non-serializable fields to null in all_fields + for (int i = 0; i < all_fields.length; i++) + { + modifiers = all_fields[i].getModifiers(); + if (Modifier.isTransient(modifiers) + || Modifier.isStatic(modifiers)) + all_fields[i] = null; + else + num_good_fields++; + } + + // make a copy of serializable (non-null) fields + fields = new ObjectStreamField[ num_good_fields ]; + for (int from = 0, to = 0; from < all_fields.length; from++) + if (all_fields[from] != null) + { + final Field f = all_fields[from]; + setAccessible.setMember(f); + AccessController.doPrivileged(setAccessible); + fields[to] = new ObjectStreamField(all_fields[from]); + to++; + } + + Arrays.sort(fields); + // Make sure we don't have any duplicate field names + // (Sun JDK 1.4.1. throws an Internal Error as well) + for (int i = 1; i < fields.length; i++) + { + if(fields[i - 1].getName().equals(fields[i].getName())) + throw new InternalError("Duplicate field " + + fields[i].getName() + " in class " + cl.getName()); + } + calculateOffsets(); + } + + // Returns the serial version UID defined by class, or if that + // isn't present, calculates value of serial version UID. + private long getClassUID(Class cl) + { + try + { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + final Field suid = cl.getDeclaredField("serialVersionUID"); + SetAccessibleAction setAccessible = new SetAccessibleAction(suid); + AccessController.doPrivileged(setAccessible); + int modifiers = suid.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && suid.getType() == Long.TYPE) + return suid.getLong(null); + } + catch (NoSuchFieldException ignore) + { + } + catch (IllegalAccessException ignore) + { + } + + // cl didn't define serialVersionUID, so we have to compute it + try + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); + } + catch (NoSuchAlgorithmException e) + { + // If a provider already provides SHA, use it; otherwise, use this. + Gnu gnuProvider = new Gnu(); + Security.addProvider(gnuProvider); + md = MessageDigest.getInstance("SHA"); + } + + DigestOutputStream digest_out = + new DigestOutputStream(nullOutputStream, md); + DataOutputStream data_out = new DataOutputStream(digest_out); + + data_out.writeUTF(cl.getName()); + + int modifiers = cl.getModifiers(); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt(modifiers); + + // Pretend that an array has no interfaces, because when array + // serialization was defined (JDK 1.1), arrays didn't have it. + if (! cl.isArray()) + { + Class[] interfaces = cl.getInterfaces(); + Arrays.sort(interfaces, interfaceComparator); + for (int i = 0; i < interfaces.length; i++) + data_out.writeUTF(interfaces[i].getName()); + } + + Field field; + Field[] fields = cl.getDeclaredFields(); + Arrays.sort(fields, memberComparator); + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers(); + if (Modifier.isPrivate(modifiers) + && (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers))) + continue; + + data_out.writeUTF(field.getName()); + data_out.writeInt(modifiers); + data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); + } + + // write class initializer method if present + if (VMObjectStreamClass.hasClassInitializer(cl)) + { + data_out.writeUTF("<clinit>"); + data_out.writeInt(Modifier.STATIC); + data_out.writeUTF("()V"); + } + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors(); + Arrays.sort (constructors, memberComparator); + for (int i = 0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF("<init>"); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods(); + Arrays.sort(methods, memberComparator); + for (int i = 0; i < methods.length; i++) + { + method = methods[i]; + modifiers = method.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF(method.getName()); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); + } + + data_out.close(); + byte[] sha = md.digest(); + long result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i = 0; i < len; i++) + result += (long) (sha[i] & 0xFF) << (8 * i); + + return result; + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException + ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName(), e); + } + catch (IOException ioe) + { + throw new RuntimeException(ioe); + } + } + + /** + * Returns the value of CLAZZ's private static final field named + * `serialPersistentFields'. It performs some sanity checks before + * returning the real array. Besides, the returned array is a clean + * copy of the original. So it can be modified. + * + * @param clazz Class to retrieve 'serialPersistentFields' from. + * @return The content of 'serialPersistentFields'. + */ + private ObjectStreamField[] getSerialPersistentFields(Class clazz) + throws NoSuchFieldException, IllegalAccessException + { + ObjectStreamField[] fieldsArray = null; + ObjectStreamField[] o; + + // Use getDeclaredField rather than getField for the same reason + // as above in getDefinedSUID. + Field f = clazz.getDeclaredField("serialPersistentFields"); + f.setAccessible(true); + + int modifiers = f.getModifiers(); + if (!(Modifier.isStatic(modifiers) && + Modifier.isFinal(modifiers) && + Modifier.isPrivate(modifiers))) + return null; + + o = (ObjectStreamField[]) f.get(null); + + if (o == null) + return null; + + fieldsArray = new ObjectStreamField[ o.length ]; + System.arraycopy(o, 0, fieldsArray, 0, o.length); + + return fieldsArray; + } + + /** + * Returns a new instance of the Class this ObjectStreamClass corresponds + * to. + * Note that this should only be used for Externalizable classes. + * + * @return A new instance. + */ + Externalizable newInstance() throws InvalidClassException + { + synchronized(this) + { + if (constructor == null) + { + try + { + final Constructor c = clazz.getConstructor(new Class[0]); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + c.setAccessible(true); + return null; + } + }); + + constructor = c; + } + catch(NoSuchMethodException x) + { + throw new InvalidClassException(clazz.getName(), + "No public zero-argument constructor"); + } + } + } + + try + { + return (Externalizable)constructor.newInstance(null); + } + catch(Exception x) + { + throw (InvalidClassException) + new InvalidClassException(clazz.getName(), + "Unable to instantiate").initCause(x); + } + } + + public static final ObjectStreamField[] NO_FIELDS = {}; + + private static Hashtable classLookupTable = new Hashtable(); + private static final NullOutputStream nullOutputStream = new NullOutputStream(); + private static final Comparator interfaceComparator = new InterfaceComparator(); + private static final Comparator memberComparator = new MemberComparator(); + private static final + Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; + + private ObjectStreamClass superClass; + private Class clazz; + private String name; + private long uid; + private byte flags; + + // this field is package protected so that ObjectInputStream and + // ObjectOutputStream can access it directly + ObjectStreamField[] fields; + + // these are accessed by ObjectIn/OutputStream + int primFieldSize = -1; // -1 if not yet calculated + int objectFieldCount; + + Method readObjectMethod; + Method readResolveMethod; + Method writeReplaceMethod; + Method writeObjectMethod; + boolean realClassIsSerializable; + boolean realClassIsExternalizable; + ObjectStreamField[] fieldMapping; + Constructor firstNonSerializableParentConstructor; + private Constructor constructor; // default constructor for Externalizable + + boolean isProxyClass = false; + + // This is probably not necessary because this class is special cased already + // but it will avoid showing up as a discrepancy when comparing SUIDs. + private static final long serialVersionUID = -6120832682080437368L; + + + // interfaces are compared only by name + private static final class InterfaceComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + return ((Class) o1).getName().compareTo(((Class) o2).getName()); + } + } + + + // Members (Methods and Constructors) are compared first by name, + // conflicts are resolved by comparing type signatures + private static final class MemberComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + Member m1 = (Member) o1; + Member m2 = (Member) o2; + + int comp = m1.getName().compareTo(m2.getName()); + + if (comp == 0) + return TypeSignature.getEncodingOfMember(m1). + compareTo(TypeSignature.getEncodingOfMember(m2)); + else + return comp; + } + } +} diff --git a/libjava/classpath/java/io/ObjectStreamConstants.java b/libjava/classpath/java/io/ObjectStreamConstants.java new file mode 100644 index 00000000000..f1a4af724e7 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamConstants.java @@ -0,0 +1,89 @@ +/* ObjectStreamConstants.java -- Interface containing constant values + used in reading and writing serialized objects + Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface contains constants that are used in object + * serialization. This interface is used by <code>ObjectOutputStream</code>, + * <code>ObjectInputStream</code>, and <code>ObjectStreamClass</code>. + * The values for these constants are specified by the Java library + * specification. + */ +public interface ObjectStreamConstants +{ + // FIXME: Javadoc comment these values. + int PROTOCOL_VERSION_1 = 1; + int PROTOCOL_VERSION_2 = 2; + + short STREAM_MAGIC = (short)0xaced; + short STREAM_VERSION = 5; + + byte TC_NULL = (byte)112; //0x70 + byte TC_REFERENCE = (byte)113; //0x71 + byte TC_CLASSDESC = (byte)114; //0x72 + byte TC_OBJECT = (byte)115; //0x73 + byte TC_STRING = (byte)116; //0x74 + byte TC_ARRAY = (byte)117; //0x75 + byte TC_CLASS = (byte)118; //0x76 + byte TC_BLOCKDATA = (byte)119; //0x77 + byte TC_ENDBLOCKDATA = (byte)120; //0x78 + byte TC_RESET = (byte)121; //0x79 + byte TC_BLOCKDATALONG = (byte)122; //0x7A + byte TC_EXCEPTION = (byte)123; //0x7B + byte TC_LONGSTRING = (byte)124; //0x7C + byte TC_PROXYCLASSDESC = (byte)125; //0x7D + + byte TC_BASE = TC_NULL; + byte TC_MAX = TC_PROXYCLASSDESC; + + int baseWireHandle = 0x7e0000; + + byte SC_WRITE_METHOD = 0x01; + byte SC_SERIALIZABLE = 0x02; + byte SC_EXTERNALIZABLE = 0x04; + byte SC_BLOCK_DATA = 0x08; + + SerializablePermission SUBSTITUTION_PERMISSION + = new SerializablePermission("enableSubstitution"); + + SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION + = new SerializablePermission("enableSubclassImplementation"); +} + diff --git a/libjava/classpath/java/io/ObjectStreamException.java b/libjava/classpath/java/io/ObjectStreamException.java new file mode 100644 index 00000000000..61d4dd09e52 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamException.java @@ -0,0 +1,74 @@ +/* ObjectStreamException.java -- Superclass of all serialization exceptions + Copyright (C) 1998, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs during serialization. + * There are more specific subclasses that give more fine grained + * indications of the precise failure. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ObjectStreamException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7260898174833392607L; + + /** + * Create an exception without a descriptive error message. + */ + protected ObjectStreamException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + protected ObjectStreamException(String message) + { + super(message); + } +} // class ObjectStreamException diff --git a/libjava/classpath/java/io/ObjectStreamField.java b/libjava/classpath/java/io/ObjectStreamField.java new file mode 100644 index 00000000000..611457b3cfb --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamField.java @@ -0,0 +1,412 @@ +/* ObjectStreamField.java -- Class used to store name and class of fields + Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.lang.reflect.TypeSignature; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class intends to describe the field of a class for the serialization + * subsystem. Serializable fields in a serializable class can be explicitly + * exported using an array of ObjectStreamFields. + */ +public class ObjectStreamField implements Comparable +{ + private String name; + private Class type; + private String typename; + private int offset = -1; // XXX make sure this is correct + private boolean unshared; + private boolean persistent = false; + private boolean toset = true; + private Field field; + + ObjectStreamField (Field field) + { + this (field.getName(), field.getType()); + this.field = field; + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named <code>name</code> and is + * of the type <code>type</code>. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + */ + public ObjectStreamField (String name, Class type) + { + this (name, type, false); + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named <code>name</code> and is + * of the type <code>type</code>. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + * @param unshared true if field will be unshared, false otherwise. + */ + public ObjectStreamField (String name, Class type, boolean unshared) + { + if (name == null) + throw new NullPointerException(); + + this.name = name; + this.type = type; + this.typename = TypeSignature.getEncodingOfClass(type); + this.unshared = unshared; + } + + /** + * There are many cases you can not get java.lang.Class from typename + * if your context class loader cannot load it, then use typename to + * construct the field. + * + * @param name Name of the field to export. + * @param typename The coded name of the type for this field. + */ + ObjectStreamField (String name, String typename) + { + this.name = name; + this.typename = typename; + try + { + type = TypeSignature.getClassForEncoding(typename); + } + catch(ClassNotFoundException e) + { + } + } + + /** + * There are many cases you can not get java.lang.Class from typename + * if your context class loader cann not load it, then use typename to + * construct the field. + * + * @param name Name of the field to export. + * @param typename The coded name of the type for this field. + * @param loader The class loader to use to resolve class names. + */ + ObjectStreamField (String name, String typename, ClassLoader loader) + { + this.name = name; + this.typename = typename; + try + { + type = TypeSignature.getClassForEncoding(typename, true, loader); + } + catch(ClassNotFoundException e) + { + } + } + + /** + * This method returns the name of the field represented by the + * ObjectStreamField instance. + * + * @return A string containing the name of the field. + */ + public String getName () + { + return name; + } + + /** + * This method returns the class representing the type of the + * field which is represented by this instance of ObjectStreamField. + * + * @return A class representing the type of the field. + */ + public Class getType () + { + return type; + } + + /** + * This method returns the char encoded type of the field which + * is represented by this instance of ObjectStreamField. + * + * @return A char representing the type of the field. + */ + public char getTypeCode () + { + return typename.charAt (0); + } + + /** + * This method returns a more explicit type name than + * {@link #getTypeCode()} in the case the type is a real + * class (and not a primitive). + * + * @return The name of the type (class name) if it is not a + * primitive, in the other case null is returned. + */ + public String getTypeString () + { + // use intern() + if (isPrimitive()) + return null; + return typename.intern(); + } + + /** + * This method returns the current offset of the field in + * the serialization stream relatively to the other fields. + * The offset is expressed in bytes. + * + * @return The offset of the field in bytes. + * @see #setOffset(int) + */ + public int getOffset () + { + return offset; + } + + /** + * This method sets the current offset of the field. + * + * @param off The offset of the field in bytes. + * @see getOffset() + */ + protected void setOffset (int off) + { + offset = off; + } + + /** + * This method returns whether the field represented by this object is + * unshared or not. + * + * @return Tells if this field is unshared or not. + */ + public boolean isUnshared () + { + return unshared; + } + + /** + * This method returns true if the type of the field + * represented by this instance is a primitive. + * + * @return true if the type is a primitive, false + * in the other case. + */ + public boolean isPrimitive () + { + return typename.length() == 1; + } + + /** + * Compares this object to the given object. + * + * @param obj the object to compare to. + * + * @return -1, 0 or 1. + */ + public int compareTo (Object obj) + { + ObjectStreamField f = (ObjectStreamField) obj; + boolean this_is_primitive = isPrimitive (); + boolean f_is_primitive = f.isPrimitive (); + + if (this_is_primitive && !f_is_primitive) + return -1; + + if (!this_is_primitive && f_is_primitive) + return 1; + + return getName ().compareTo (f.getName ()); + } + + /** + * This method is specific to classpath's implementation and so has the default + * access. It changes the state of this field to "persistent". It means that + * the field should not be changed when the stream is read (if it is not + * explicitly specified using serialPersistentFields). + * + * @param persistent True if the field is persistent, false in the + * other cases. + * @see #isPersistent() + */ + void setPersistent(boolean persistent) + { + this.persistent = persistent; + } + + /** + * This method returns true if the field is marked as persistent. + * + * @return True if persistent, false in the other cases. + * @see #setPersistent(boolean) + */ + boolean isPersistent() + { + return persistent; + } + + /** + * This method is specific to classpath's implementation and so + * has the default access. It changes the state of this field as + * to be set by ObjectInputStream. + * + * @param toset True if this field should be set, false in the other + * cases. + * @see #isToSet() + */ + void setToSet(boolean toset) + { + this.toset = toset; + } + + /** + * This method returns true if the field is marked as to be + * set. + * + * @return True if it is to be set, false in the other cases. + * @see #setToSet(boolean) + */ + boolean isToSet() + { + return toset; + } + + /** + * This method searches for its field reference in the specified class + * object. It requests privileges. If an error occurs the internal field + * reference is not modified. + * + * @throws NoSuchFieldException if the field name does not exist in this class. + * @throws SecurityException if there was an error requesting the privileges. + */ + void lookupField(Class clazz) throws NoSuchFieldException, SecurityException + { + final Field f = clazz.getDeclaredField(name); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + f.setAccessible(true); + return null; + } + }); + + this.field = f; + } + + /** + * This method check whether the field described by this + * instance of ObjectStreamField is compatible with the + * actual implementation of this field. + * + * @throws NullPointerException if this field does not exist + * in the real class. + * @throws InvalidClassException if the types are incompatible. + */ + void checkFieldType() throws InvalidClassException + { + Class ftype = field.getType(); + + if (!ftype.isAssignableFrom(type)) + throw new InvalidClassException + ("invalid field type for " + name + + " in class " + field.getDeclaringClass()); + } + + /** + * Returns a string representing this object. + * + * @return the string. + */ + public String toString () + { + return "ObjectStreamField< " + type + " " + name + " >"; + } + + final void setBooleanField(Object obj, boolean val) + { + VMObjectStreamClass.setBooleanNative(field, obj, val); + } + + final void setByteField(Object obj, byte val) + { + VMObjectStreamClass.setByteNative(field, obj, val); + } + + final void setCharField(Object obj, char val) + { + VMObjectStreamClass.setCharNative(field, obj, val); + } + + final void setShortField(Object obj, short val) + { + VMObjectStreamClass.setShortNative(field, obj, val); + } + + final void setIntField(Object obj, int val) + { + VMObjectStreamClass.setIntNative(field, obj, val); + } + + final void setLongField(Object obj, long val) + { + VMObjectStreamClass.setLongNative(field, obj, val); + } + + final void setFloatField(Object obj, float val) + { + VMObjectStreamClass.setFloatNative(field, obj, val); + } + + final void setDoubleField(Object obj, double val) + { + VMObjectStreamClass.setDoubleNative(field, obj, val); + } + + final void setObjectField(Object obj, Object val) + { + VMObjectStreamClass.setObjectNative(field, obj, val); + } +} diff --git a/libjava/classpath/java/io/OptionalDataException.java b/libjava/classpath/java/io/OptionalDataException.java new file mode 100644 index 00000000000..8d8b1bda0e3 --- /dev/null +++ b/libjava/classpath/java/io/OptionalDataException.java @@ -0,0 +1,91 @@ +/* OptionalDataException.java -- indicates unexpected data in serialized stream + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when unexpected data appears in the input + * stream from which a serialized object is being read. There are two + * cases:<br><ul> + * <li>The next stream element is primitive data. <code>eof</code> will + * be false, and <code>count</code> is the number of bytes of primitive + * data available.</li> + * <li>The data consumable by readObject or readExternal has been exhausted. + * <code>eof</code> is true, and <code>count</code> is 0.</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class OptionalDataException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8011121865681257820L; + + /** + * Whether or not the end of the stream has been reached. + * + * @serial the end of the buffer was reached + */ + public boolean eof; + + /** + * The number of valid bytes that can be read. + * + * @serial the bytes of the buffer remaining + */ + public int length; + + /** + * Create a new OptionalDataException with an eof parameter indicating + * whether or not the end of stream is reached and the number of valid + * bytes that may be read. + * + * @param eof 'true' if end of stream reached, 'false' otherwise + * @param count The number of valid bytes to be read + */ + OptionalDataException(boolean eof, int count) + { + this.eof = eof; + this.length = count; + } +} // class OptionalDataException diff --git a/libjava/classpath/java/io/OutputStream.java b/libjava/classpath/java/io/OutputStream.java new file mode 100644 index 00000000000..8608daaa161 --- /dev/null +++ b/libjava/classpath/java/io/OutputStream.java @@ -0,0 +1,140 @@ +/* OutputStream.java -- Base class for byte output streams + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of bytes. It provides a common set of methods + * for writing bytes to stream. Subclasses implement and/or extend these + * methods to write bytes in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class OutputStream +{ + /** + * This is the default no-argument constructor for this class. This method + * does nothing in this class. + */ + public OutputStream () + { + } + + /** + * This method writes a single byte to the output stream. The byte written + * is the low eight bits of the <code>int</code> passed and a argument. + * <p> + * Subclasses must provide an implementation of this abstract method + * + * @param b The byte to be written to the output stream, passed as + * the low eight bits of an <code>int</code> + * + * @exception IOException If an error occurs + */ + public abstract void write (int b) throws IOException; + + /** + * This method all the writes bytes from the passed array to the + * output stream. This method is equivalent to <code>write(b, 0, + * buf.length)</code> which is exactly how it is implemented in this + * class. + * + * @param b The array of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b) throws IOException, NullPointerException + { + write (b, 0, b.length); + } + + /** + * This method writes <code>len</code> bytes from the specified array + * <code>b</code> starting at index <code>off</code> into the array. + * <p> + * This method in this class calls the single byte <code>write()</code> + * method in a loop until all bytes have been written. Subclasses should + * override this method if possible in order to provide a more efficent + * implementation. + * + * @param b The array of bytes to write from + * @param off The index into the array to start writing from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b, int off, int len) + throws IOException, NullPointerException, IndexOutOfBoundsException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException (); + for (int i = 0; i < len; ++i) + write (b[off + i]); + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + } + + /** + * This method closes the stream. Any internal or native resources + * associated with this stream are freed. Any subsequent attempt to + * access the stream might throw an exception. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + } +} diff --git a/libjava/classpath/java/io/OutputStreamWriter.java b/libjava/classpath/java/io/OutputStreamWriter.java new file mode 100644 index 00000000000..ee229796cce --- /dev/null +++ b/libjava/classpath/java/io/OutputStreamWriter.java @@ -0,0 +1,356 @@ +/* OutputStreamWriter.java -- Writer that converts chars to bytes + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.charset.EncodingHelper; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnsupportedCharsetException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +/** + * This class writes characters to an output stream that is byte oriented + * It converts the chars that are written to bytes using an encoding layer, + * which is specific to a particular encoding standard. The desired + * encoding can either be specified by name, or if no encoding is specified, + * the system default encoding will be used. The system default encoding + * name is determined from the system property <code>file.encoding</code>. + * The only encodings that are guaranteed to be available are "8859_1" + * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not + * provide a mechanism for listing the encodings that are supported in + * a given implementation. + * <p> + * Here is a list of standard encoding names that may be available: + * <p> + * <ul> + * <li>8859_1 (ISO-8859-1/Latin-1) + * <li>8859_2 (ISO-8859-2/Latin-2) + * <li>8859_3 (ISO-8859-3/Latin-3) + * <li>8859_4 (ISO-8859-4/Latin-4) + * <li>8859_5 (ISO-8859-5/Latin-5) + * <li>8859_6 (ISO-8859-6/Latin-6) + * <li>8859_7 (ISO-8859-7/Latin-7) + * <li>8859_8 (ISO-8859-8/Latin-8) + * <li>8859_9 (ISO-8859-9/Latin-9) + * <li>ASCII (7-bit ASCII) + * <li>UTF8 (UCS Transformation Format-8) + * <li>More Later + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 17, 1998. + */ +public class OutputStreamWriter extends Writer +{ + /** + * The output stream. + */ + private OutputStream out; + + /** + * The charset encoder. + */ + private CharsetEncoder encoder; + + /** + * java.io canonical name of the encoding. + */ + private String encodingName; + + /** + * Buffer output before character conversion as it has costly overhead. + */ + private CharBuffer outputBuffer; + private final static int BUFFER_SIZE = 1024; + + /** + * This method initializes a new instance of <code>OutputStreamWriter</code> + * to write to the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param out The <code>OutputStream</code> to write to + * @param encoding_scheme The name of the encoding scheme to use for + * character to byte translation + * + * @exception UnsupportedEncodingException If the named encoding is + * not available. + */ + public OutputStreamWriter (OutputStream out, String encoding_scheme) + throws UnsupportedEncodingException + { + this.out = out; + try + { + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_scheme)) + { + encodingName = "ISO8859_1"; + encoder = null; + return; + } + + /* + * Workraround for encodings with a byte-order-mark. + * We only want to write it once per stream. + */ + try { + if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || + encoding_scheme.equalsIgnoreCase("UTF-16") || + encoding_scheme.equalsIgnoreCase("UTF16")) + { + encoding_scheme = "UTF-16BE"; + out.write((byte)0xFE); + out.write((byte)0xFF); + } else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){ + encoding_scheme = "UTF-16LE"; + out.write((byte)0xFF); + out.write((byte)0xFE); + } + } catch(IOException ioe){ + } + + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + + Charset cs = EncodingHelper.getCharset(encoding_scheme); + if(cs == null) + throw new UnsupportedEncodingException("Encoding "+encoding_scheme+ + " unknown"); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } catch(RuntimeException e) { + // Default to ISO Latin-1, will happen if this is called, for instance, + // before the NIO provider is loadable. + encoder = null; + encodingName = "ISO8859_1"; + } + } + + /** + * This method initializes a new instance of <code>OutputStreamWriter</code> + * to write to the specified stream using the default encoding. + * + * @param out The <code>OutputStream</code> to write to + */ + public OutputStreamWriter (OutputStream out) + { + this.out = out; + outputBuffer = null; + try + { + String encoding = System.getProperty("file.encoding"); + Charset cs = Charset.forName(encoding); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoder = null; + encodingName = "ISO8859_1"; + } + if(encoder != null) + { + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + } + } + + /** + * This method closes this stream, and the underlying + * <code>OutputStream</code> + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + if(out == null) + return; + flush(); + out.close (); + out = null; + } + + /** + * This method returns the name of the character encoding scheme currently + * in use by this stream. If the stream has been closed, then this method + * may return <code>null</code>. + * + * @return The encoding scheme name + */ + public String getEncoding () + { + return out != null ? encodingName : null; + } + + /** + * This method flushes any buffered bytes to the underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + if(out != null){ + if(outputBuffer != null){ + char[] buf = new char[outputBuffer.position()]; + if(buf.length > 0){ + outputBuffer.flip(); + outputBuffer.get(buf); + writeConvert(buf, 0, buf.length); + outputBuffer.clear(); + } + } + out.flush (); + } + } + + /** + * This method writes <code>count</code> characters from the specified + * array to the output stream starting at position <code>offset</code> + * into the array. + * + * @param buf The array of character to write from + * @param offset The offset into the array to start writing chars from + * @param count The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int count) throws IOException + { + if(out == null) + throw new IOException("Stream is closed."); + if(buf == null) + throw new IOException("Buffer is null."); + + if(outputBuffer != null) + { + if(count >= outputBuffer.remaining()) + { + int r = outputBuffer.remaining(); + outputBuffer.put(buf, offset, r); + writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); + outputBuffer.clear(); + offset += r; + count -= r; + // if the remaining bytes is larger than the whole buffer, + // just don't buffer. + if(count >= outputBuffer.remaining()){ + writeConvert(buf, offset, count); + return; + } + } + outputBuffer.put(buf, offset, count); + } else writeConvert(buf, offset, count); + } + + /** + * Converts and writes characters. + */ + private void writeConvert (char[] buf, int offset, int count) + throws IOException + { + if(encoder == null) + { + byte[] b = new byte[count]; + for(int i=0;i<count;i++) + b[i] = (byte)((buf[offset+i] <= 0xFF)?buf[offset+i]:'?'); + out.write(b); + } else { + try { + ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count)); + encoder.reset(); + if(output.hasArray()) + out.write(output.array()); + else + { + byte[] outbytes = new byte[output.remaining()]; + output.get(outbytes); + out.write(outbytes); + } + } catch(IllegalStateException e) { + throw new IOException("Internal error."); + } catch(MalformedInputException e) { + throw new IOException("Invalid character sequence."); + } catch(CharacterCodingException e) { + throw new IOException("Unmappable character."); + } + } + } + + /** + * This method writes <code>count</code> bytes from the specified + * <code>String</code> starting at position <code>offset</code> into the + * <code>String</code>. + * + * @param str The <code>String</code> to write chars from + * @param offset The position in the <code>String</code> to start + * writing chars from + * @param count The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int count) throws IOException + { + if(str == null) + throw new IOException("String is null."); + + write(str.toCharArray(), offset, count); + } + + /** + * This method writes a single character to the output stream. + * + * @param ch The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int ch) throws IOException + { + write(new char[]{ (char)ch }, 0, 1); + } +} // class OutputStreamWriter + diff --git a/libjava/classpath/java/io/PipedInputStream.java b/libjava/classpath/java/io/PipedInputStream.java new file mode 100644 index 00000000000..beb310b4f0c --- /dev/null +++ b/libjava/classpath/java/io/PipedInputStream.java @@ -0,0 +1,374 @@ +/* PipedInputStream.java -- Read portion of piped streams. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedReader. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedReader code. + +/** + * An input stream that reads its bytes from an output stream + * to which it is connected. + * <p> + * Data is read and written to an internal buffer. It is highly recommended + * that the <code>PipedInputStream</code> and connected + * <code>PipedOutputStream</code> + * be part of different threads. If they are not, the read and write + * operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedInputStream extends InputStream +{ + /** PipedOutputStream to which this is connected. Null only if this + * InputStream hasn't been connected yet. */ + PipedOutputStream source; + + /** Set to true if close() has been called on this InputStream. */ + boolean closed; + + + /** + * The size of the internal buffer used for input/output. + */ + /* The "Constant Field Values" Javadoc of the Sun J2SE 1.4 + * specifies 1024. + */ + protected static final int PIPE_SIZE = 1024; + + + /** + * This is the internal circular buffer used for storing bytes written + * to the pipe and from which bytes are read by this stream + */ + protected byte[] buffer = new byte[PIPE_SIZE]; + + /** + * The index into buffer where the next byte from the connected + * <code>PipedOutputStream</code> will be written. If this variable is + * equal to <code>out</code>, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + protected int in = -1; + + /** + * This index into the buffer where bytes will be read from. + */ + protected int out = 0; + + /** Buffer used to implement single-argument read/receive */ + private byte[] read_buf = new byte[1]; + + /** + * Creates a new <code>PipedInputStream</code> that is not connected to a + * <code>PipedOutputStream</code>. It must be connected before bytes can + * be read from this stream. + */ + public PipedInputStream() + { + } + + /** + * This constructor creates a new <code>PipedInputStream</code> and connects + * it to the passed in <code>PipedOutputStream</code>. The stream is then + * ready for reading. + * + * @param source The <code>PipedOutputStream</code> to connect this + * stream to + * + * @exception IOException If <code>source</code> is already connected. + */ + public PipedInputStream(PipedOutputStream source) throws IOException + { + connect(source); + } + + /** + * This method connects this stream to the passed in + * <code>PipedOutputStream</code>. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param src The <code>PipedOutputStream</code> to connect this stream to + * + * @exception IOException If this PipedInputStream or <code>source</code> + * has been connected already. + */ + public void connect(PipedOutputStream source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method receives a byte of input from the source PipedOutputStream. + * If the internal circular buffer is full, this method blocks. + * + * @param val The byte to write to this stream + * + * @exception IOException if error occurs + * @specnote Weird. This method must be some sort of accident. + */ + protected synchronized void receive(int val) throws IOException + { + read_buf[0] = (byte) (val & 0xff); + receive (read_buf, 0, 1); + } + + /** + * This method is used by the connected <code>PipedOutputStream</code> to + * write bytes into the buffer. + * + * @param buf The array containing bytes to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedOutputStream.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + synchronized void receive(byte[] buf, int offset, int len) + throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + notifyAll(); + wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many bytes from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy bytes until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + notifyAll(); + } + + /** + * This method reads one byte from the stream. + * -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + * + * <p>This method will block if no byte is available to be read.</p> + * + * @return the value of the read byte value, or -1 of the end of the stream + * was reached + * + * @throws IOException if an error occured + */ + public int read() throws IOException + { + // Method operates by calling the multibyte overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? (read_buf[0] & 0xff) : -1; + } + + /** + * This method reads bytes from the stream into a caller supplied buffer. + * It starts storing bytes at position <code>offset</code> into the + * buffer and + * reads a maximum of <code>len</code> bytes. Note that this method + * can actually + * read fewer than <code>len</code> bytes. The actual number of bytes + * read is + * returned. A -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedOutputStream. + * <p> + * This method will block if no bytes are available to be read. + * + * @param buf The buffer into which bytes will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of bytes to read. + * + * @exception IOException If <code>close()</code> was called on this Piped + * InputStream. + */ + public synchronized int read(byte[] buf, int offset, int len) + throws IOException + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many bytes from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting outputstream that there is now space + // to write. + notifyAll(); + return total; + } + } + } + + /** + * This method returns the number of bytes that can be read from this stream + * before blocking could occur. This is the number of bytes that are + * currently unread in the internal circular buffer. Note that once this + * many additional bytes are read, the stream may block on a subsequent + * read, but it not guaranteed to block. + * + * @return The number of bytes that can be read before blocking might occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. + + if (in < 0) + return 0; + else if (out < in) + return in - out; + else + return (buffer.length - out) + in; + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + notifyAll(); + } +} + diff --git a/libjava/classpath/java/io/PipedOutputStream.java b/libjava/classpath/java/io/PipedOutputStream.java new file mode 100644 index 00000000000..81881050d1b --- /dev/null +++ b/libjava/classpath/java/io/PipedOutputStream.java @@ -0,0 +1,181 @@ +/* PipedOutputStream.java -- Write portion of piped streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedWriter. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedWriter code. + +/** + * This class writes its bytes to a <code>PipedInputStream</code> to + * which it is connected. + * <p> + * It is highly recommended that a <code>PipedOutputStream</code> and its + * connected <code>PipedInputStream</code> be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedOutputStream extends OutputStream +{ + /** Target PipedInputStream to which this is connected. Null only if this + * OutputStream hasn't been connected yet. */ + PipedInputStream sink; + + /** Set to true if close() has been called on this OutputStream. */ + boolean closed; + + /** + * Create an unconnected PipedOutputStream. It must be connected + * to a <code>PipedInputStream</code> using the <code>connect</code> + * method prior to writing any data or an exception will be thrown. + */ + public PipedOutputStream() + { + } + + /** + * Create a new <code>PipedOutputStream</code> instance + * to write to the specified <code>PipedInputStream</code>. This stream + * is then ready for writing. + * + * @param sink The <code>PipedInputStream</code> to connect this stream to. + * + * @exception IOException If <code>sink</code> has already been connected + * to a different PipedOutputStream. + */ + public PipedOutputStream(PipedInputStream sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified <code>PipedInputStream</code> + * object. This stream will then be ready for writing. + * + * @param sink The <code>PipedInputStream</code> to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedInputStream sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single byte of date to the stream. Note that this method will + * block if the <code>PipedInputStream</code> to which this object is + * connected has a full buffer. + * + * @param b The byte of data to be written, passed as an <code>int</code>. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive (b); + } + + /** + * This method writes <code>len</code> bytes of data from the byte array + * <code>buf</code> starting at index <code>offset</code> in the array + * to the stream. Note that this method will block if the + * <code>PipedInputStream</code> to which this object is connected has + * a buffer that cannot hold all of the bytes to be written. + * + * @param buffer The array containing bytes to write to the stream. + * @param offset The index into the array to start writing bytes from. + * @param len The number of bytes to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(byte[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedOutputStream has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PipedReader.java b/libjava/classpath/java/io/PipedReader.java new file mode 100644 index 00000000000..90fc10f672d --- /dev/null +++ b/libjava/classpath/java/io/PipedReader.java @@ -0,0 +1,361 @@ +/* PipedReader.java -- Read portion of piped character streams. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedInputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedInputStream code. + +/** + * An input stream that reads characters from a piped writer to which it is + * connected. + * <p> + * Data is read and written to an internal buffer. It is highly recommended + * that the <code>PipedReader</code> and connected <code>PipedWriter</code> + * be part of different threads. If they are not, there is a possibility + * that the read and write operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedReader extends Reader +{ + /** PipedWriter to which this is connected. Null only if this + * Reader hasn't been connected yet. */ + PipedWriter source; + + /** Set to true if close() has been called on this Reader. */ + boolean closed; + + /** + * The size of the internal buffer used for input/output. + */ + static final int PIPE_SIZE = 2048; + + /** + * This is the internal circular buffer used for storing chars written + * to the pipe and from which chars are read by this stream + */ + char[] buffer = new char[PIPE_SIZE]; + + /** + * The index into buffer where the next char from the connected + * <code>PipedWriter</code> will be written. If this variable is + * equal to <code>out</code>, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + int in = -1; + + /** + * This index into the buffer where chars will be read from. + */ + int out = 0; + + /** Buffer used to implement single-argument read/receive */ + char[] read_buf = new char[1]; + + /** + * Creates a new <code>PipedReader</code> that is not connected to a + * <code>PipedWriter</code>. It must be connected before chars can + * be read from this stream. + */ + public PipedReader() + { + } + + /** + * This constructor creates a new <code>PipedReader</code> and connects + * it to the passed in <code>PipedWriter</code>. The stream is then + * ready for reading. + * + * @param source The <code>PipedWriter</code> to connect this stream to + * + * @exception IOException If <code>source</code> is already connected. + */ + public PipedReader(PipedWriter source) throws IOException + { + connect(source); + } + + /** + * This method connects this stream to the passed in + * <code>PipedWriter</code>. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param source The <code>PipedWriter</code> to connect this stream to + * + * @exception IOException If this PipedReader or <code>source</code> + * has been connected already. + */ + public void connect(PipedWriter source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method is used by the connected <code>PipedWriter</code> to + * write chars into the buffer. + * + * @param buf The array containing chars to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedWriter.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + void receive(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + lock.notifyAll(); + lock.wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many chars from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy chars until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + lock.notifyAll(); + } + } + + /** + * This method reads chars from the stream into a caller supplied buffer. + * It starts storing chars at position <code>offset</code> into the + * buffer and + * reads a maximum of <code>len</code> chars. Note that this method + * can actually + * read fewer than <code>len</code> chars. The actual number of chars + * read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + * <p> + * This method will block if no char is available to be read. + */ + public int read() throws IOException + { + // Method operates by calling the multichar overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? read_buf[0] : -1; + } + + /** + * This method reads characters from the stream into a caller supplied + * buffer. It starts storing chars at position <code>offset</code> into + * the buffer and reads a maximum of <code>len</code> chars. Note that + * this method can actually read fewer than <code>len</code> chars. + * The actual number of chars read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedWriter. + * <p> + * This method will block if no chars are available to be read. + * + * @param buf The buffer into which chars will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of chars to read. + * + * @exception IOException If <code>close()</code> was called on this Piped + * Reader. + */ + public int read(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + lock.wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many chars from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting Writer that there is now space + // to write. + lock.notifyAll(); + return total; + } + } + } + } + + public boolean ready() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. However, checking for a + // closed stream is explicitly required by the JDK 1.2 and 1.3 + // documentation (for Reader.close()), so we do it. + + synchronized (lock) + { + if (closed) + throw new IOException("Pipe closed"); + + if (in < 0) + return false; + + int count; + if (out < in) + count = in - out; + else + count = (buffer.length - out) - in; + + return (count > 0); + } + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + lock.notifyAll(); + } + } +} + diff --git a/libjava/classpath/java/io/PipedWriter.java b/libjava/classpath/java/io/PipedWriter.java new file mode 100644 index 00000000000..92786e5de7d --- /dev/null +++ b/libjava/classpath/java/io/PipedWriter.java @@ -0,0 +1,182 @@ +/* PipedWriter.java -- Write portion of piped character streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedOutputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedOutputStream code. + +/** + * This class writes its chars to a <code>PipedReader</code> to + * which it is connected. + * <p> + * It is highly recommended that a <code>PipedWriter</code> and its + * connected <code>PipedReader</code> be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedWriter extends Writer +{ + /** Target PipedReader to which this is connected. Null only if this + * Writer hasn't been connected yet. */ + PipedReader sink; + + /** Set to true if close() has been called on this Writer. */ + boolean closed; + + /** Buffer used to implement single-argument write */ + char[] read_buf = new char[1]; + + /** + * Create an unconnected PipedWriter. It must be connected + * to a <code>PipedReader</code> using the <code>connect</code> + * method prior to writing any data or an exception will be thrown. + */ + public PipedWriter() + { + } + + /** + * Create a new <code>PipedWriter</code> instance + * to write to the specified <code>PipedReader</code>. This stream + * is then ready for writing. + * + * @param sink The <code>PipedReader</code> to connect this stream to. + * + * @exception IOException If <code>sink</code> has already been connected + * to a different PipedWriter. + */ + public PipedWriter(PipedReader sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified <code>PipedReader</code> + * object. This stream will then be ready for writing. + * + * @param sink The <code>PipedReader</code> to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedReader sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single char of date to the stream. Note that this method will + * block if the <code>PipedReader</code> to which this object is + * connected has a full buffer. + * + * @param b The char of data to be written, passed as an <code>int</code>. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + read_buf[0] = (char) (b & 0xffff); + sink.receive (read_buf, 0, 1); + } + + /** + * This method writes <code>len</code> chars of data from the char array + * <code>buf</code> starting at index <code>offset</code> in the array + * to the stream. Note that this method will block if the + * <code>PipedReader</code> to which this object is connected has + * a buffer that cannot hold all of the chars to be written. + * + * @param buffer The array containing chars to write to the stream. + * @param offset The index into the array to start writing chars from. + * @param len The number of chars to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(char[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedWriter has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PrintStream.java b/libjava/classpath/java/io/PrintStream.java new file mode 100644 index 00000000000..8e50559b310 --- /dev/null +++ b/libjava/classpath/java/io/PrintStream.java @@ -0,0 +1,553 @@ +/* PrintStream.java -- OutputStream for printing output + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believed complete and correct to 1.3 + */ + +/** + * This class prints Java primitive values and object to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the <code>checkError()</code> method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink when the current line is terminated. + * <p> + * This class converts char's into byte's using the system default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class PrintStream extends FilterOutputStream +{ + /* Notice the implementation is quite similar to OutputStreamWriter. + * This leads to some minor duplication, because neither inherits + * from the other, and we want to maximize performance. */ + + // Line separator string. + private static final char[] line_separator + = System.getProperty("line.separator").toCharArray(); + + /** + * Encoding name + */ + private String encoding; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error_occurred = false; + + /** + * This is <code>true</code> if auto-flush is enabled, + * <code>false</code> otherwise + */ + private boolean auto_flush; + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. + * + * @param out The <code>OutputStream</code> to write to. + */ + public PrintStream (OutputStream out) + { + this (out, false); + } + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every <code>print</code> or <code>println</code> call, when the + * <code>write</code> methods with array arguments are called, or when a + * single new-line character is written. + * <p> + * + * @param out The <code>OutputStream</code> to write to. + * @param auto_flush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + */ + public PrintStream (OutputStream out, boolean auto_flush) + { + super (out); + + try { + this.encoding = System.getProperty("file.encoding"); + } catch (SecurityException e){ + this.encoding = "ISO8859_1"; + } catch (IllegalArgumentException e){ + this.encoding = "ISO8859_1"; + } catch (NullPointerException e){ + this.encoding = "ISO8859_1"; + } + this.auto_flush = auto_flush; + } + + /** + * This method intializes a new <code>PrintStream</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every <code>print</code> or <code>println</code> call, when the + * <code>write</code> methods with array arguments are called, or when a + * single new-line character is written. + * <p> + * + * @param out The <code>OutputStream</code> to write to. + * @param auto_flush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + * @param encoding The name of the character encoding to use for this + * object. + */ + public PrintStream (OutputStream out, boolean auto_flush, String encoding) + throws UnsupportedEncodingException + { + super (out); + + new String(new byte[]{0}, encoding); // check if encoding is supported + this.encoding = encoding; + this.auto_flush = auto_flush; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * <code>true</code> forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return <code>true</code> if an error has occurred, + * <code>false</code> otherwise + */ + public boolean checkError () + { + flush (); + return error_occurred; + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by <code>checkError</code>. + */ + protected void setError () + { + error_occurred = true; + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close () + { + try + { + flush(); + out.close(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method flushes any buffered bytes to the underlying stream and + * then flushes that stream as well. + */ + public void flush () + { + try + { + out.flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (String str, boolean println) + { + try + { + writeChars(str, 0, str.length()); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (char[] chars, int pos, int len, + boolean println) + { + try + { + writeChars(chars, pos, len); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private void writeChars(char[] buf, int offset, int count) + throws IOException + { + byte[] bytes = (new String(buf, offset, count)).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + private void writeChars(String str, int offset, int count) + throws IOException + { + byte[] bytes = str.substring(offset, offset+count).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * @param bool The <code>boolean</code> value to print + */ + public void print (boolean bool) + { + print(String.valueOf(bool), false); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param inum The <code>int</code> value to be printed + */ + public void print (int inum) + { + print(String.valueOf(inum), false); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param lnum The <code>long</code> value to be printed + */ + public void print (long lnum) + { + print(String.valueOf(lnum), false); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param fnum The <code>float</code> value to be printed + */ + public void print (float fnum) + { + print(String.valueOf(fnum), false); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param dnum The <code>double</code> value to be printed + */ + public void print (double dnum) + { + print(String.valueOf(dnum), false); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * @param obj The <code>Object</code> to print. + */ + public void print (Object obj) + { + print(obj == null ? "null" : obj.toString(), false); + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The <code>String</code> to print. + */ + public void print (String str) + { + print(str == null ? "null" : str, false); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The <code>char</code> value to be printed + */ + public synchronized void print (char ch) + { + print(new char[]{ch}, 0, 1, false); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print (char[] charArray) + { + print(charArray, 0, charArray.length, false); + } + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property <xmp>line.separator</xmp> + * and is not necessarily the Unix '\n' newline character. + */ + public void println () + { + print(line_separator, 0, line_separator.length, false); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param bool The <code>boolean</code> value to print + */ + public void println (boolean bool) + { + print(String.valueOf(bool), true); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param inum The <code>int</code> value to be printed + */ + public void println (int inum) + { + print(String.valueOf(inum), true); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param lnum The <code>long</code> value to be printed + */ + public void println (long lnum) + { + print(String.valueOf(lnum), true); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param fnum The <code>float</code> value to be printed + */ + public void println (float fnum) + { + print(String.valueOf(fnum), true); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param dnum The <code>double</code> value to be printed + */ + public void println (double dnum) + { + print(String.valueOf(dnum), true); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param obj The <code>Object</code> to print. + */ + public void println (Object obj) + { + print(obj == null ? "null" : obj.toString(), true); + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param str The <code>String</code> to print. + */ + public void println (String str) + { + print (str == null ? "null" : str, true); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param ch The <code>char</code> value to be printed + */ + public synchronized void println (char ch) + { + print(new char[]{ch}, 0, 1, true); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * <p> + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println (char[] charArray) + { + print(charArray, 0, charArray.length, true); + } + + /** + * This method writes a byte of data to the stream. If auto-flush is + * enabled, printing a newline character will cause the stream to be + * flushed after the character is written. + * + * @param oneByte The byte to be written + */ + public void write (int oneByte) + { + try + { + out.write (oneByte & 0xff); + + if (auto_flush && (oneByte == '\n')) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method writes <code>len</code> bytes from the specified array + * starting at index <code>offset</code> into the array. + * + * @param buffer The array of bytes to write + * @param offset The index into the array to start writing from + * @param len The number of bytes to write + */ + public void write (byte[] buffer, int offset, int len) + { + try + { + out.write (buffer, offset, len); + + if (auto_flush) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } +} // class PrintStream + diff --git a/libjava/classpath/java/io/PrintWriter.java b/libjava/classpath/java/io/PrintWriter.java new file mode 100644 index 00000000000..5fd0b162f31 --- /dev/null +++ b/libjava/classpath/java/io/PrintWriter.java @@ -0,0 +1,571 @@ +/* PrintWriter.java -- prints primitive values and objects to a stream as text + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * However, should use native methods for conversion. + */ + +/** + * This class prints Java primitive values and objects to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the <code>checkError()</code> method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink whenever one of the <code>println</code> methods is + * called. (Note that this differs from the <code>PrintStream</code> + * class which also auto-flushes when it encounters a newline character + * in the chars written). + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date April 17, 1998. + */ +public class PrintWriter extends Writer +{ + /** + * <code>true</code> if auto-flush is enabled, <code>false</code> otherwise + */ + private boolean autoflush; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error; + + /** + * This is the underlying <code>Writer</code> we are sending output + * to + */ + protected Writer out; + + /** + * This method intializes a new <code>PrintWriter</code> object to write + * to the specified output sink. The form of the constructor does not + * enable auto-flush functionality. + * + * @param wr The <code>Writer</code> to write to. + */ + public PrintWriter(Writer wr) + { + super(wr.lock); + this.out = wr; + } + + /** + * This method intializes a new <code>PrintWriter</code> object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every line is terminated or newline character is written. + * + * @param wr The <code>Writer</code> to write to. + * @param autoflush <code>true</code> to flush the stream after every + * line, <code>false</code> otherwise + */ + public PrintWriter(Writer wr, boolean autoflush) + { + super(wr.lock); + this.out = wr; + this.autoflush = autoflush; + } + + /** + * This method initializes a new <code>PrintWriter</code> object to write + * to the specified <code>OutputStream</code>. Characters will be converted + * to chars using the system default encoding. Auto-flush functionality + * will not be enabled. + * + * @param out The <code>OutputStream</code> to write to + */ + public PrintWriter(OutputStream out) + { + super(); + this.out = new OutputStreamWriter(out); + this.lock = this.out; + } + + /** + * This method initializes a new <code>PrintWriter</code> object to write + * to the specified <code>OutputStream</code>. Characters will be converted + * to chars using the system default encoding. This form of the + * constructor allows auto-flush functionality to be enabled if desired + * + * @param out The <code>OutputStream</code> to write to + * @param autoflush <code>true</code> to flush the stream after every + * <code>println</code> call, <code>false</code> otherwise. + */ + public PrintWriter(OutputStream out, boolean autoflush) + { + this(out); + this.autoflush = autoflush; + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by <code>checkError</code>. + */ + protected void setError() + { + error = true; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * <code>true</code> forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return <code>true</code> if an error has occurred, + * <code>false</code> otherwise + */ + public boolean checkError() + { + flush(); + return error; + } + + /** + * This method flushes any buffered chars to the underlying stream and + * then flushes that stream as well. + */ + public void flush() + { + try + { + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close() + { + try + { + out.close(); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The <code>String</code> to print. + */ + public void print(String str) + { + write(str == null ? "null" : str); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The <code>char</code> value to be printed + */ + public void print(char ch) + { + write((int) ch); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * @param bool The <code>boolean</code> value to print + */ + public void print(boolean bool) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write (bool ? "true" : "false"); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param inum The <code>int</code> value to be printed + */ + public void print(int inum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Integer.toString(inum)); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param lnum The <code>long</code> value to be printed + */ + public void print(long lnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Long.toString(lnum)); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param fnum The <code>float</code> value to be printed + */ + public void print(float fnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Float.toString(fnum)); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * @param dnum The <code>double</code> value to be printed + */ + public void print(double dnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Double.toString(dnum)); + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * @param obj The <code>Object</code> to print. + */ + public void print(Object obj) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(obj == null ? "null" : obj.toString()); + } + + /** + * This is the system dependent line separator + */ + private static final char[] line_separator + = System.getProperty("line.separator").toCharArray(); + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property <xmp>line.separator</xmp> + * and is not necessarily the Unix '\n' newline character. + */ + public void println() + { + synchronized (lock) + { + try + { + write(line_separator, 0, line_separator.length); + if (autoflush) + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + } + + /** + * This methods prints a boolean value to the stream. <code>true</code> + * values are printed as "true" and <code>false</code> values are printed + * as "false". + * + * This method prints a line termination sequence after printing the value. + * + * @param bool The <code>boolean</code> value to print + */ + public void println(boolean bool) + { + synchronized (lock) + { + print(bool); + println(); + } + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param inum The <code>int</code> value to be printed + */ + public void println(int inum) + { + synchronized (lock) + { + print(inum); + println(); + } + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param lnum The <code>long</code> value to be printed + */ + public void println(long lnum) + { + synchronized (lock) + { + print(lnum); + println(); + } + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param fnum The <code>float</code> value to be printed + */ + public void println(float fnum) + { + synchronized (lock) + { + print(fnum); + println(); + } + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the <code>String.valueOf()</code> method. + * + * This method prints a line termination sequence after printing the value. + * + * @param dnum The <code>double</code> value to be printed + */ + public void println(double dnum) + { + synchronized (lock) + { + print(dnum); + println(); + } + } + + /** + * This method prints an <code>Object</code> to the stream. The actual + * value printed is determined by calling the <code>String.valueOf()</code> + * method. + * + * This method prints a line termination sequence after printing the value. + * + * @param obj The <code>Object</code> to print. + */ + public void println(Object obj) + { + synchronized (lock) + { + print(obj); + println(); + } + } + + /** + * This method prints a <code>String</code> to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param str The <code>String</code> to print. + */ + public void println(String str) + { + synchronized (lock) + { + print(str); + println(); + } + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * This method prints a line termination sequence after printing the value. + * + * @param ch The <code>char</code> value to be printed + */ + public void println(char ch) + { + synchronized (lock) + { + print(ch); + println(); + } + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println(char[] charArray) + { + synchronized (lock) + { + print(charArray); + println(); + } + } + + /** + * This method writes a single char to the stream. + * + * @param ch The char to be written, passed as a int + */ + public void write(int ch) + { + try + { + out.write(ch); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes <code>count</code> chars from the specified array + * starting at index <code>offset</code> into the array. + * + * @param charArray The array of chars to write + * @param offset The index into the array to start writing from + * @param count The number of chars to write + */ + public void write(char[] charArray, int offset, int count) + { + try + { + out.write(charArray, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes <code>count</code> chars from the specified + * <code>String</code> to the output starting at character position + * <code>offset</code> into the <code>String</code> + * + * @param str The <code>String</code> to write chars from + * @param offset The offset into the <code>String</code> to start writing from + * @param count The number of chars to write. + */ + public void write(String str, int offset, int count) + { + try + { + out.write(str, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method write all the chars in the specified array to the output. + * + * @param charArray The array of characters to write + */ + public void write(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This method writes the contents of the specified <code>String</code> + * to the underlying stream. + * + * @param str The <code>String</code> to write + */ + public void write(String str) + { + write(str, 0, str.length()); + } +} + diff --git a/libjava/classpath/java/io/PushbackInputStream.java b/libjava/classpath/java/io/PushbackInputStream.java new file mode 100644 index 00000000000..71cf244274e --- /dev/null +++ b/libjava/classpath/java/io/PushbackInputStream.java @@ -0,0 +1,328 @@ +/* PushbackInputStream.java -- An input stream that can unread bytes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This subclass of <code>FilterInputStream</code> provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + * <p> + * The default pushback buffer size one byte, but this can be overridden + * by the creator of the stream. + * <p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackInputStream extends FilterInputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + protected byte[] buf; + + /** + * This is the position in the buffer from which the next byte will be + * read. Bytes are stored in reverse order in the buffer, starting from + * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when + * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when + * it is empty + */ + protected int pos; + + /** + * This method initializes a <code>PushbackInputStream</code> to + * read from the specified subordinate <code>InputStream</code> + * with a default pushback buffer size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a <code>PushbackInputStream</code> to + * read from the specified subordinate <code>InputStream</code> with + * the specified buffer size + * + * @param in The subordinate <code>InputStream</code> to read from + * @param size The pushback buffer size to use + */ + public PushbackInputStream(InputStream in, int size) + { + super(in); + if (size < 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + pos = buf.length; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + * <p> + * This method will return the number of bytes available from the + * pushback buffer plus the number of bytes available from the + * underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return (buf.length - pos) + super.available(); + } + + /** + * This method closes the stream and releases any associated resources. + * + * @exception IOException If an error occurs. + */ + public synchronized void close() throws IOException + { + buf = null; + super.close(); + } + + /** + * This method returns <code>false</code> to indicate that it does + * not support mark/reset functionality. + * + * @return This method returns <code>false</code> to indicate that + * this class does not support mark/reset functionality + */ + public boolean markSupported() + { + return false; + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("Mark not supported in this class"); + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. The byte returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the byte will be read from the underlying stream. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos < buf.length) + return ((int) buf[pos++]) & 0xFF; + + return super.read(); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * <code>offset</code> into the buffer and attempts to read + * <code>len</code> bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method first reads bytes from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the bytes requested, the remaining bytes are read from the + * underlying stream. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + int numBytes = Math.min(buf.length - pos, len); + + if (numBytes > 0) + { + System.arraycopy (buf, pos, b, off, numBytes); + pos += numBytes; + len -= numBytes; + off += numBytes; + } + + if (len > 0) + { + len = super.read(b, off, len); + if (len == -1) //EOF + return numBytes > 0 ? numBytes : -1; + numBytes += len; + } + return numBytes; + } + + /** + * This method pushes a single byte of data into the pushback buffer. + * The byte pushed back is the one that will be returned as the first byte + * of the next read. + * <p> + * If the pushback buffer is full, this method throws an exception. + * <p> + * The argument to this method is an <code>int</code>. Only the low + * eight bits of this value are pushed back. + * + * @param b The byte to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public synchronized void unread(int b) throws IOException + { + if (pos <= 0) + throw new IOException("Insufficient space in pushback buffer"); + + buf[--pos] = (byte) b; + } + + /** + * This method pushes all of the bytes in the passed byte array into + * the pushback bfer. These bytes are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * <code>b[0]</code> followed by <code>b[1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b) throws IOException + { + unread(b, 0, b.length); + } + + /** + * This method pushed back bytes from the passed in array into the + * pushback buffer. The bytes from <code>b[offset]</code> to + * <code>b[offset + len]</code> are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * <code>b[offset]</code> followed by <code>b[offset + 1]</code>, + * etc. + * <p> + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * @param off The index into the array where the bytes to be push start + * @param len The number of bytes to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b, int off, int len) + throws IOException + { + if (pos < len) + throw new IOException("Insufficient space in pushback buffer"); + + // Note the order that these bytes are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1390. + System.arraycopy(b, off, buf, pos - len, len); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= len; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * <p> + * This method first discards bytes from the buffer, then calls the + * <code>skip</code> method on the underlying <code>InputStream</code> to + * skip additional bytes if necessary. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public synchronized long skip(long n) throws IOException + { + final long origN = n; + + if (n > 0L) + { + int numread = (int) Math.min((long) (buf.length - pos), n); + pos += numread; + n -= numread; + if (n > 0) + n -= super.skip(n); + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/PushbackReader.java b/libjava/classpath/java/io/PushbackReader.java new file mode 100644 index 00000000000..04bccc70fc5 --- /dev/null +++ b/libjava/classpath/java/io/PushbackReader.java @@ -0,0 +1,384 @@ +/* PushbackReader.java -- An character stream that can unread chars + Copyright (C) 1998, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This subclass of <code>FilterReader</code> provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + * <p> + * The default pushback buffer size one char, but this can be overridden + * by the creator of the stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackReader extends FilterReader +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + private char[] buf; + + /** + * This is the position in the buffer from which the next char will be + * read. Bytes are stored in reverse order in the buffer, starting from + * <code>buf[buf.length - 1]</code> to <code>buf[0]</code>. Thus when + * <code>pos</code> is 0 the buffer is full and <code>buf.length</code> when + * it is empty + */ + private int pos; + + /** + * This method initializes a <code>PushbackReader</code> to read from the + * specified subordinate <code>Reader</code> with a default pushback buffer + * size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a <code>PushbackReader</code> to read from the + * specified subordinate <code>Reader</code> with the specified buffer + * size + * + * @param in The subordinate <code>Reader</code> to read from + * @param bufsize The pushback buffer size to use + */ + public PushbackReader(Reader in, int bufsize) + { + super(in); + + if (bufsize < 0) + throw new IllegalArgumentException("buffer size must be positive"); + + buf = new char[bufsize]; + pos = bufsize; + } + + /** + * This method closes the stream and frees any associated resources. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + synchronized (lock) + { + buf = null; + super.close(); + } + } + + /** + * This method throws an exception when called since this class does + * not support mark/reset. + * + * @param read_limit Not used. + * + * @exception IOException Always thrown to indicate mark/reset not supported. + */ + public void mark(int read_limit) throws IOException + { + throw new IOException("mark not supported in this class"); + } + + /** + * This method returns <code>false</code> to indicate that it does not support + * mark/reset functionality. + * + * @return This method returns <code>false</code> to indicate that this + * class does not support mark/reset functionality + * + */ + public boolean markSupported() + { + return(false); + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported in this class"); + } + + /** + * This method determines whether or not this stream is ready to be read. + * If it returns <code>false</code> to indicate that the stream is not + * ready, any attempt to read from the stream could (but is not + * guaranteed to) block. + * <p> + * This stream is ready to read if there are either chars waiting to be + * read in the pushback buffer or if the underlying stream is ready to + * be read. + * + * @return <code>true</code> if this stream is ready to be read, + * <code>false</code> otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException ("stream closed"); + + if (((buf.length - pos) > 0) || super.ready()) + return(true); + else + return(false); + } + } + + // Don't delete this method just because the spec says it shouldn't be there! + // See the CVS log for details. + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method first discards chars from the buffer, then calls the + * <code>skip</code> method on the underlying <code>Reader</code> to + * skip additional chars if necessary. + * + * @param num_chars The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + synchronized (lock) + { + if (num_chars <= 0) + return(0); + + if ((buf.length - pos) >= num_chars) + { + pos += num_chars; + return(num_chars); + } + + int chars_discarded = buf.length - pos; + pos = buf.length; + + long chars_skipped = in.skip(num_chars - chars_discarded); + + return(chars_discarded + chars_skipped); + } + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. The char returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the char will be read from the underlying stream. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (pos == buf.length) + return(super.read()); + + ++pos; + return((buf[pos - 1] & 0xFFFF)); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into + * the buffer and attempts to read <code>len</code> chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method first reads chars from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the chars requested, the remaining chars are read from the + * underlying stream. + * + * @param buffer The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param length The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (offset < 0 || length < 0 || offset + length > buffer.length) + throw new ArrayIndexOutOfBoundsException(); + + int numBytes = Math.min(buf.length - pos, length); + if (numBytes > 0) + { + System.arraycopy (buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + return super.read(buffer, offset, length); + } + } + + /** + * This method pushes a single char of data into the pushback buffer. + * The char pushed back is the one that will be returned as the first char + * of the next read. + * <p> + * If the pushback buffer is full, this method throws an exception. + * <p> + * The argument to this method is an <code>int</code>. Only the low eight + * bits of this value are pushed back. + * + * @param b The char to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public void unread(int b) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos == 0) + throw new IOException("Pushback buffer is full"); + + --pos; + buf[pos] = (char)(b & 0xFFFF); + } + } + + /** + * This method pushes all of the chars in the passed char array into + * the pushback buffer. These chars are pushed in reverse order so that + * the next char read from the stream after this operation will be + * <code>buf[0]</code> followed by <code>buf[1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buf The char array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buf) throws IOException + { + unread(buf, 0, buf.length); + } + + /** + * This method pushed back chars from the passed in array into the pushback + * buffer. The chars from <code>buf[offset]</code> to + * <code>buf[offset + len]</code> + * are pushed in reverse order so that the next char read from the stream + * after this operation will be <code>buf[offset]</code> followed by + * <code>buf[offset + 1]</code>, etc. + * <p> + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buffer The char array to be pushed back + * @param offset The index into the array where the chars to be push start + * @param length The number of chars to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos < length) + throw new IOException("Pushback buffer is full"); + + // Note the order that these chars are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1397. + System.arraycopy(buffer, offset, buf, pos - length, length); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= length; + } + } +} + diff --git a/libjava/classpath/java/io/RandomAccessFile.java b/libjava/classpath/java/io/RandomAccessFile.java new file mode 100644 index 00000000000..23be5a31947 --- /dev/null +++ b/libjava/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,991 @@ +/* RandomAccessFile.java -- Class supporting random file I/O + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.channels.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believe complete and correct to 1.1. + */ + +/** + * This class allows reading and writing of files at random locations. + * Most Java I/O classes are either pure sequential input or output. This + * class fulfills the need to be able to read the bytes of a file in an + * arbitrary order. In addition, this class implements the + * <code>DataInput</code> and <code>DataOutput</code> interfaces to allow + * the reading and writing of Java primitives. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class RandomAccessFile implements DataOutput, DataInput +{ + + // The underlying file. + private FileChannelImpl ch; + private FileDescriptor fd; + // The corresponding input and output streams. + private DataOutputStream out; + private DataInputStream in; + + + /** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified <code>File</code> object with the specified + * access mode. The access mode is either "r" for read only access or "rw" + * for read-write access. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param file The <code>File</code> object to read and/or write. + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If <code>mode</code> has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory, or + * any other error occurs + */ + public RandomAccessFile (File file, String mode) + throws FileNotFoundException + { + int fdmode; + if (mode.equals("r")) + fdmode = FileChannelImpl.READ; + else if (mode.equals("rw")) + fdmode = FileChannelImpl.READ | FileChannelImpl.WRITE; + else if (mode.equals("rws")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.SYNC); + } + else if (mode.equals("rwd")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.DSYNC); + } + else + throw new IllegalArgumentException ("invalid mode: " + mode); + + final String fileName = file.getPath(); + + // The obligatory SecurityManager stuff + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + s.checkRead(fileName); + + if ((fdmode & FileChannelImpl.WRITE) != 0) + s.checkWrite(fileName); + } + + ch = FileChannelImpl.create(file, fdmode); + fd = new FileDescriptor(ch); + out = new DataOutputStream (new FileOutputStream (fd)); + in = new DataInputStream (new FileInputStream (fd)); + } + + /** + * This method initializes a new instance of <code>RandomAccessFile</code> + * to read from the specified file name with the specified access mode. + * The access mode is either "r" for read only access, "rw" for read + * write access, "rws" for synchronized read/write access of both + * content and metadata, or "rwd" for read/write access + * where only content is required to be synchronous. + * <p> + * Note that a <code>SecurityManager</code> check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param fileName The name of the file to read and/or write + * @param mode "r", "rw", "rws", or "rwd" + * + * @exception IllegalArgumentException If <code>mode</code> has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory or + * any other error occurs + */ + public RandomAccessFile (String fileName, String mode) + throws FileNotFoundException + { + this (new File(fileName), mode); + } + + /** + * This method closes the file and frees up all file related system + * resources. Since most operating systems put a limit on how many files + * may be opened at any given time, it is a good idea to close all files + * when no longer needed to avoid hitting this limit + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method returns a <code>FileDescriptor</code> object that + * represents the native file handle for this file. + * + * @return The <code>FileDescriptor</code> object for this file + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method returns the current offset in the file at which the next + * read or write will occur + * + * @return The current file position + * + * @exception IOException If an error occurs + */ + public long getFilePointer () throws IOException + { + return ch.position(); + } + + /** + * This method sets the length of the file to the specified length. + * If the currently length of the file is longer than the specified + * length, then the file is truncated to the specified length (the + * file position is set to the end of file in this case). If the + * current length of the file is shorter than the specified length, + * the file is extended with bytes of an undefined value (the file + * position is unchanged in this case). + * <p> + * The file must be open for write access for this operation to succeed. + * + * @param newLen The new length of the file + * + * @exception IOException If an error occurs + */ + public void setLength (long newLen) throws IOException + { + // FIXME: Extending a file should probably be done by one method call. + + // FileChannel.truncate() can only shrink a file. + // To expand it we need to seek forward and write at least one byte. + if (newLen < length()) + ch.truncate (newLen); + else if (newLen > length()) + { + long pos = getFilePointer(); + seek(newLen - 1); + write(0); + seek(pos); + } + } + + /** + * This method returns the length of the file in bytes + * + * @return The length of the file + * + * @exception IOException If an error occurs + */ + public long length () throws IOException + { + return ch.size(); + } + + /** + * This method reads a single byte of data from the file and returns it + * as an integer. + * + * @return The byte read as an int, or -1 if the end of the file was reached. + * + * @exception IOException If an error occurs + */ + public int read () throws IOException + { + return in.read(); + } + + /** + * This method reads bytes from the file into the specified array. The + * bytes are stored starting at the beginning of the array and up to + * <code>buf.length</code> bytes can be read. + * + * @param buffer The buffer to read bytes from the file into + * + * @return The actual number of bytes read or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer) throws IOException + { + return in.read (buffer); + } + + /** + * This methods reads up to <code>len</code> bytes from the file into the + * specified array starting at position <code>offset</code> into the array. + * + * @param buffer The array to read the bytes into + * @param offset The index into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer, int offset, int len) throws IOException + { + return in.read (buffer, offset, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is <code>false</code> If the byte is non-zero, then + * the value returned is <code>true</code> + * <p> + * This method can read a <code>boolean</code> written by an object + * implementing the + * <code>writeBoolean()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>boolean</code> value read + * + * @exception EOFException If end of file is reached before reading the + * boolean + * @exception IOException If any other error occurs + */ + public final boolean readBoolean () throws IOException + { + return in.readBoolean (); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + * <p> + * This method can read a <code>byte</code> written by an object + * implementing the + * <code>writeByte()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>byte</code> value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final byte readByte () throws IOException + { + return in.readByte (); + } + + /** + * This method reads a Java <code>char</code> value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>char</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> represent + * the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>char</code> in the following manner: + * <p> + * <code>(char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * This method can read a <code>char</code> written by an object + * implementing the + * <code>writeChar()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>char</code> value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final char readChar () throws IOException + { + return in.readChar(); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a <code>logn</code> value from the stream by calling the + * <code>readLong()</code> method in this interface, then + * converts that <code>long</code> + * to a <code>double</code> using the <code>longBitsToDouble</code> + * method in the class <code>java.lang.Double</code> + * <p> + * This method can read a <code>double</code> written by an object + * implementing the + * <code>writeDouble()</code> method in the <code>DataOutput</code> + * interface. + * + * @return The <code>double</code> value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see java.lang.Double + * @see DataOutput + */ + public final double readDouble () throws IOException + { + return in.readDouble (); + } + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an <code>int</code> value from the stream by calling the + * <code>readInt()</code> method in this interface, then converts + * that <code>int</code> + * to a <code>float</code> using the <code>intBitsToFloat</code> method in + * the class <code>java.lang.Float</code> + * <p> + * This method can read a <code>float</code> written by an object + * implementing the + * <code>writeFloat()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>float</code> value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see java.lang.Float + * @see DataOutput + */ + public final float readFloat () throws IOException + { + return in.readFloat(); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer + * + * @param buffer The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer) throws IOException + { + in.readFully(buffer); + } + + /** + * This method reads raw bytes into the passed array <code>buf</code> + * starting + * <code>offset</code> bytes into the buffer. The number of bytes read + * will be + * exactly <code>len</code> Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read <code>len</code> bytes. + * + * @param buffer The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param count The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling + * the buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer, int offset, int count) + throws IOException + { + in.readFully (buffer, offset, count); + } + + /** + * This method reads a Java <code>int</code> value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java <code>int</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte4</code> + * represent the first + * four bytes read from the stream, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF) << 8) + (byte4 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>int</code> written by an object + * implementing the + * <code>writeInt()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>int</code> value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readInt () throws IOException + { + return in.readInt(); + } + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * <code>char</code> + * values by treating the byte read as the low eight bits of the + * <code>char</code> + * and using <code>0</code> as the high eight bits. Because of this, it does + * not support the full 16-bit Unicode character set. + * <p> + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a <code>String</code> + * A line terminator is a byte sequence consisting of either + * <code>\r</code> <code>\n</code> or <code>\r\n</code> These + * termination charaters are + * discarded and are not returned as part of the string. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeLine()</code> method in <code>DataOutput</code> + * + * @return The line read as a <code>String</code> + * + * @exception IOException If an error occurs + * + * @see DataOutput + */ + public final String readLine () throws IOException + { + return in.readLine (); + } + + /** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java <code>long</code> The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> through <code>byte8</code> + * represent the first + * eight bytes read from the stream, they will be + * transformed to an <code>long</code> in the following manner: + * <p> + * <code> + * (long)((((long)byte1 & 0xFF) << 56) + (((long)byte2 & 0xFF) << 48) + + * (((long)byte3 & 0xFF) << 40) + (((long)byte4 & 0xFF) << 32) + + * (((long)byte5 & 0xFF) << 24) + (((long)byte6 & 0xFF) << 16) + + * (((long)byte7 & 0xFF) << 8) + ((long)byte9 & 0xFF)))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an <code>long</code> written by an object + * implementing the + * <code>writeLong()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>long</code> value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final long readLong () throws IOException + { + return in.readLong(); + } + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java <code>short</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to a <code>short</code> in the following manner: + * <p> + * <code>(short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)</code> + * <p> + * The value returned is in the range of -32768 to 32767. + * <p> + * This method can read a <code>short</code> written by an object + * implementing the + * <code>writeShort()</code> method in the <code>DataOutput</code> interface. + * + * @return The <code>short</code> value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final short readShort () throws IOException + { + return in.readShort(); + } + + /** + * This method reads 8 unsigned bits into a Java <code>int</code> value + * from the + * stream. The value returned is in the range of 0 to 255. + * <p> + * This method can read an unsigned byte written by an object implementing + * the <code>writeUnsignedByte()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned bytes value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readUnsignedByte () throws IOException + { + return in.readUnsignedByte(); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java <code>int</code> The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to an <code>int</code> in the following manner: + * <p> + * <code>(int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF))</code> + * <p> + * The value returned is in the range of 0 to 65535. + * <p> + * This method can read an unsigned short written by an object implementing + * the <code>writeUnsignedShort()</code> method in the + * <code>DataOutput</code> interface. + * + * @return The unsigned short value read as a Java <code>int</code> + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + */ + public final int readUnsignedShort () throws IOException + { + return in.readUnsignedShort(); + } + + /** + * This method reads a <code>String</code> from an input stream that + * is encoded in + * a modified UTF-8 format. This format has a leading two byte sequence + * that contains the remaining number of bytes to read. This two byte + * sequence is read using the <code>readUnsignedShort()</code> method of this + * interface. + * <p> + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into <code>char</code> values. + * These <code>char</code> values + * are encoded in the stream using either a one, two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + * <p> + * If the first byte has a high order bit of 0 then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if <code>byte1</code> is the byte read from the stream, it would + * be converted to a <code>char</code> like so: + * <p> + * <code>(char)byte1</code> + * <p> + * If the first byte has <code>110</code> as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> and <code>byte2</code> + * are the first two bytes + * read respectively, and the high order bits of them match the patterns + * which indicate a two byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F))</code> + * <p> + * If the first byte has a <code>1110</code> as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have <code>10</code> as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + * <p> + * As an example, if <code>byte1</code> <code>byte2</code> + * and <code>byte3</code> are the + * three bytes read, and the high order bits of them match the patterns + * which indicate a three byte character encoding, then they would be + * converted to a Java <code>char</code> like so: + * <p> + * <code>(char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F))</code> + * <p> + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of <code>\u0000</code> which is encoded as two bytes. This is + * a modification of the UTF standard used to prevent C language style + * <code>NUL</code> values from appearing in the byte stream. + * <p> + * This method can read data that was written by an object implementing the + * <code>writeUTF()</code> method in <code>DataOutput</code> + * + * @return The <code>String</code> read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final String readUTF () throws IOException + { + return in.readUTF(); + } + + /** + * This method sets the current file position to the specified offset + * from the beginning of the file. Note that some operating systems will + * allow the file pointer to be set past the current end of the file. + * + * @param pos The offset from the beginning of the file at which to set + * the file pointer + * + * @exception IOException If an error occurs + */ + public void seek (long pos) throws IOException + { + ch.position(pos); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * The actual number of bytes skipped is returned. This method will not + * skip any bytes if passed a negative number of bytes to skip. + * + * @param numBytes The requested number of bytes to skip. + * + * @return The number of bytes actually skipped. + * + * @exception IOException If an error occurs. + */ + public int skipBytes (int numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position(); + long newPos = oldPos + numBytes; + long size = ch.size(); + if (newPos > size) + newPos = size; + ch.position(newPos); + return (int) (ch.position() - oldPos); + } + + /** + * This method writes a single byte of data to the file. The file must + * be open for read-write in order for this operation to succeed. + * + * @param oneByte The byte of data to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int oneByte) throws IOException + { + out.write(oneByte); + } + + /** + * This method writes all the bytes in the specified array to the file. + * The file must be open read-write in order for this operation to succeed. + * + * @param buffer The array of bytes to write to the file + */ + public void write (byte[] buffer) throws IOException + { + out.write(buffer); + } + + /** + * This method writes <code>len</code> bytes to the file from the specified + * array starting at index <code>offset</code> into the array. + * + * @param buffer The array of bytes to write to the file + * @param offset The index into the array to start writing file + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] buffer, int offset, int len) throws IOException + { + out.write (buffer, offset, len); + } + + /** + * This method writes a Java <code>boolean</code> to the underlying output + * stream. For a value of <code>true</code>, 1 is written to the stream. + * For a value of <code>false</code>, 0 is written. + * + * @param val The <code>boolean</code> value to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBoolean (boolean val) throws IOException + { + out.writeBoolean(val); + } + + /** + * This method writes a Java <code>byte</code> value to the underlying + * output stream. + * + * @param val The <code>byte</code> to write to the stream, passed + * as an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeByte (int val) throws IOException + { + out.writeByte(val); + } + + /** + * This method writes a Java <code>short</code> to the stream, high byte + * first. This method requires two bytes to encode the value. + * + * @param val The <code>short</code> value to write to the stream, + * passed as an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeShort (int val) throws IOException + { + out.writeShort(val); + } + + /** + * This method writes a single <code>char</code> value to the stream, + * high byte first. + * + * @param val The <code>char</code> value to write, passed as + * an <code>int</code>. + * + * @exception IOException If an error occurs + */ + public final void writeChar (int val) throws IOException + { + out.writeChar(val); + } + + /** + * This method writes a Java <code>int</code> to the stream, high bytes + * first. This method requires four bytes to encode the value. + * + * @param val The <code>int</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeInt (int val) throws IOException + { + out.writeInt(val); + } + + /** + * This method writes a Java <code>long</code> to the stream, high bytes + * first. This method requires eight bytes to encode the value. + * + * @param val The <code>long</code> value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeLong (long val) throws IOException + { + out.writeLong(val); + } + + /** + * This method writes a Java <code>float</code> value to the stream. This + * value is written by first calling the method + * <code>Float.floatToIntBits</code> + * to retrieve an <code>int</code> representing the floating point number, + * then writing this <code>int</code> value to the stream exactly the same + * as the <code>writeInt()</code> method does. + * + * @param val The floating point number to write to the stream. + * + * @exception IOException If an error occurs + * + * @see #writeInt(int) + */ + public final void writeFloat (float val) throws IOException + { + out.writeFloat(val); + } + + /** + * This method writes a Java <code>double</code> value to the stream. This + * value is written by first calling the method + * <code>Double.doubleToLongBits</code> + * to retrieve an <code>long</code> representing the floating point number, + * then writing this <code>long</code> value to the stream exactly the same + * as the <code>writeLong()</code> method does. + * + * @param val The double precision floating point number to write to the + * stream. + * + * @exception IOException If an error occurs + * + * @see #writeLong(long) + */ + public final void writeDouble (double val) throws IOException + { + out.writeDouble(val); + } + + /** + * This method writes all the bytes in a <code>String</code> out to the + * stream. One byte is written for each character in the <code>String</code>. + * The high eight bits of each character are discarded. + * + * @param val The <code>String</code> to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String val) throws IOException + { + out.writeBytes(val); + } + + /** + * This method writes all the characters in a <code>String</code> to the + * stream. There will be two bytes for each character value. The high + * byte of the character will be written first. + * + * @param val The <code>String</code> to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeChars (String val) throws IOException + { + out.writeChars(val); + } + + /** + * This method writes a Java <code>String</code> to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded <code>String</code> not the <code>String</code> length. Next + * come the encoded characters. Each character in the <code>String</code> + * is encoded as either one, two or three bytes. For characters in the + * range of <code>\u0001</code> to <code>\u007F</code>, + * one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of <code>\u0080</code> to <code>\u007FF</code>, two + * bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * <code>\u0000</code>. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * @param val The <code>String</code> to write to the output in UTF format + * + * @exception IOException If an error occurs + */ + public final void writeUTF (String val) throws IOException + { + out.writeUTF(val); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public final synchronized FileChannel getChannel () + { + return ch; + } +} diff --git a/libjava/classpath/java/io/Reader.java b/libjava/classpath/java/io/Reader.java new file mode 100644 index 00000000000..7970d9a2434 --- /dev/null +++ b/libjava/classpath/java/io/Reader.java @@ -0,0 +1,271 @@ +/* Reader.java -- base class of classes that read input as a stream of chars + Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of characters. It provides a common set of methods for + * reading characters from streams. Subclasses implement and extend these + * methods to read characters from a particular input source such as a file + * or network connection. + * + * @author Per Bothner (bothner@cygnus.com) + * @date April 21, 1998. + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Reader +{ + /** + * This is the <code>Object</code> used for synchronizing critical code + * sections. Subclasses should use this variable instead of a + * synchronized method or an explicit synchronization on <code>this</code> + */ + protected Object lock; + + /** + * Unitializes a <code>Reader</code> that will use the object + * itself for synchronization of critical code sections. + */ + protected Reader() + { + this.lock = this; + } + + /** + * Initializes a <code>Reader</code> that will use the specified + * <code>Object</code> for synchronization of critical code sections. + * + * @param lock The <code>Object</code> to use for synchronization + */ + protected Reader(Object lock) + { + this.lock = lock; + } + + /** + * Read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> chars. This method + * can return before reading the number of chars requested. The actual + * number of chars read is returned as an int. A -1 is returned to indicate + * the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling the single char <code>read()</code> method + * in a loop until the desired number of chars are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a chars fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public abstract int read(char buf[], int offset, int count) + throws IOException; + + /** + * Reads chars from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of chars read is + * returned as an int. A -1 is returned to indicate the end of the stream. + * <p> + * This method will block until some data can be read. + * <p> + * This method operates by calling an overloaded read method like so: + * <code>read(buf, 0, buf.length)</code> + * + * @param buf The buffer into which the chars read will be stored. + * + * @return The number of chars read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(char buf[]) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * Reads an char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. + * <p> + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** + * Closes the stream. Any futher attempts to read from the + * stream may generate an <code>IOException</code>. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * Returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + * <p> + * This method always returns <code>false</code> in this class, but + * subclasses can override this method to return <code>true</code> if they + * support mark/reset functionality. + * + * @return <code>true</code> if mark/reset functionality is supported, + * <code>false</code> otherwise + * + */ + public boolean markSupported() + { + return false; + } + + /** + * Marks a position in the input to which the stream can be + * "reset" by calling the <code>reset()</code> method. The parameter + * <code>readlimit</code> is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if <code>mark()</code> is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * <code>reset()</code> method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs such as mark not being + * supported for this class + */ + public void mark(int readLimit) throws IOException + { + throw new IOException("mark not supported"); + } + + /** + * Resets a stream to the point where the <code>mark()</code> + * method was called. Any chars that were read after the mark point was + * set will be re-read during subsequent reads. + * <p> + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } + + /** + * Determines whether or not this stream is ready to be + * read. If it returns <code>false</code> the stream may block if a + * read is attempted, but it is not guaranteed to do so. + * <p> + * This method always returns <code>false</code> in this class + * + * @return <code>true</code> if the stream is ready to be read, + * <code>false</code> otherwise. + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return false; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * <p> + * This method reads and discards chars into a 256 char array until the + * specified number of chars were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this method to provide a more efficient implementation where + * one exists. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (count <= 0) + return 0; + int bsize = count > 1024 ? 1024 : (int) count; + char[] buffer = new char[bsize]; + long todo = count; + synchronized (lock) + { + while (todo > 0) + { + int skipped = read(buffer, 0, bsize > todo ? (int) todo : bsize); + if (skipped <= 0) + break; + todo -= skipped; + } + } + return count - todo; + } +} diff --git a/libjava/classpath/java/io/SequenceInputStream.java b/libjava/classpath/java/io/SequenceInputStream.java new file mode 100644 index 00000000000..7fefe243263 --- /dev/null +++ b/libjava/classpath/java/io/SequenceInputStream.java @@ -0,0 +1,221 @@ +/* SequenceInputStream.java -- Reads multiple input streams in sequence + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.util.Enumeration; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class merges a sequence of multiple <code>InputStream</code>'s in + * order to form a single logical stream that can be read by applications + * that expect only one stream. + * <p> + * The streams passed to the constructor method are read in order until + * they return -1 to indicate they are at end of stream. When a stream + * reports end of stream, it is closed, then the next stream is read. + * When the last stream is closed, the next attempt to read from this + * stream will return a -1 to indicate it is at end of stream. + * <p> + * If this stream is closed prior to all subordinate streams being read + * to completion, all subordinate streams are closed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class SequenceInputStream extends InputStream +{ + /** The handle for the current input stream. */ + private InputStream in; + + /** Secondary input stream; not used if constructed w/ enumeration. */ + private InputStream in2; + + /** The enumeration handle; not used if constructed w/ 2 explicit input streams. */ + private Enumeration e; + + /** + * This method creates a new <code>SequenceInputStream</code> that obtains + * its list of subordinate <code>InputStream</code>s from the specified + * <code>Enumeration</code> + * + * @param e An <code>Enumeration</code> that will return a list of + * <code>InputStream</code>s to read in sequence + */ + public SequenceInputStream(Enumeration e) + { + this.e = e; + in = (InputStream) e.nextElement(); + in2 = null; + } + + /** + * This method creates a new <code>SequenceInputStream</code> that will read + * the two specified subordinate <code>InputStream</code>s in sequence. + * + * @param s1 The first <code>InputStream</code> to read + * @param s2 The second <code>InputStream</code> to read + */ + public SequenceInputStream(InputStream s1, InputStream s2) + { + in = s1; + in2 = s2; + } + + /** + * This method returns the number of bytes than can be read from the + * currently being read subordinate stream before that stream could + * block. Note that it is possible more bytes than this can actually + * be read without the stream blocking. If a 0 is returned, then the + * stream could block on the very next read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + if (in == null) + return 0; + + return in.available(); + } + + /** + * Closes this stream. This will cause any remaining unclosed subordinate + * <code>InputStream</code>'s to be closed as well. Subsequent attempts to + * read from this stream may cause an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + while (in != null) + { + in.close(); + in = getNextStream (); + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. This will only happen when + * all of the subordinate streams have been read. + * <p> + * This method will block until the byte can be read. + * + * @return The byte read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + int ch = -1; + + while (in != null && (ch = in.read()) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returend to indicate the + * end of the stream. This will only happen when all of the subordinate + * streams have been read. + * <p> + * This method will block until at least one byte can be read. + * + * @param b The array into which bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read(byte[] b, int off, int len) throws IOException + { + int ch = -1; + + // The validity of the parameters will be checked by in.read so + // don't bother doing it here. + while (in != null && (ch = in.read(b, off, len)) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This private method is used to get the next <code>InputStream</code> to + * read from. Returns null when no more streams are available. + */ + private InputStream getNextStream() + { + InputStream nextIn = null; + + if (e != null) + { + if (e.hasMoreElements()) + nextIn = (InputStream) e.nextElement(); + } + else + if (in2 != null) + { + nextIn = in2; + in2 = null; + } + + return nextIn; + } +} diff --git a/libjava/classpath/java/io/Serializable.java b/libjava/classpath/java/io/Serializable.java new file mode 100644 index 00000000000..a6d99f6d522 --- /dev/null +++ b/libjava/classpath/java/io/Serializable.java @@ -0,0 +1,54 @@ +/* Serializable.java -- Interface to indicate a class may be serialized + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * Status: Believed complete + */ + +/** + * This interface has no methods. It simply serves to indicate that + * the implementing class may be serialized. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface Serializable +{ +} // interface Serializable diff --git a/libjava/classpath/java/io/SerializablePermission.java b/libjava/classpath/java/io/SerializablePermission.java new file mode 100644 index 00000000000..b5c07e4ec2c --- /dev/null +++ b/libjava/classpath/java/io/SerializablePermission.java @@ -0,0 +1,113 @@ +/* SerializablePermission.java -- Basic permissions related to serialization. + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.BasicPermission; + +/** + * This class models permissions related to serialization. As a subclass + * of <code>BasicPermission</code>, this class has permissions that have + * a name only. There is no associated action list. + * <p> + * There are currently two allowable permission names for this class: + * <ul> + * <li><code>enableSubclassImplementation</code> - Allows a subclass to + * override the default serialization behavior of objects.</li> + * <li><code>enableSubstitution</code> - Allows substitution of one object + * for another during serialization or deserialization.</li> + * </ul> + * + * @see java.security.BasicPermission + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SerializablePermission extends BasicPermission +{ + static final long serialVersionUID = 8537212141160296410L; + + /* + * Class Variables + */ + + private static final String[] legal_names = { "enableSubclassImplementation", + "enableSubstitution" }; + /* + * Constructors + */ + + /** + * This method initializes a new instance of + * <code>SerializablePermission</code> + * that has the specified name. + * + * @param name The name of the permission. + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name) + { + this(name, null); + } + + /** + * This method initializes a new instance of + * <code>SerializablePermission</code> + * that has the specified name and action list. Note that the action list + * is unused in this class. + * + * @param name The name of the permission. + * @param actions The action list (unused). + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name, String actions) + { + super(name, actions); + + for (int i = 0; i < legal_names.length; i++) + if (legal_names[i].equals(name)) + return; + + throw new IllegalArgumentException("Bad permission name: " + name); + } + +} // class SerializablePermission + diff --git a/libjava/classpath/java/io/StreamCorruptedException.java b/libjava/classpath/java/io/StreamCorruptedException.java new file mode 100644 index 00000000000..d24d12150c5 --- /dev/null +++ b/libjava/classpath/java/io/StreamCorruptedException.java @@ -0,0 +1,73 @@ +/* StreamCorruptedException.java -- Error in stream during serialization + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is an error in the data that is + * read from a stream during de-serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class StreamCorruptedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8983558202217591746L; + + /** + * Create an exception without a descriptive error message. + */ + public StreamCorruptedException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public StreamCorruptedException(String message) + { + super(message); + } +} // class StreamCorruptedException diff --git a/libjava/classpath/java/io/StreamTokenizer.java b/libjava/classpath/java/io/StreamTokenizer.java new file mode 100644 index 00000000000..bd7773b1990 --- /dev/null +++ b/libjava/classpath/java/io/StreamTokenizer.java @@ -0,0 +1,708 @@ +/* StreamTokenizer.java -- parses streams of characters into tokens + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This class parses streams of characters into tokens. There are a + * million-zillion flags that can be set to control the parsing, as + * described under the various method headings. + * + * @author Warren Levy (warrenl@cygnus.com) + * @date October 25, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +public class StreamTokenizer +{ + /** A constant indicating that the end of the stream has been read. */ + public static final int TT_EOF = -1; + + /** A constant indicating that the end of the line has been read. */ + public static final int TT_EOL = '\n'; + + /** A constant indicating that a number token has been read. */ + public static final int TT_NUMBER = -2; + + /** A constant indicating that a word token has been read. */ + public static final int TT_WORD = -3; + + /** A constant indicating that no tokens have been read yet. */ + private static final int TT_NONE = -4; + + /** + * Contains the type of the token read resulting from a call to nextToken + * The rules are as follows: + * <ul> + * <li>For a token consisting of a single ordinary character, this is the + * value of that character.</li> + * <li>For a quoted string, this is the value of the quote character</li> + * <li>For a word, this is TT_WORD</li> + * <li>For a number, this is TT_NUMBER</li> + * <li>For the end of the line, this is TT_EOL</li> + * <li>For the end of the stream, this is TT_EOF</li> + * </ul> + */ + public int ttype = TT_NONE; + + /** The String associated with word and string tokens. */ + public String sval; + + /** The numeric value associated with number tokens. */ + public double nval; + + /* Indicates whether end-of-line is recognized as a token. */ + private boolean eolSignificant = false; + + /* Indicates whether word tokens are automatically made lower case. */ + private boolean lowerCase = false; + + /* Indicates whether C++ style comments are recognized and skipped. */ + private boolean slashSlash = false; + + /* Indicates whether C style comments are recognized and skipped. */ + private boolean slashStar = false; + + /* Attribute tables of each byte from 0x00 to 0xFF. */ + private boolean[] whitespace = new boolean[256]; + private boolean[] alphabetic = new boolean[256]; + private boolean[] numeric = new boolean[256]; + private boolean[] quote = new boolean[256]; + private boolean[] comment = new boolean[256]; + + /* The Reader associated with this class. */ + private PushbackReader in; + + /* Indicates if a token has been pushed back. */ + private boolean pushedBack = false; + + /* Contains the current line number of the reader. */ + private int lineNumber = 1; + + /** + * This method reads bytes from an <code>InputStream</code> and tokenizes + * them. For details on how this method operates by default, see + * <code>StreamTokenizer(Reader)</code>. + * + * @param is The <code>InputStream</code> to read from + * + * @deprecated Since JDK 1.1. + */ + public StreamTokenizer(InputStream is) + { + this(new InputStreamReader(is)); + } + + /** + * This method initializes a new <code>StreamTokenizer</code> to read + * characters from a <code>Reader</code> and parse them. The char values + * have their hight bits masked so that the value is treated a character + * in the range of 0x0000 to 0x00FF. + * <p> + * This constructor sets up the parsing table to parse the stream in the + * following manner: + * <ul> + * <li>The values 'A' through 'Z', 'a' through 'z' and 0xA0 through 0xFF + * are initialized as alphabetic</li> + * <li>The values 0x00 through 0x20 are initialized as whitespace</li> + * <li>The values '\'' and '"' are initialized as quote characters</li> + * <li>'/' is a comment character</li> + * <li>Numbers will be parsed</li> + * <li>EOL is not treated as significant</li> + * <li>C and C++ (//) comments are not recognized</li> + * </ul> + * + * @param r The <code>Reader</code> to read chars from + */ + public StreamTokenizer(Reader r) + { + in = new PushbackReader(r); + + whitespaceChars(0x00, 0x20); + wordChars('A', 'Z'); + wordChars('a', 'z'); + wordChars(0xA0, 0xFF); + commentChar('/'); + quoteChar('\''); + quoteChar('"'); + parseNumbers(); + } + + /** + * This method sets the comment attribute on the specified + * character. Other attributes for the character are cleared. + * + * @param ch The character to set the comment attribute for, passed as an int + */ + public void commentChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + comment[ch] = true; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + quote[ch] = false; + } + } + + /** + * This method sets a flag that indicates whether or not the end of line + * sequence terminates and is a token. The defaults to <code>false</code> + * + * @param flag <code>true</code> if EOF is significant, <code>false</code> + * otherwise + */ + public void eolIsSignificant(boolean flag) + { + eolSignificant = flag; + } + + /** + * This method returns the current line number. Note that if the + * <code>pushBack()</code> method is called, it has no effect on the + * line number returned by this method. + * + * @return The current line number + */ + public int lineno() + { + return lineNumber; + } + + /** + * This method sets a flag that indicates whether or not alphabetic + * tokens that are returned should be converted to lower case. + * + * @param flag <code>true</code> to convert to lower case, + * <code>false</code> otherwise + */ + public void lowerCaseMode(boolean flag) + { + lowerCase = flag; + } + + private boolean isWhitespace(int ch) + { + return (ch >= 0 && ch <= 255 && whitespace[ch]); + } + + private boolean isAlphabetic(int ch) + { + return ((ch > 255) || (ch >= 0 && alphabetic[ch])); + } + + private boolean isNumeric(int ch) + { + return (ch >= 0 && ch <= 255 && numeric[ch]); + } + + private boolean isQuote(int ch) + { + return (ch >= 0 && ch <= 255 && quote[ch]); + } + + private boolean isComment(int ch) + { + return (ch >= 0 && ch <= 255 && comment[ch]); + } + + /** + * This method reads the next token from the stream. It sets the + * <code>ttype</code> variable to the appropriate token type and + * returns it. It also can set <code>sval</code> or <code>nval</code> + * as described below. The parsing strategy is as follows: + * <ul> + * <li>Skip any whitespace characters.</li> + * <li>If a numeric character is encountered, attempt to parse a numeric + * value. Leading '-' characters indicate a numeric only if followed by + * another non-'-' numeric. The value of the numeric token is terminated + * by either the first non-numeric encountered, or the second occurrence of + * '-' or '.'. The token type returned is TT_NUMBER and <code>nval</code> + * is set to the value parsed.</li> + * <li>If an alphabetic character is parsed, all subsequent characters + * are read until the first non-alphabetic or non-numeric character is + * encountered. The token type returned is TT_WORD and the value parsed + * is stored in <code>sval</code>. If lower case mode is set, the token + * stored in <code>sval</code> is converted to lower case. The end of line + * sequence terminates a word only if EOL signficance has been turned on. + * The start of a comment also terminates a word. Any character with a + * non-alphabetic and non-numeric attribute (such as white space, a quote, + * or a commet) are treated as non-alphabetic and terminate the word.</li> + * <li>If a comment character is parsed, then all remaining characters on + * the current line are skipped and another token is parsed. Any EOL or + * EOF's encountered are not discarded, but rather terminate the comment.</li> + * <li>If a quote character is parsed, then all characters up to the + * second occurrence of the same quote character are parsed into a + * <code>String</code>. This <code>String</code> is stored as + * <code>sval</code>, but is not converted to lower case, even if lower case + * mode is enabled. The token type returned is the value of the quote + * character encountered. Any escape sequences + * (\b (backspace), \t (HTAB), \n (linefeed), \f (form feed), \r + * (carriage return), \" (double quote), \' (single quote), \\ + * (backslash), \XXX (octal esacpe)) are converted to the appropriate + * char values. Invalid esacape sequences are left in untranslated. + * Unicode characters like ('\ u0000') are not recognized. </li> + * <li>If the C++ comment sequence "//" is encountered, and the parser + * is configured to handle that sequence, then the remainder of the line + * is skipped and another token is read exactly as if a character with + * the comment attribute was encountered.</li> + * <li>If the C comment sequence "/*" is encountered, and the parser + * is configured to handle that sequence, then all characters up to and + * including the comment terminator sequence are discarded and another + * token is parsed.</li> + * <li>If all cases above are not met, then the character is an ordinary + * character that is parsed as a token by itself. The char encountered + * is returned as the token type.</li> + * </ul> + * + * @return The token type + * @exception IOException If an I/O error occurs + */ + public int nextToken() throws IOException + { + if (pushedBack) + { + pushedBack = false; + if (ttype != TT_NONE) + return ttype; + } + + sval = null; + int ch; + + // Skip whitespace. Deal with EOL along the way. + while (isWhitespace(ch = in.read())) + if (ch == '\n' || ch == '\r') + { + lineNumber++; + + // Throw away \n if in combination with \r. + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + if (eolSignificant) + return (ttype = TT_EOL); + } + + if (ch == '/') + if ((ch = in.read()) == '/' && slashSlash) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases + } + else if (ch == '*' && slashStar) + { + while (true) + { + ch = in.read(); + if (ch == '*') + { + if ((ch = in.read()) == '/') + break; + else if (ch != TT_EOF) + in.unread(ch); + } + else if (ch == '\n' || ch == '\r') + { + lineNumber++; + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + } + else if (ch == TT_EOF) + { + break; + } + } + return nextToken(); // Recursive, but not too deep in normal cases + } + else + { + if (ch != TT_EOF) + in.unread(ch); + ch = '/'; + } + + if (ch == TT_EOF) + ttype = TT_EOF; + else if (isNumeric(ch)) + { + boolean isNegative = false; + if (ch == '-') + { + // Read ahead to see if this is an ordinary '-' rather than numeric. + ch = in.read(); + if (isNumeric(ch) && ch != '-') + { + isNegative = true; + } + else + { + if (ch != TT_EOF) + in.unread(ch); + return (ttype = '-'); + } + } + + StringBuffer tokbuf = new StringBuffer(); + tokbuf.append((char) ch); + + int decCount = 0; + while (isNumeric(ch = in.read()) && ch != '-') + if (ch == '.' && decCount++ > 0) + break; + else + tokbuf.append((char) ch); + + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_NUMBER; + try + { + nval = Double.valueOf(tokbuf.toString()).doubleValue(); + } + catch (NumberFormatException _) + { + nval = 0.0; + } + if (isNegative) + nval = -nval; + } + else if (isAlphabetic(ch)) + { + StringBuffer tokbuf = new StringBuffer(); + tokbuf.append((char) ch); + while (isAlphabetic(ch = in.read()) || isNumeric(ch)) + tokbuf.append((char) ch); + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_WORD; + sval = tokbuf.toString(); + if (lowerCase) + sval = sval.toLowerCase(); + } + else if (isComment(ch)) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases. + } + else if (isQuote(ch)) + { + ttype = ch; + StringBuffer tokbuf = new StringBuffer(); + while ((ch = in.read()) != ttype && ch != '\n' && ch != '\r' && + ch != TT_EOF) + { + if (ch == '\\') + switch (ch = in.read()) + { + case 'a': ch = 0x7; + break; + case 'b': ch = '\b'; + break; + case 'f': ch = 0xC; + break; + case 'n': ch = '\n'; + break; + case 'r': ch = '\r'; + break; + case 't': ch = '\t'; + break; + case 'v': ch = 0xB; + break; + case '\n': ch = '\n'; + break; + case '\r': ch = '\r'; + break; + case '\"': + case '\'': + case '\\': + break; + default: + int ch1, nextch; + if ((nextch = ch1 = ch) >= '0' && ch <= '7') + { + ch -= '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7') + { + ch = ch * 8 + nextch - '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7' && + ch1 >= '0' && ch1 <= '3') + { + ch = ch * 8 + nextch - '0'; + nextch = in.read(); + } + } + } + + if (nextch != TT_EOF) + in.unread(nextch); + } + + tokbuf.append((char) ch); + } + + // Throw away matching quote char. + if (ch != ttype && ch != TT_EOF) + in.unread(ch); + + sval = tokbuf.toString(); + } + else + { + ttype = ch; + } + + return ttype; + } + + private void resetChar(int ch) + { + whitespace[ch] = alphabetic[ch] = numeric[ch] = quote[ch] = comment[ch] = + false; + } + + /** + * This method makes the specified character an ordinary character. This + * means that none of the attributes (whitespace, alphabetic, numeric, + * quote, or comment) will be set on this character. This character will + * parse as its own token. + * + * @param ch The character to make ordinary, passed as an int + */ + public void ordinaryChar(int ch) + { + if (ch >= 0 && ch <= 255) + resetChar(ch); + } + + /** + * This method makes all the characters in the specified range, range + * terminators included, ordinary. This means the none of the attributes + * (whitespace, alphabetic, numeric, quote, or comment) will be set on + * any of the characters in the range. This makes each character in this + * range parse as its own token. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void ordinaryChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + resetChar(i); + } + + /** + * This method sets the numeric attribute on the characters '0' - '9' and + * the characters '.' and '-'. + */ + public void parseNumbers() + { + for (int i = 0; i <= 9; i++) + numeric['0' + i] = true; + + numeric['.'] = true; + numeric['-'] = true; + } + + /** + * Puts the current token back into the StreamTokenizer so + * <code>nextToken</code> will return the same value on the next call. + * May cause the lineno method to return an incorrect value + * if lineno is called before the next call to nextToken. + */ + public void pushBack() + { + pushedBack = true; + } + + /** + * This method sets the quote attribute on the specified character. + * Other attributes for the character are cleared. + * + * @param ch The character to set the quote attribute for, passed as an int. + */ + public void quoteChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + quote[ch] = true; + comment[ch] = false; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + } + } + + /** + * This method removes all attributes (whitespace, alphabetic, numeric, + * quote, and comment) from all characters. It is equivalent to calling + * <code>ordinaryChars(0x00, 0xFF)</code>. + * + * @see #ordinaryChars(int, int) + */ + public void resetSyntax() + { + ordinaryChars(0x00, 0xFF); + } + + /** + * This method sets a flag that indicates whether or not "C++" language style + * comments ("//" comments through EOL ) are handled by the parser. + * If this is <code>true</code> commented out sequences are skipped and + * ignored by the parser. This defaults to <code>false</code>. + * + * @param flag <code>true</code> to recognized and handle "C++" style + * comments, <code>false</code> otherwise + */ + public void slashSlashComments(boolean flag) + { + slashSlash = flag; + } + + /** + * This method sets a flag that indicates whether or not "C" language style + * comments (with nesting not allowed) are handled by the parser. + * If this is <code>true</code> commented out sequences are skipped and + * ignored by the parser. This defaults to <code>false</code>. + * + * @param flag <code>true</code> to recognized and handle "C" style comments, + * <code>false</code> otherwise + */ + public void slashStarComments(boolean flag) + { + slashStar = flag; + } + + /** + * This method returns the current token value as a <code>String</code> in + * the form "Token[x], line n", where 'n' is the current line numbers and + * 'x' is determined as follows. + * <p> + * <ul> + * <li>If no token has been read, then 'x' is "NOTHING" and 'n' is 0</li> + * <li>If <code>ttype</code> is TT_EOF, then 'x' is "EOF"</li> + * <li>If <code>ttype</code> is TT_EOL, then 'x' is "EOL"</li> + * <li>If <code>ttype</code> is TT_WORD, then 'x' is <code>sval</code></li> + * <li>If <code>ttype</code> is TT_NUMBER, then 'x' is "n=strnval" where + * 'strnval' is <code>String.valueOf(nval)</code>.</li> + * <li>If <code>ttype</code> is a quote character, then 'x' is + * <code>sval</code></li> + * <li>For all other cases, 'x' is <code>ttype</code></li> + * </ul> + */ + public String toString() + { + String tempstr; + if (ttype == TT_EOF) + tempstr = "EOF"; + else if (ttype == TT_EOL) + tempstr = "EOL"; + else if (ttype == TT_WORD) + tempstr = sval; + else if (ttype == TT_NUMBER) + tempstr = "n=" + nval; + else if (ttype == TT_NONE) + tempstr = "NOTHING"; + else // must be an ordinary char. + tempstr = "\'" + (char) ttype + "\'"; + + return "Token[" + tempstr + "], line " + lineno(); + } + + /** + * This method sets the whitespace attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void whitespaceChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + { + resetChar(i); + whitespace[i] = true; + } + } + + /** + * This method sets the alphabetic attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the alphabetic + * attribute for + * @param hi The high end of the range of values to set the alphabetic + * attribute for + */ + public void wordChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + alphabetic[i] = true; + } +} diff --git a/libjava/classpath/java/io/StringBufferInputStream.java b/libjava/classpath/java/io/StringBufferInputStream.java new file mode 100644 index 00000000000..090881985b8 --- /dev/null +++ b/libjava/classpath/java/io/StringBufferInputStream.java @@ -0,0 +1,187 @@ +/* StringBufferInputStream.java -- Read an String as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. Deprecated in JDK 1.1. + */ + +/** + * This class permits a <code>String</code> to be read as an input stream. + * The low eight bits of each character in the <code>String</code> are the + * bytes that are returned. The high eight bits of each character are + * discarded. + * <p> + * The mark/reset functionality in this class behaves differently than + * normal. The <code>mark()</code> method is always ignored and the + * <code>reset()</code> method always resets in stream to start reading from + * position 0 in the String. Note that since this method does not override + * <code>markSupported()</code> in <code>InputStream</code>, calling that + * method will return <code>false</code>. + * <p> + * Note that this class is deprecated because it does not properly handle + * 16-bit Java characters. It is provided for backwards compatibility only + * and should not be used for new development. The <code>StringReader</code> + * class should be used instead. + * + * @deprecated + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class StringBufferInputStream extends InputStream +{ + /** The String which is the input to this stream. */ + protected String buffer; + + /** Position of the next byte in buffer to be read. */ + protected int pos = 0; + + /** The length of the String buffer. */ + protected int count; + + /** + * Create a new <code>StringBufferInputStream</code> that will read bytes + * from the passed in <code>String</code>. This stream will read from the + * beginning to the end of the <code>String</code>. + * + * @param s The <code>String</code> this stream will read from. + */ + public StringBufferInputStream(String s) + { + buffer = s; + count = s.length(); + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to <code>count - pos</code>. + * + * @return The number of bytes that can be read from this stream before + * blocking, which is all of them + */ + public int available() + { + return count - pos; + } + + /** + * This method reads one byte from the stream. The <code>pos</code> counter + * is advanced to the next byte to be read. The byte read is returned as + * an int in the range of 0-255. If the stream position is already at the + * end of the buffer, no byte is read and a -1 is returned in order to + * indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public int read() + { + if (pos >= count) + return -1; // EOF + + return ((int) buffer.charAt(pos++)) & 0xFF; + } + +/** + * This method reads bytes from the stream and stores them into a caller + * supplied buffer. It starts storing the data at index <code>offset</code> + * into the buffer and attempts to read <code>len</code> bytes. This method + * can return before reading the number of bytes requested if the end of the + * stream is encountered first. The actual number of bytes read is + * returned. If no bytes can be read because the stream is already at + * the end of stream position, a -1 is returned. + * <p> + * This method does not block. + * + * @param b The array into which the bytes read should be stored. + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public int read(byte[] b, int off, int len) + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; // EOF + + int numRead = Math.min(len, count - pos); + if (numRead < 0) + return 0; + + buffer.getBytes(pos, pos + numRead, b, off); + pos += numRead; + return numRead; + } + + /** + * This method sets the read position in the stream to the beginning + * setting the <code>pos</code> variable equal to 0. Note that this differs + * from the common implementation of the <code>reset()</code> method. + */ + public void reset() + { + pos = 0; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the <code>pos</code> value by the + * specified number of bytes. It this would exceed the length of the + * buffer, then only enough bytes are skipped to position the stream at + * the end of the buffer. The actual number of bytes skipped is returned. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public long skip(long n) + { + if (n < 0) + return 0L; + + long actualSkip = Math.min(n, count - pos); + pos += actualSkip; + return actualSkip; + } +} diff --git a/libjava/classpath/java/io/StringReader.java b/libjava/classpath/java/io/StringReader.java new file mode 100644 index 00000000000..7e4e7d84f62 --- /dev/null +++ b/libjava/classpath/java/io/StringReader.java @@ -0,0 +1,209 @@ +/* StringReader.java -- permits a String to be read as a character input stream + Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct + */ + +/** + * This class permits a <code>String</code> to be read as a character + * input stream. + * <p> + * The mark/reset functionality in this class behaves differently than + * normal. If no mark has been set, then calling the <code>reset()</code> + * method rewinds the read pointer to the beginning of the <code>String</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date October 19, 1998. + */ +public class StringReader extends Reader +{ + /* A String provided by the creator of the stream. */ + private String buf; + + /* Position of the next char in buf to be read. */ + private int pos; + + /* The currently marked position in the stream. */ + private int markedPos; + + /* The index in buf one greater than the last valid character. */ + private int count; + + /** + * Create a new <code>StringReader</code> that will read chars from the + * passed in <code>String</code>. This stream will read from the beginning + * to the end of the <code>String</code>. + * + * @param buffer The <code>String</code> this stream will read from. + */ + public StringReader(String buffer) + { + super(); + buf = buffer; + + count = buffer.length(); + markedPos = pos = 0; + } + + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // readAheadLimit is ignored per Java Class Lib. book, p. 1692. + markedPos = pos; + } + } + + public boolean markSupported() + { + return true; + } + + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < count) + return ((int) buf.charAt(pos++)) & 0xFFFF; + return -1; + } + } + + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int lastChar = Math.min(count, pos + len); + buf.getChars(pos, lastChar, b, off); + int numChars = lastChar - pos; + pos = lastChar; + return numChars; + } + } + + /** + * This method determines if the stream is ready to be read. This class + * is always ready to read and so always returns <code>true</code>, unless + * close() has previously been called in which case an IOException is + * thrown. + * + * @return <code>true</code> to indicate that this object is ready to be read. + * @exception IOException If the stream is closed. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return true; + } + + /** + * Sets the read position in the stream to the previously + * marked position or to 0 (i.e., the beginning of the stream) if the mark + * has not already been set. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the <code>pos</code> value by + * the specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} + diff --git a/libjava/classpath/java/io/StringWriter.java b/libjava/classpath/java/io/StringWriter.java new file mode 100644 index 00000000000..a1e9aeb6bd2 --- /dev/null +++ b/libjava/classpath/java/io/StringWriter.java @@ -0,0 +1,191 @@ +/* StringWriter.java -- Writes bytes to a StringBuffer + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// Wow is this a dumb class. CharArrayWriter can do all this and +// more. I would redirect all calls to one in fact, but the javadocs say +// use a StringBuffer so I will comply. + +/** + * This class writes chars to an internal <code>StringBuffer</code> that + * can then be used to retrieve a <code>String</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class StringWriter extends Writer +{ + /** + * This is the default size of the buffer if the user doesn't specify it. + * @specnote The JCL Volume 1 says that 16 is the default size. + */ + private static final int DEFAULT_BUFFER_SIZE = 16; + + /** + * This method closes the stream. The contents of the internal buffer + * can still be retrieved, but future writes are not guaranteed to work. + * + * @exception IOException If an error orrurs. + */ + public void close () throws IOException + { + // JCL says this does nothing. This seems to violate the Writer + // contract, in that other methods should still throw an + // IOException after a close. Still, we just follow JCL. + } + + /** + * This method flushes any buffered characters to the underlying output. + * It does nothing in this class. + */ + public void flush () + { + } + + /** + * This method returns the <code>StringBuffer</code> object that this + * object is writing to. Note that this is the actual internal buffer, so + * any operations performed on it will affect this stream object. + * + * @return The <code>StringBuffer</code> object being written to + */ + public StringBuffer getBuffer () + { + return buffer; + } + + /** + * This method initializes a new <code>StringWriter</code> to write to a + * <code>StringBuffer</code> initially sized to a default size of 16 + * chars. + */ + public StringWriter () + { + this (DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new <code>StringWriter</code> to write to a + * <code>StringBuffer</code> with the specified initial size. + * + * @param size The initial size to make the <code>StringBuffer</code> + */ + public StringWriter (int size) + { + super (); + buffer = new StringBuffer (size); + lock = buffer; + } + + /** + * This method returns the contents of the internal <code>StringBuffer</code> + * as a <code>String</code>. + * + * @return A <code>String</code> representing the chars written to + * this stream. + */ + public String toString () + { + return buffer.toString(); + } + + /** + * This method writes a single character to the output, storing it in + * the internal buffer. + * + * @param oneChar The <code>char</code> to write, passed as an int. + */ + public void write (int oneChar) + { + buffer.append((char) (oneChar & 0xFFFF)); + } + + /** + * This method writes <code>len</code> chars from the specified + * array starting at index <code>offset</code> in that array to this + * stream by appending the chars to the end of the internal buffer. + * + * @param chars The array of chars to write + * @param offset The index into the array to start writing from + * @param len The number of chars to write + */ + public void write (char[] chars, int offset, int len) + { + buffer.append(chars, offset, len); + } + + /** + * This method writes the characters in the specified <code>String</code> + * to the stream by appending them to the end of the internal buffer. + * + * @param str The <code>String</code> to write to the stream. + */ + public void write (String str) + { + buffer.append(str); + } + + /** + * This method writes out <code>len</code> characters of the specified + * <code>String</code> to the stream starting at character position + * <code>offset</code> into the stream. This is done by appending the + * characters to the internal buffer. + * + * @param str The <code>String</code> to write characters from + * @param offset The character position to start writing from + * @param len The number of characters to write. + */ + public void write (String str, int offset, int len) + { +// char[] tmpbuf = new char[len]; +// str.getChars(offset, offset+len, tmpbuf, 0); +// buf.append(tmpbuf, 0, tmpbuf.length); + // This implementation assumes that String.substring is more + // efficient than using String.getChars and copying the data + // twice. For libgcj, this is true. For Classpath, it is not. + // FIXME. + buffer.append(str.substring(offset, offset + len)); + } + + /** + * This is the <code>StringBuffer</code> that we use to store bytes that + * are written. + */ + private StringBuffer buffer; +} diff --git a/libjava/classpath/java/io/SyncFailedException.java b/libjava/classpath/java/io/SyncFailedException.java new file mode 100644 index 00000000000..c514c44f23f --- /dev/null +++ b/libjava/classpath/java/io/SyncFailedException.java @@ -0,0 +1,66 @@ +/* SyncFailedException.java -- a file sync failed + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * Thrown when a file synchronization fails. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see FileDescriptor#sync() + * @since 1.1 + * @status updated to 1.4 + */ +public class SyncFailedException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2353342684412443330L; + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public SyncFailedException(String message) + { + super(message); + } +} // class SyncFailedException diff --git a/libjava/classpath/java/io/UTFDataFormatException.java b/libjava/classpath/java/io/UTFDataFormatException.java new file mode 100644 index 00000000000..6bb76aebdfe --- /dev/null +++ b/libjava/classpath/java/io/UTFDataFormatException.java @@ -0,0 +1,74 @@ +/* UTFDataFormatException.java -- thrown on bad format in UTF data + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * When reading a UTF string from an input stream, this exception is thrown + * to indicate that the data read is invalid. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see DataInput + * @see DataInputStream#readUTF(DataInput) + * @status updated to 1.4 + */ +public class UTFDataFormatException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 420743449228280612L; + + /** + * Create a new UTFDataFormatException without a descriptive error message. + */ + public UTFDataFormatException() + { + } + + /** + * Create a new UTFDataFormatException with a descriptive error message. + * + * @param message the descriptive error message + */ + public UTFDataFormatException(String message) + { + super(message); + } +} // class UTFDataFormatException diff --git a/libjava/classpath/java/io/UnsupportedEncodingException.java b/libjava/classpath/java/io/UnsupportedEncodingException.java new file mode 100644 index 00000000000..cf0ab64cb89 --- /dev/null +++ b/libjava/classpath/java/io/UnsupportedEncodingException.java @@ -0,0 +1,73 @@ +/* UnsupportedEncodingException.java -- the requested encoding isn't supported + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the requested character encoding is + * not supported. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class UnsupportedEncodingException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4274276298326136670L; + + /** + * Create an exception without a descriptive error message. + */ + public UnsupportedEncodingException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public UnsupportedEncodingException(String message) + { + super(message); + } +} // class UnsupportedEncodingException diff --git a/libjava/classpath/java/io/WriteAbortedException.java b/libjava/classpath/java/io/WriteAbortedException.java new file mode 100644 index 00000000000..f051dc975c8 --- /dev/null +++ b/libjava/classpath/java/io/WriteAbortedException.java @@ -0,0 +1,109 @@ +/* WriteAbortedException.java -- wraps an exception thrown while writing + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when another ObjectStreamException occurs during + * a serialization read or write. The stream is reset, and deserialized + * objects are discarded. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class WriteAbortedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3326426625597282442L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Exception detail; + + /** + * Create a new WriteAbortedException with a specified message and + * cause. + * + * @param msg the message + * @param detail the cause + */ + public WriteAbortedException(String msg, Exception detail) + { + super(msg); + initCause(detail); + this.detail = detail; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * <code>super.getMessage() + (detail == null ? "" : "; " + detail)</code>. + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; " + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the <code>detail</code> field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if <code>detail == this</code>, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} // class WriteAbortedException diff --git a/libjava/classpath/java/io/Writer.java b/libjava/classpath/java/io/Writer.java new file mode 100644 index 00000000000..f153e31cfed --- /dev/null +++ b/libjava/classpath/java/io/Writer.java @@ -0,0 +1,192 @@ +/* Writer.java -- Base class for character output streams + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of chars. It provides a common set of methods + * for writing chars to stream. Subclasses implement and/or extend these + * methods to write chars in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class Writer +{ + /** + * This is the object used to synchronize criticial code sections for + * thread safety. Subclasses should use this field instead of using + * synchronized methods or explicity synchronizations on <code>this</code> + */ + protected Object lock; + + /** + * This is the default no-argument constructor for this class. This method + * will set up the class to synchronize criticial sections on itself. + */ + protected Writer() + { + lock = this; + } + + /** + * This method initializes a <code>Writer</code> that will synchronize + * on the specified <code>Object</code>. + * + * @param lock The <code>Object</code> to use for synchronizing critical + * sections. Must not be null. + */ + protected Writer(Object lock) + { + if (lock == null) + throw new NullPointerException(); + + this.lock = lock; + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + * + * @exception IOException If an error occurs + */ + public abstract void flush() throws IOException; + + /** + * This method closes the stream. Any internal or native resources + * associated + * with this stream are freed. Any subsequent attempt to access the stream + * might throw an exception. + * <p> + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * This method writes a single char to the output stream. + * + * @param b The char to be written to the output stream, passed as an int + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + char[] buf = new char[1]; + + buf[0] = (char)b; + write(buf, 0, buf.length); + } + + /** + * This method all the writes char from the passed array to the output + * stream. This method is equivalent to + * <code>write(buf, 0, buf.length)</code> which + * is exactly how it is implemented in this class. + * + * @param buf The array of char to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf) throws IOException + { + write(buf, 0, buf.length); + } + + /** + * This method writes <code>len</code> char from the specified array + * <code>buf</code> starting at index <code>offset</code> into the array. + * <p> + * Subclasses must provide an implementation of this abstract method. + * + * @param buf The array of char to write from + * @param offset The index into the array to start writing from + * @param len The number of char to write + * + * @exception IOException If an error occurs + */ + public abstract void write(char[] buf, int offset, int len) + throws IOException; + + /** + * This method writes all the characters in a <code>String</code> to the + * output. + * + * @param str The <code>String</code> whose chars are to be written. + * + * @exception IOException If an error occurs + */ + public void write(String str) throws IOException + { + write(str, 0, str.length()); + } + + /** + * This method writes <code>len</code> chars from the <code>String</code> + * starting at position <code>offset</code>. + * + * @param str The <code>String</code> that is to be written + * @param offset The character offset into the <code>String</code> to start + * writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + // FIXME - for libgcj re-write using native code to not require + // copied buffer. + char[] buf = new char[len]; + + str.getChars(offset, offset + len, buf, 0); + write(buf, 0, len); + } + +} // class Writer + diff --git a/libjava/classpath/java/io/class-dependencies.conf b/libjava/classpath/java/io/class-dependencies.conf new file mode 100644 index 00000000000..633bb174941 --- /dev/null +++ b/libjava/classpath/java/io/class-dependencies.conf @@ -0,0 +1,100 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +java/io/File: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/FileDescriptor: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +java/io/FileInputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V + +java/io/FileOutputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +java/io/ObjectInputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/ObjectOutputStream: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/lang/SecurityManager.currentClassLoader()Ljava/lang/ClassLoader; \ + java/lang/IllegalArgumentException.<init>(Ljava/lang/String;)V + +java/io/RandomAccessFile: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/FileNotFoundException.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V + +# end of file diff --git a/libjava/classpath/java/io/package.html b/libjava/classpath/java/io/package.html new file mode 100644 index 00000000000..02e1c5bc365 --- /dev/null +++ b/libjava/classpath/java/io/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.io package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.io</title></head> + +<body> +<p>Classes for manipulating character and byte streams and files.</p> + +</body> +</html> diff --git a/libjava/classpath/java/lang/AbstractMethodError.java b/libjava/classpath/java/lang/AbstractMethodError.java new file mode 100644 index 00000000000..b9eb622ee8e --- /dev/null +++ b/libjava/classpath/java/lang/AbstractMethodError.java @@ -0,0 +1,75 @@ +/* AbstractMethodError.java -- thrown if an abstract method is invoked + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>AbstractMethodError</code> is thrown when an application attempts + * to access an abstract method. Compilers typically detect this error, but + * it can be thrown at run time if the definition of a class has changed + * since the application was last compiled. This can also occur when + * reflecting on methods. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class AbstractMethodError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1654391082989018462L; + + /** + * Create an error without a message. + */ + public AbstractMethodError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public AbstractMethodError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Appendable.java b/libjava/classpath/java/lang/Appendable.java new file mode 100644 index 00000000000..c0966773355 --- /dev/null +++ b/libjava/classpath/java/lang/Appendable.java @@ -0,0 +1,122 @@ +/* Appendable.java -- Something to which characters can be appended + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.IOException; + +/** + * <p> + * An <code>Appendable</code> object is one to which a sequence of Unicode + * characters can be added. The appended characters must be valid Unicode + * characters, and may include supplementary characters, composed of multiple + * 16-bit <code>char</code> values. + * </p> + * <p> + * The behaviour of the <code>Appendable</code> object is heavily dependent + * on the particular implementation being used. Some implementations may be + * thread-safe, while others may not. Likewise, some implementing classes + * may produce errors which aren't propogated to the invoking class, due + * to differences in the error handling used. + * </p> + * <p> + * <strong>Note</strong>: implementation of this interface is required for + * any class that wishes to receive data from a <code>Formatter</code> + * instance. + * </p> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Appendable +{ + + /** + * Appends the Unicode character, c, to this <code>Appendable</code> + * object. + * + * @param c the character to append. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + */ + Appendable append(char c) + throws IOException; + + /** + * Appends the specified sequence of Unicode characters to this + * <code>Appendable</code> object. The entire sequence may not + * be appended, if constrained by the underlying implementation. + * For example, a buffer may reach its size limit before the entire + * sequence is appended. + * + * @param seq the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + */ + Appendable append(CharSequence seq) + throws IOException; + + /** + * Appends the specified subsequence of Unicode characters to this + * <code>Appendable</code> object, starting and ending at the specified + * positions within the sequence. The entire sequence may not + * be appended, if constrained by the underlying implementation. + * For example, a buffer may reach its size limit before the entire + * sequence is appended. The behaviour of this method matches the + * behaviour of <code>append(seq.subSequence(start,end))</code> when + * the sequence is not null. + * + * @param seq the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @param start the index of the first Unicode character to use from + * the sequence. + * @param end the index of the last Unicode character to use from the + * sequence. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + * @throws IndexOutOfBoundsException if either of the indices are negative, + * the start index occurs after the end index, or the end index is + * beyond the end of the sequence. + */ + Appendable append(CharSequence seq, int start, int end) + throws IOException; + +} diff --git a/libjava/classpath/java/lang/ArithmeticException.java b/libjava/classpath/java/lang/ArithmeticException.java new file mode 100644 index 00000000000..5acea43533b --- /dev/null +++ b/libjava/classpath/java/lang/ArithmeticException.java @@ -0,0 +1,77 @@ +/* ArithmeticException.java -- exception thrown to indicate conditions + like divide by zero. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a math error has occured, such as trying to divide an + * integer by zero. For example:<br> + * <pre> + * int i = 0; + * int j = 2 / i; + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArithmeticException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 2256477558314496007L; + + /** + * Create an exception without a message. + */ + public ArithmeticException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArithmeticException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java b/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java new file mode 100644 index 00000000000..371623bdaaa --- /dev/null +++ b/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java @@ -0,0 +1,87 @@ +/* ArrayIndexOutOfBoundsException.java -- exception thrown when accessing + an illegal index. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when attempting to access a position outside the valid range of + * an array. For example:<br> + * <pre> + * int[] i = { 1 }; + * i[1] = 2; + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5116101128118950844L; + + /** + * Create an exception without a message. + */ + public ArrayIndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArrayIndexOutOfBoundsException(String s) + { + super(s); + } + + /** + * Create an exception indicating the illegal index. + * + * @param index the invalid index + */ + public ArrayIndexOutOfBoundsException(int index) + { + super("Array index out of range: " + index); + } +} diff --git a/libjava/classpath/java/lang/ArrayStoreException.java b/libjava/classpath/java/lang/ArrayStoreException.java new file mode 100644 index 00000000000..042e78c5515 --- /dev/null +++ b/libjava/classpath/java/lang/ArrayStoreException.java @@ -0,0 +1,77 @@ +/* ArrayStoreException.java -- exception thrown to when trying to store an + object into an array of a different type. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when trying to store an object of the wrong runtime type in an + * array. For example:<br> + * <pre> + * Object[] o = new Integer[1]; + * o[0] = "oops"; + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArrayStoreException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4522193890499838241L; + + /** + * Create an exception without a message. + */ + public ArrayStoreException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArrayStoreException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/AssertionError.java b/libjava/classpath/java/lang/AssertionError.java new file mode 100644 index 00000000000..778eb583051 --- /dev/null +++ b/libjava/classpath/java/lang/AssertionError.java @@ -0,0 +1,148 @@ +/* AssertionError.java -- indication of a failed assertion + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An assertion error normally occurs as a result of the <code>assert</code> + * statement added in JDK 1.4, to indicate that an assertion failed. There + * are enough constructors to ensure that + * <code>new AssertionError(<em>expression</em>)</code> will work for all + * expressions, regardless of type, as if the error message were given by + * the string <code>"" + <em>expression</em></code>. This extends Error, + * because you usually do not want to inadvertently trap an assertion failure. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class AssertionError extends Error +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -5013299493970297370L; + + /** + * Construct an AssertionError with no detail message. + */ + public AssertionError() + { + } + + /** + * Construct an AssertionError with the string conversion of the given + * object as its error message. If the object is a Throwable, it is also + * set as the cause of this error. + * + * @param msg the source of the error message + * @see Throwable#getCause() + */ + public AssertionError(Object msg) + { + super("" + msg); + if (msg instanceof Throwable) + initCause((Throwable) msg); + } + + /** + * Construct an AssertionError with the string conversion of the given + * boolean as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(boolean msg) + { + super(msg ? "true" : "false"); + } + + /** + * Construct an AssertionError with the string conversion of the given + * char as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(char msg) + { + super(String.valueOf(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * int as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(int msg) + { + super(Integer.toString(msg, 10)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * long as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(long msg) + { + super(Long.toString(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * float as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(float msg) + { + super(Float.toString(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * double as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(double msg) + { + super(Double.toString(msg)); + } +} diff --git a/libjava/classpath/java/lang/Boolean.java b/libjava/classpath/java/lang/Boolean.java new file mode 100644 index 00000000000..b6910280e6b --- /dev/null +++ b/libjava/classpath/java/lang/Boolean.java @@ -0,0 +1,224 @@ +/* Boolean.java -- object wrapper for boolean + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * Instances of class <code>Boolean</code> represent primitive + * <code>boolean</code> values. + * + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public final class Boolean implements Serializable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = -3665804199014368530L; + + /** + * This field is a <code>Boolean</code> object representing the + * primitive value <code>true</code>. This instance is returned + * by the static <code>valueOf()</code> methods if they return + * a <code>Boolean</code> representing <code>true</code>. + */ + public static final Boolean TRUE = new Boolean(true); + + /** + * This field is a <code>Boolean</code> object representing the + * primitive value <code>false</code>. This instance is returned + * by the static <code>valueOf()</code> methods if they return + * a <code>Boolean</code> representing <code>false</code>. + */ + public static final Boolean FALSE = new Boolean(false); + + /** + * The primitive type <code>boolean</code> is represented by this + * <code>Class</code> object. + * + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('Z'); + + /** + * The immutable value of this Boolean. + * @serial the wrapped value + */ + private final boolean value; + + /** + * Create a <code>Boolean</code> object representing the value of the + * argument <code>value</code>. In general the use of the static + * method <code>valueof(boolean)</code> is more efficient since it will + * not create a new object. + * + * @param value the primitive value of this <code>Boolean</code> + * @see #valueOf(boolean) + */ + public Boolean(boolean value) + { + this.value = value; + } + + /** + * Creates a <code>Boolean</code> object representing the primitive + * <code>true</code> if and only if <code>s</code> matches + * the string "true" ignoring case, otherwise the object will represent + * the primitive <code>false</code>. In general the use of the static + * method <code>valueof(String)</code> is more efficient since it will + * not create a new object. + * + * @param s the <code>String</code> representation of <code>true</code> + * or false + */ + public Boolean(String s) + { + value = "true".equalsIgnoreCase(s); + } + + /** + * Return the primitive <code>boolean</code> value of this + * <code>Boolean</code> object. + * + * @return true or false, depending on the value of this Boolean + */ + public boolean booleanValue() + { + return value; + } + + /** + * Returns the Boolean <code>TRUE</code> if the given boolean is + * <code>true</code>, otherwise it will return the Boolean + * <code>FALSE</code>. + * + * @param b the boolean to wrap + * @return the wrapper object + * @see #TRUE + * @see #FALSE + * @since 1.4 + */ + public static Boolean valueOf(boolean b) + { + return b ? TRUE : FALSE; + } + + /** + * Returns the Boolean <code>TRUE</code> if and only if the given + * String is equal, ignoring case, to the the String "true", otherwise + * it will return the Boolean <code>FALSE</code>. + * + * @param s the string to convert + * @return a wrapped boolean from the string + */ + public static Boolean valueOf(String s) + { + return "true".equalsIgnoreCase(s) ? TRUE : FALSE; + } + + /** + * Returns "true" if the value of the give boolean is <code>true</code> and + * returns "false" if the value of the given boolean is <code>false</code>. + * + * @param b the boolean to convert + * @return the string representation of the boolean + * @since 1.4 + */ + public static String toString(boolean b) + { + return b ? "true" : "false"; + } + + /** + * Returns "true" if the value of this object is <code>true</code> and + * returns "false" if the value of this object is <code>false</code>. + * + * @return the string representation of this + */ + public String toString() + { + return value ? "true" : "false"; + } + + /** + * Returns the integer <code>1231</code> if this object represents + * the primitive <code>true</code> and the integer <code>1237</code> + * otherwise. + * + * @return the hash code + */ + public int hashCode() + { + return value ? 1231 : 1237; + } + + /** + * If the <code>obj</code> is an instance of <code>Boolean</code> and + * has the same primitive value as this object then <code>true</code> + * is returned. In all other cases, including if the <code>obj</code> + * is <code>null</code>, <code>false</code> is returned. + * + * @param obj possibly an instance of any <code>Class</code> + * @return true if <code>obj</code> equals this + */ + public boolean equals(Object obj) + { + return obj instanceof Boolean && value == ((Boolean) obj).value; + } + + /** + * If the value of the system property <code>name</code> matches + * "true" ignoring case then the function returns <code>true</code>. + * + * @param name the property name to look up + * @return true if the property resulted in "true" + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + */ + public static boolean getBoolean(String name) + { + if (name == null || "".equals(name)) + return false; + return "true".equalsIgnoreCase(System.getProperty(name)); + } +} diff --git a/libjava/classpath/java/lang/Byte.java b/libjava/classpath/java/lang/Byte.java new file mode 100644 index 00000000000..338e2167aa1 --- /dev/null +++ b/libjava/classpath/java/lang/Byte.java @@ -0,0 +1,357 @@ +/* Byte.java -- object wrapper for byte + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class <code>Byte</code> represent primitive <code>byte</code> + * values. + * + * Additionally, this class provides various helper functions and variables + * useful to bytes. + * + * @author Paul Fisher + * @author John Keiser + * @author Per Bothner + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public final class Byte extends Number implements Comparable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7183698231559129828L; + + /** + * The minimum value a <code>byte</code> can represent is -128 (or + * -2<sup>7</sup>). + */ + public static final byte MIN_VALUE = -128; + + /** + * The maximum value a <code>byte</code> can represent is 127 (or + * 2<sup>7</sup> - 1). + */ + public static final byte MAX_VALUE = 127; + + /** + * The primitive type <code>byte</code> is represented by this + * <code>Class</code> object. + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('B'); + + /** + * The immutable value of this Byte. + * + * @serial the wrapped byte + */ + private final byte value; + + /** + * Create a <code>Byte</code> object representing the value of the + * <code>byte</code> argument. + * + * @param value the value to use + */ + public Byte(byte value) + { + this.value = value; + } + + /** + * Create a <code>Byte</code> object representing the value specified + * by the <code>String</code> argument + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain a byte + * @see #valueOf(String) + */ + public Byte(String s) + { + value = parseByte(s, 10); + } + + /** + * Converts the <code>byte</code> to a <code>String</code> and assumes + * a radix of 10. + * + * @param b the <code>byte</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toString(byte b) + { + return String.valueOf(b); + } + + /** + * Converts the specified <code>String</code> into a <code>byte</code>. + * This function assumes a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the <code>byte</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>byte</code> + * @see #parseByte(String) + */ + public static byte parseByte(String s) + { + return parseByte(s, 10); + } + + /** + * Converts the specified <code>String</code> into an <code>int</code> + * using the specified radix (base). The string must not be <code>null</code> + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * <code>Character.digit(d, radix)</code>, and must be in the range + * <code>0</code> to <code>radix - 1</code>. Finally, the result must be + * within <code>MIN_VALUE</code> to <code>MAX_VALUE</code>, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> argument converted to <code>byte</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>byte</code> + */ + public static byte parseByte(String s, int radix) + { + int i = Integer.parseInt(s, radix, false); + if ((byte) i != i) + throw new NumberFormatException(); + return (byte) i; + } + + /** + * Creates a new <code>Byte</code> object using the <code>String</code> + * and specified radix (base). + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to convert with + * @return the new <code>Byte</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>byte</code> + * @see #parseByte(String, int) + */ + public static Byte valueOf(String s, int radix) + { + return new Byte(parseByte(s, radix)); + } + + /** + * Creates a new <code>Byte</code> object using the <code>String</code>, + * assuming a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the new <code>Byte</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>byte</code> + * @see #Byte(String) + * @see #parseByte(String) + */ + public static Byte valueOf(String s) + { + return new Byte(parseByte(s, 10)); + } + + /** + * Convert the specified <code>String</code> into a <code>Byte</code>. + * The <code>String</code> may represent decimal, hexadecimal, or + * octal numbers. + * + * <p>The extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> ] <em>DecimalNumber</em> ) + * | ( [ <code>-</code> ] ( <code>0x</code> | <code>0X</code> + * | <code>#</code> ) { <em>HexDigit</em> }+ ) + * | ( [ <code>-</code> ] <code>0</code> { <em>OctalDigit</em> } ) + * <em>DecimalNumber</em>: + * <em>DecimalDigit except '0'</em> { <em>DecimalDigit</em> } + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 10) has value 0 to 9</em> + * <em>OctalDigit</em>: + * <em>Character.digit(d, 8) has value 0 to 7</em> + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 16) has value 0 to 15</em> + * </pre> + * Finally, the value must be in the range <code>MIN_VALUE</code> to + * <code>MAX_VALUE</code>, or an exception is thrown. + * + * @param s the <code>String</code> to interpret + * @return the value of the String as a <code>Byte</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>byte</code> + * @throws NullPointerException if <code>s</code> is null + * @see Integer#decode(String) + */ + public static Byte decode(String s) + { + int i = Integer.parseInt(s, 10, true); + if ((byte) i != i) + throw new NumberFormatException(); + return new Byte((byte) i); + } + + /** + * Return the value of this <code>Byte</code>. + * + * @return the byte value + */ + public byte byteValue() + { + return value; + } + + /** + * Return the value of this <code>Byte</code> as a <code>short</code>. + * + * @return the short value + */ + public short shortValue() + { + return value; + } + + /** + * Return the value of this <code>Byte</code> as an <code>int</code>. + * + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this <code>Byte</code> as a <code>long</code>. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this <code>Byte</code> as a <code>float</code>. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this <code>Byte</code> as a <code>double</code>. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the <code>Byte</code> value to a <code>String</code> and + * assumes a radix of 10. + * + * @return the <code>String</code> representation of this <code>Byte</code> + * @see Integer#toString() + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. <code>Byte</code>'s hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Byte</code> and represents the same byte value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Byte && value == ((Byte) obj).value; + } + + /** + * Compare two Bytes numerically by comparing their <code>byte</code> values. + * The result is positive if the first is greater, negative if the second + * is greater, and 0 if the two are equal. + * + * @param b the Byte to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Byte b) + { + return value - b.value; + } + + /** + * Behaves like <code>compareTo(Byte)</code> unless the Object + * is not a <code>Byte</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not a <code>Byte</code> + * @see #compareTo(Byte) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((Byte) o); + } +} diff --git a/libjava/classpath/java/lang/CharSequence.java b/libjava/classpath/java/lang/CharSequence.java new file mode 100644 index 00000000000..5c014e173a8 --- /dev/null +++ b/libjava/classpath/java/lang/CharSequence.java @@ -0,0 +1,99 @@ +/* CharSequence.java -- Anything that has an indexed sequence of chars + Copyright (C) 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * General functions on a sequence of chars. This interface is implemented + * by <code>String</code>, <code>StringBuffer</code> and + * <code>CharBuffer</code> to give a uniform way to get chars at a certain + * index, the number of characters in the sequence and a subrange of the + * chars. Indexes start at 0 and the last index is <code>length()-1</code>. + * + * <p>Even when classes implement this interface they are not always + * exchangeble because they might implement their compare, equals or hash + * function differently. This means that in general one should not use a + * <code>CharSequence</code> as keys in collections since two sequences + * with the same chars at the same indexes with the same length might not + * have the same hash code, be equal or be comparable since the are + * represented by different classes. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.4 + * @status updated to 1.4 + */ +public interface CharSequence +{ + /** + * Returns the character at the given index. + * + * @param i the index to retrieve from + * @return the character at that location + * @throws IndexOutOfBoundsException if i < 0 || i >= length() - 1 + */ + char charAt(int i); + + /** + * Returns the length of the sequence. This is the number of 16-bit + * characters in the sequence, which may differ from the length of the + * underlying encoding. + * + * @return the sequence length + */ + int length(); + + /** + * Returns a new <code>CharSequence</code> of the indicated range. + * + * @param begin the start index (inclusive) + * @param end the end index (exclusive) + * @return a subsequence of this + * @throws IndexOutOfBoundsException if begin > end || begin < 0 || + * end > length() + */ + CharSequence subSequence(int begin, int end); + + /** + * Returns the complete <code>CharSequence</code> as a <code>String</code>. + * Classes that implement this interface should return a <code>String</code> + * which contains only the characters in the sequence in the correct order. + * + * @return the character sequence as a String + */ + String toString(); +} diff --git a/libjava/classpath/java/lang/Character.java b/libjava/classpath/java/lang/Character.java new file mode 100644 index 00000000000..4eac1476184 --- /dev/null +++ b/libjava/classpath/java/lang/Character.java @@ -0,0 +1,2253 @@ +/* java.lang.Character -- Wrapper class for char, and Unicode subsets + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.java.lang.CharData; + +import java.io.Serializable; + +/** + * Wrapper class for the primitive char data type. In addition, this class + * allows one to retrieve property information and perform transformations + * on the 57,707 defined characters in the Unicode Standard, Version 3.0.0. + * java.lang.Character is designed to be very dynamic, and as such, it + * retrieves information on the Unicode character set from a separate + * database, gnu.java.lang.CharData, which can be easily upgraded. + * + * <p>For predicates, boundaries are used to describe + * the set of characters for which the method will return true. + * This syntax uses fairly normal regular expression notation. + * See 5.13 of the Unicode Standard, Version 3.0, for the + * boundary specification. + * + * <p>See <a href="http://www.unicode.org">http://www.unicode.org</a> + * for more information on the Unicode Standard. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Paul N. Fisher + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see CharData + * @since 1.0 + * @status updated to 1.4 + */ +public final class Character implements Serializable, Comparable +{ + /** + * A subset of Unicode blocks. + * + * @author Paul N. Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Subset + { + /** The name of the subset. */ + private final String name; + + /** + * Construct a new subset of characters. + * + * @param name the name of the subset + * @throws NullPointerException if name is null + */ + protected Subset(String name) + { + // Note that name.toString() is name, unless name was null. + this.name = name.toString(); + } + + /** + * Compares two Subsets for equality. This is <code>final</code>, and + * restricts the comparison on the <code>==</code> operator, so it returns + * true only for the same object. + * + * @param o the object to compare + * @return true if o is this + */ + public final boolean equals(Object o) + { + return o == this; + } + + /** + * Makes the original hashCode of Object final, to be consistent with + * equals. + * + * @return the hash code for this object + */ + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Returns the name of the subset. + * + * @return the name + */ + public final String toString() + { + return name; + } + } // class Subset + + /** + * A family of character subsets in the Unicode specification. A character + * is in at most one of these blocks. + * + * This inner class was generated automatically from + * <code>doc/unicode/Block-3.txt</code>, by some perl scripts. + * This Unicode definition file can be found on the + * <a href="http://www.unicode.org">http://www.unicode.org</a> website. + * JDK 1.4 uses Unicode version 3.0.0. + * + * @author scripts/unicode-blocks.pl (written by Eric Blake) + * @since 1.2 + */ + public static final class UnicodeBlock extends Subset + { + /** The start of the subset. */ + private final char start; + + /** The end of the subset. */ + private final char end; + + /** + * Constructor for strictly defined blocks. + * + * @param start the start character of the range + * @param end the end character of the range + * @param name the block name + */ + private UnicodeBlock(char start, char end, String name) + { + super(name); + this.start = start; + this.end = end; + } + + /** + * Returns the Unicode character block which a character belongs to. + * + * @param ch the character to look up + * @return the set it belongs to, or null if it is not in one + */ + public static UnicodeBlock of(char ch) + { + // Special case, since SPECIALS contains two ranges. + if (ch == '\uFEFF') + return SPECIALS; + // Simple binary search for the correct block. + int low = 0; + int hi = sets.length - 1; + while (low <= hi) + { + int mid = (low + hi) >> 1; + UnicodeBlock b = sets[mid]; + if (ch < b.start) + hi = mid - 1; + else if (ch > b.end) + low = mid + 1; + else + return b; + } + return null; + } + + /** + * Basic Latin. + * '\u0000' - '\u007F'. + */ + public static final UnicodeBlock BASIC_LATIN + = new UnicodeBlock('\u0000', '\u007F', + "BASIC_LATIN"); + + /** + * Latin-1 Supplement. + * '\u0080' - '\u00FF'. + */ + public static final UnicodeBlock LATIN_1_SUPPLEMENT + = new UnicodeBlock('\u0080', '\u00FF', + "LATIN_1_SUPPLEMENT"); + + /** + * Latin Extended-A. + * '\u0100' - '\u017F'. + */ + public static final UnicodeBlock LATIN_EXTENDED_A + = new UnicodeBlock('\u0100', '\u017F', + "LATIN_EXTENDED_A"); + + /** + * Latin Extended-B. + * '\u0180' - '\u024F'. + */ + public static final UnicodeBlock LATIN_EXTENDED_B + = new UnicodeBlock('\u0180', '\u024F', + "LATIN_EXTENDED_B"); + + /** + * IPA Extensions. + * '\u0250' - '\u02AF'. + */ + public static final UnicodeBlock IPA_EXTENSIONS + = new UnicodeBlock('\u0250', '\u02AF', + "IPA_EXTENSIONS"); + + /** + * Spacing Modifier Letters. + * '\u02B0' - '\u02FF'. + */ + public static final UnicodeBlock SPACING_MODIFIER_LETTERS + = new UnicodeBlock('\u02B0', '\u02FF', + "SPACING_MODIFIER_LETTERS"); + + /** + * Combining Diacritical Marks. + * '\u0300' - '\u036F'. + */ + public static final UnicodeBlock COMBINING_DIACRITICAL_MARKS + = new UnicodeBlock('\u0300', '\u036F', + "COMBINING_DIACRITICAL_MARKS"); + + /** + * Greek. + * '\u0370' - '\u03FF'. + */ + public static final UnicodeBlock GREEK + = new UnicodeBlock('\u0370', '\u03FF', + "GREEK"); + + /** + * Cyrillic. + * '\u0400' - '\u04FF'. + */ + public static final UnicodeBlock CYRILLIC + = new UnicodeBlock('\u0400', '\u04FF', + "CYRILLIC"); + + /** + * Armenian. + * '\u0530' - '\u058F'. + */ + public static final UnicodeBlock ARMENIAN + = new UnicodeBlock('\u0530', '\u058F', + "ARMENIAN"); + + /** + * Hebrew. + * '\u0590' - '\u05FF'. + */ + public static final UnicodeBlock HEBREW + = new UnicodeBlock('\u0590', '\u05FF', + "HEBREW"); + + /** + * Arabic. + * '\u0600' - '\u06FF'. + */ + public static final UnicodeBlock ARABIC + = new UnicodeBlock('\u0600', '\u06FF', + "ARABIC"); + + /** + * Syriac. + * '\u0700' - '\u074F'. + * @since 1.4 + */ + public static final UnicodeBlock SYRIAC + = new UnicodeBlock('\u0700', '\u074F', + "SYRIAC"); + + /** + * Thaana. + * '\u0780' - '\u07BF'. + * @since 1.4 + */ + public static final UnicodeBlock THAANA + = new UnicodeBlock('\u0780', '\u07BF', + "THAANA"); + + /** + * Devanagari. + * '\u0900' - '\u097F'. + */ + public static final UnicodeBlock DEVANAGARI + = new UnicodeBlock('\u0900', '\u097F', + "DEVANAGARI"); + + /** + * Bengali. + * '\u0980' - '\u09FF'. + */ + public static final UnicodeBlock BENGALI + = new UnicodeBlock('\u0980', '\u09FF', + "BENGALI"); + + /** + * Gurmukhi. + * '\u0A00' - '\u0A7F'. + */ + public static final UnicodeBlock GURMUKHI + = new UnicodeBlock('\u0A00', '\u0A7F', + "GURMUKHI"); + + /** + * Gujarati. + * '\u0A80' - '\u0AFF'. + */ + public static final UnicodeBlock GUJARATI + = new UnicodeBlock('\u0A80', '\u0AFF', + "GUJARATI"); + + /** + * Oriya. + * '\u0B00' - '\u0B7F'. + */ + public static final UnicodeBlock ORIYA + = new UnicodeBlock('\u0B00', '\u0B7F', + "ORIYA"); + + /** + * Tamil. + * '\u0B80' - '\u0BFF'. + */ + public static final UnicodeBlock TAMIL + = new UnicodeBlock('\u0B80', '\u0BFF', + "TAMIL"); + + /** + * Telugu. + * '\u0C00' - '\u0C7F'. + */ + public static final UnicodeBlock TELUGU + = new UnicodeBlock('\u0C00', '\u0C7F', + "TELUGU"); + + /** + * Kannada. + * '\u0C80' - '\u0CFF'. + */ + public static final UnicodeBlock KANNADA + = new UnicodeBlock('\u0C80', '\u0CFF', + "KANNADA"); + + /** + * Malayalam. + * '\u0D00' - '\u0D7F'. + */ + public static final UnicodeBlock MALAYALAM + = new UnicodeBlock('\u0D00', '\u0D7F', + "MALAYALAM"); + + /** + * Sinhala. + * '\u0D80' - '\u0DFF'. + * @since 1.4 + */ + public static final UnicodeBlock SINHALA + = new UnicodeBlock('\u0D80', '\u0DFF', + "SINHALA"); + + /** + * Thai. + * '\u0E00' - '\u0E7F'. + */ + public static final UnicodeBlock THAI + = new UnicodeBlock('\u0E00', '\u0E7F', + "THAI"); + + /** + * Lao. + * '\u0E80' - '\u0EFF'. + */ + public static final UnicodeBlock LAO + = new UnicodeBlock('\u0E80', '\u0EFF', + "LAO"); + + /** + * Tibetan. + * '\u0F00' - '\u0FFF'. + */ + public static final UnicodeBlock TIBETAN + = new UnicodeBlock('\u0F00', '\u0FFF', + "TIBETAN"); + + /** + * Myanmar. + * '\u1000' - '\u109F'. + * @since 1.4 + */ + public static final UnicodeBlock MYANMAR + = new UnicodeBlock('\u1000', '\u109F', + "MYANMAR"); + + /** + * Georgian. + * '\u10A0' - '\u10FF'. + */ + public static final UnicodeBlock GEORGIAN + = new UnicodeBlock('\u10A0', '\u10FF', + "GEORGIAN"); + + /** + * Hangul Jamo. + * '\u1100' - '\u11FF'. + */ + public static final UnicodeBlock HANGUL_JAMO + = new UnicodeBlock('\u1100', '\u11FF', + "HANGUL_JAMO"); + + /** + * Ethiopic. + * '\u1200' - '\u137F'. + * @since 1.4 + */ + public static final UnicodeBlock ETHIOPIC + = new UnicodeBlock('\u1200', '\u137F', + "ETHIOPIC"); + + /** + * Cherokee. + * '\u13A0' - '\u13FF'. + * @since 1.4 + */ + public static final UnicodeBlock CHEROKEE + = new UnicodeBlock('\u13A0', '\u13FF', + "CHEROKEE"); + + /** + * Unified Canadian Aboriginal Syllabics. + * '\u1400' - '\u167F'. + * @since 1.4 + */ + public static final UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS + = new UnicodeBlock('\u1400', '\u167F', + "UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS"); + + /** + * Ogham. + * '\u1680' - '\u169F'. + * @since 1.4 + */ + public static final UnicodeBlock OGHAM + = new UnicodeBlock('\u1680', '\u169F', + "OGHAM"); + + /** + * Runic. + * '\u16A0' - '\u16FF'. + * @since 1.4 + */ + public static final UnicodeBlock RUNIC + = new UnicodeBlock('\u16A0', '\u16FF', + "RUNIC"); + + /** + * Khmer. + * '\u1780' - '\u17FF'. + * @since 1.4 + */ + public static final UnicodeBlock KHMER + = new UnicodeBlock('\u1780', '\u17FF', + "KHMER"); + + /** + * Mongolian. + * '\u1800' - '\u18AF'. + * @since 1.4 + */ + public static final UnicodeBlock MONGOLIAN + = new UnicodeBlock('\u1800', '\u18AF', + "MONGOLIAN"); + + /** + * Latin Extended Additional. + * '\u1E00' - '\u1EFF'. + */ + public static final UnicodeBlock LATIN_EXTENDED_ADDITIONAL + = new UnicodeBlock('\u1E00', '\u1EFF', + "LATIN_EXTENDED_ADDITIONAL"); + + /** + * Greek Extended. + * '\u1F00' - '\u1FFF'. + */ + public static final UnicodeBlock GREEK_EXTENDED + = new UnicodeBlock('\u1F00', '\u1FFF', + "GREEK_EXTENDED"); + + /** + * General Punctuation. + * '\u2000' - '\u206F'. + */ + public static final UnicodeBlock GENERAL_PUNCTUATION + = new UnicodeBlock('\u2000', '\u206F', + "GENERAL_PUNCTUATION"); + + /** + * Superscripts and Subscripts. + * '\u2070' - '\u209F'. + */ + public static final UnicodeBlock SUPERSCRIPTS_AND_SUBSCRIPTS + = new UnicodeBlock('\u2070', '\u209F', + "SUPERSCRIPTS_AND_SUBSCRIPTS"); + + /** + * Currency Symbols. + * '\u20A0' - '\u20CF'. + */ + public static final UnicodeBlock CURRENCY_SYMBOLS + = new UnicodeBlock('\u20A0', '\u20CF', + "CURRENCY_SYMBOLS"); + + /** + * Combining Marks for Symbols. + * '\u20D0' - '\u20FF'. + */ + public static final UnicodeBlock COMBINING_MARKS_FOR_SYMBOLS + = new UnicodeBlock('\u20D0', '\u20FF', + "COMBINING_MARKS_FOR_SYMBOLS"); + + /** + * Letterlike Symbols. + * '\u2100' - '\u214F'. + */ + public static final UnicodeBlock LETTERLIKE_SYMBOLS + = new UnicodeBlock('\u2100', '\u214F', + "LETTERLIKE_SYMBOLS"); + + /** + * Number Forms. + * '\u2150' - '\u218F'. + */ + public static final UnicodeBlock NUMBER_FORMS + = new UnicodeBlock('\u2150', '\u218F', + "NUMBER_FORMS"); + + /** + * Arrows. + * '\u2190' - '\u21FF'. + */ + public static final UnicodeBlock ARROWS + = new UnicodeBlock('\u2190', '\u21FF', + "ARROWS"); + + /** + * Mathematical Operators. + * '\u2200' - '\u22FF'. + */ + public static final UnicodeBlock MATHEMATICAL_OPERATORS + = new UnicodeBlock('\u2200', '\u22FF', + "MATHEMATICAL_OPERATORS"); + + /** + * Miscellaneous Technical. + * '\u2300' - '\u23FF'. + */ + public static final UnicodeBlock MISCELLANEOUS_TECHNICAL + = new UnicodeBlock('\u2300', '\u23FF', + "MISCELLANEOUS_TECHNICAL"); + + /** + * Control Pictures. + * '\u2400' - '\u243F'. + */ + public static final UnicodeBlock CONTROL_PICTURES + = new UnicodeBlock('\u2400', '\u243F', + "CONTROL_PICTURES"); + + /** + * Optical Character Recognition. + * '\u2440' - '\u245F'. + */ + public static final UnicodeBlock OPTICAL_CHARACTER_RECOGNITION + = new UnicodeBlock('\u2440', '\u245F', + "OPTICAL_CHARACTER_RECOGNITION"); + + /** + * Enclosed Alphanumerics. + * '\u2460' - '\u24FF'. + */ + public static final UnicodeBlock ENCLOSED_ALPHANUMERICS + = new UnicodeBlock('\u2460', '\u24FF', + "ENCLOSED_ALPHANUMERICS"); + + /** + * Box Drawing. + * '\u2500' - '\u257F'. + */ + public static final UnicodeBlock BOX_DRAWING + = new UnicodeBlock('\u2500', '\u257F', + "BOX_DRAWING"); + + /** + * Block Elements. + * '\u2580' - '\u259F'. + */ + public static final UnicodeBlock BLOCK_ELEMENTS + = new UnicodeBlock('\u2580', '\u259F', + "BLOCK_ELEMENTS"); + + /** + * Geometric Shapes. + * '\u25A0' - '\u25FF'. + */ + public static final UnicodeBlock GEOMETRIC_SHAPES + = new UnicodeBlock('\u25A0', '\u25FF', + "GEOMETRIC_SHAPES"); + + /** + * Miscellaneous Symbols. + * '\u2600' - '\u26FF'. + */ + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS + = new UnicodeBlock('\u2600', '\u26FF', + "MISCELLANEOUS_SYMBOLS"); + + /** + * Dingbats. + * '\u2700' - '\u27BF'. + */ + public static final UnicodeBlock DINGBATS + = new UnicodeBlock('\u2700', '\u27BF', + "DINGBATS"); + + /** + * Braille Patterns. + * '\u2800' - '\u28FF'. + * @since 1.4 + */ + public static final UnicodeBlock BRAILLE_PATTERNS + = new UnicodeBlock('\u2800', '\u28FF', + "BRAILLE_PATTERNS"); + + /** + * CJK Radicals Supplement. + * '\u2E80' - '\u2EFF'. + * @since 1.4 + */ + public static final UnicodeBlock CJK_RADICALS_SUPPLEMENT + = new UnicodeBlock('\u2E80', '\u2EFF', + "CJK_RADICALS_SUPPLEMENT"); + + /** + * Kangxi Radicals. + * '\u2F00' - '\u2FDF'. + * @since 1.4 + */ + public static final UnicodeBlock KANGXI_RADICALS + = new UnicodeBlock('\u2F00', '\u2FDF', + "KANGXI_RADICALS"); + + /** + * Ideographic Description Characters. + * '\u2FF0' - '\u2FFF'. + * @since 1.4 + */ + public static final UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS + = new UnicodeBlock('\u2FF0', '\u2FFF', + "IDEOGRAPHIC_DESCRIPTION_CHARACTERS"); + + /** + * CJK Symbols and Punctuation. + * '\u3000' - '\u303F'. + */ + public static final UnicodeBlock CJK_SYMBOLS_AND_PUNCTUATION + = new UnicodeBlock('\u3000', '\u303F', + "CJK_SYMBOLS_AND_PUNCTUATION"); + + /** + * Hiragana. + * '\u3040' - '\u309F'. + */ + public static final UnicodeBlock HIRAGANA + = new UnicodeBlock('\u3040', '\u309F', + "HIRAGANA"); + + /** + * Katakana. + * '\u30A0' - '\u30FF'. + */ + public static final UnicodeBlock KATAKANA + = new UnicodeBlock('\u30A0', '\u30FF', + "KATAKANA"); + + /** + * Bopomofo. + * '\u3100' - '\u312F'. + */ + public static final UnicodeBlock BOPOMOFO + = new UnicodeBlock('\u3100', '\u312F', + "BOPOMOFO"); + + /** + * Hangul Compatibility Jamo. + * '\u3130' - '\u318F'. + */ + public static final UnicodeBlock HANGUL_COMPATIBILITY_JAMO + = new UnicodeBlock('\u3130', '\u318F', + "HANGUL_COMPATIBILITY_JAMO"); + + /** + * Kanbun. + * '\u3190' - '\u319F'. + */ + public static final UnicodeBlock KANBUN + = new UnicodeBlock('\u3190', '\u319F', + "KANBUN"); + + /** + * Bopomofo Extended. + * '\u31A0' - '\u31BF'. + * @since 1.4 + */ + public static final UnicodeBlock BOPOMOFO_EXTENDED + = new UnicodeBlock('\u31A0', '\u31BF', + "BOPOMOFO_EXTENDED"); + + /** + * Enclosed CJK Letters and Months. + * '\u3200' - '\u32FF'. + */ + public static final UnicodeBlock ENCLOSED_CJK_LETTERS_AND_MONTHS + = new UnicodeBlock('\u3200', '\u32FF', + "ENCLOSED_CJK_LETTERS_AND_MONTHS"); + + /** + * CJK Compatibility. + * '\u3300' - '\u33FF'. + */ + public static final UnicodeBlock CJK_COMPATIBILITY + = new UnicodeBlock('\u3300', '\u33FF', + "CJK_COMPATIBILITY"); + + /** + * CJK Unified Ideographs Extension A. + * '\u3400' - '\u4DB5'. + * @since 1.4 + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + = new UnicodeBlock('\u3400', '\u4DB5', + "CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A"); + + /** + * CJK Unified Ideographs. + * '\u4E00' - '\u9FFF'. + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS + = new UnicodeBlock('\u4E00', '\u9FFF', + "CJK_UNIFIED_IDEOGRAPHS"); + + /** + * Yi Syllables. + * '\uA000' - '\uA48F'. + * @since 1.4 + */ + public static final UnicodeBlock YI_SYLLABLES + = new UnicodeBlock('\uA000', '\uA48F', + "YI_SYLLABLES"); + + /** + * Yi Radicals. + * '\uA490' - '\uA4CF'. + * @since 1.4 + */ + public static final UnicodeBlock YI_RADICALS + = new UnicodeBlock('\uA490', '\uA4CF', + "YI_RADICALS"); + + /** + * Hangul Syllables. + * '\uAC00' - '\uD7A3'. + */ + public static final UnicodeBlock HANGUL_SYLLABLES + = new UnicodeBlock('\uAC00', '\uD7A3', + "HANGUL_SYLLABLES"); + + /** + * Surrogates Area. + * '\uD800' - '\uDFFF'. + */ + public static final UnicodeBlock SURROGATES_AREA + = new UnicodeBlock('\uD800', '\uDFFF', + "SURROGATES_AREA"); + + /** + * Private Use Area. + * '\uE000' - '\uF8FF'. + */ + public static final UnicodeBlock PRIVATE_USE_AREA + = new UnicodeBlock('\uE000', '\uF8FF', + "PRIVATE_USE_AREA"); + + /** + * CJK Compatibility Ideographs. + * '\uF900' - '\uFAFF'. + */ + public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS + = new UnicodeBlock('\uF900', '\uFAFF', + "CJK_COMPATIBILITY_IDEOGRAPHS"); + + /** + * Alphabetic Presentation Forms. + * '\uFB00' - '\uFB4F'. + */ + public static final UnicodeBlock ALPHABETIC_PRESENTATION_FORMS + = new UnicodeBlock('\uFB00', '\uFB4F', + "ALPHABETIC_PRESENTATION_FORMS"); + + /** + * Arabic Presentation Forms-A. + * '\uFB50' - '\uFDFF'. + */ + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_A + = new UnicodeBlock('\uFB50', '\uFDFF', + "ARABIC_PRESENTATION_FORMS_A"); + + /** + * Combining Half Marks. + * '\uFE20' - '\uFE2F'. + */ + public static final UnicodeBlock COMBINING_HALF_MARKS + = new UnicodeBlock('\uFE20', '\uFE2F', + "COMBINING_HALF_MARKS"); + + /** + * CJK Compatibility Forms. + * '\uFE30' - '\uFE4F'. + */ + public static final UnicodeBlock CJK_COMPATIBILITY_FORMS + = new UnicodeBlock('\uFE30', '\uFE4F', + "CJK_COMPATIBILITY_FORMS"); + + /** + * Small Form Variants. + * '\uFE50' - '\uFE6F'. + */ + public static final UnicodeBlock SMALL_FORM_VARIANTS + = new UnicodeBlock('\uFE50', '\uFE6F', + "SMALL_FORM_VARIANTS"); + + /** + * Arabic Presentation Forms-B. + * '\uFE70' - '\uFEFE'. + */ + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_B + = new UnicodeBlock('\uFE70', '\uFEFE', + "ARABIC_PRESENTATION_FORMS_B"); + + /** + * Halfwidth and Fullwidth Forms. + * '\uFF00' - '\uFFEF'. + */ + public static final UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS + = new UnicodeBlock('\uFF00', '\uFFEF', + "HALFWIDTH_AND_FULLWIDTH_FORMS"); + + /** + * Specials. + * '\uFEFF', '\uFFF0' - '\uFFFD'. + */ + public static final UnicodeBlock SPECIALS + = new UnicodeBlock('\uFFF0', '\uFFFD', + "SPECIALS"); + + /** + * The defined subsets. + */ + private static final UnicodeBlock sets[] = { + BASIC_LATIN, + LATIN_1_SUPPLEMENT, + LATIN_EXTENDED_A, + LATIN_EXTENDED_B, + IPA_EXTENSIONS, + SPACING_MODIFIER_LETTERS, + COMBINING_DIACRITICAL_MARKS, + GREEK, + CYRILLIC, + ARMENIAN, + HEBREW, + ARABIC, + SYRIAC, + THAANA, + DEVANAGARI, + BENGALI, + GURMUKHI, + GUJARATI, + ORIYA, + TAMIL, + TELUGU, + KANNADA, + MALAYALAM, + SINHALA, + THAI, + LAO, + TIBETAN, + MYANMAR, + GEORGIAN, + HANGUL_JAMO, + ETHIOPIC, + CHEROKEE, + UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS, + OGHAM, + RUNIC, + KHMER, + MONGOLIAN, + LATIN_EXTENDED_ADDITIONAL, + GREEK_EXTENDED, + GENERAL_PUNCTUATION, + SUPERSCRIPTS_AND_SUBSCRIPTS, + CURRENCY_SYMBOLS, + COMBINING_MARKS_FOR_SYMBOLS, + LETTERLIKE_SYMBOLS, + NUMBER_FORMS, + ARROWS, + MATHEMATICAL_OPERATORS, + MISCELLANEOUS_TECHNICAL, + CONTROL_PICTURES, + OPTICAL_CHARACTER_RECOGNITION, + ENCLOSED_ALPHANUMERICS, + BOX_DRAWING, + BLOCK_ELEMENTS, + GEOMETRIC_SHAPES, + MISCELLANEOUS_SYMBOLS, + DINGBATS, + BRAILLE_PATTERNS, + CJK_RADICALS_SUPPLEMENT, + KANGXI_RADICALS, + IDEOGRAPHIC_DESCRIPTION_CHARACTERS, + CJK_SYMBOLS_AND_PUNCTUATION, + HIRAGANA, + KATAKANA, + BOPOMOFO, + HANGUL_COMPATIBILITY_JAMO, + KANBUN, + BOPOMOFO_EXTENDED, + ENCLOSED_CJK_LETTERS_AND_MONTHS, + CJK_COMPATIBILITY, + CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A, + CJK_UNIFIED_IDEOGRAPHS, + YI_SYLLABLES, + YI_RADICALS, + HANGUL_SYLLABLES, + SURROGATES_AREA, + PRIVATE_USE_AREA, + CJK_COMPATIBILITY_IDEOGRAPHS, + ALPHABETIC_PRESENTATION_FORMS, + ARABIC_PRESENTATION_FORMS_A, + COMBINING_HALF_MARKS, + CJK_COMPATIBILITY_FORMS, + SMALL_FORM_VARIANTS, + ARABIC_PRESENTATION_FORMS_B, + HALFWIDTH_AND_FULLWIDTH_FORMS, + SPECIALS, + }; + } // class UnicodeBlock + + /** + * The immutable value of this Character. + * + * @serial the value of this Character + */ + private final char value; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3786198910865385080L; + + /** + * Smallest value allowed for radix arguments in Java. This value is 2. + * + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see Integer#toString(int, int) + * @see Integer#valueOf(String) + */ + public static final int MIN_RADIX = 2; + + /** + * Largest value allowed for radix arguments in Java. This value is 36. + * + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see Integer#toString(int, int) + * @see Integer#valueOf(String) + */ + public static final int MAX_RADIX = 36; + + /** + * The minimum value the char data type can hold. + * This value is <code>'\\u0000'</code>. + */ + public static final char MIN_VALUE = '\u0000'; + + /** + * The maximum value the char data type can hold. + * This value is <code>'\\uFFFF'</code>. + */ + public static final char MAX_VALUE = '\uFFFF'; + + /** + * Class object representing the primitive char data type. + * + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('C'); + + /** + * Lu = Letter, Uppercase (Informative). + * + * @since 1.1 + */ + public static final byte UPPERCASE_LETTER = 1; + + /** + * Ll = Letter, Lowercase (Informative). + * + * @since 1.1 + */ + public static final byte LOWERCASE_LETTER = 2; + + /** + * Lt = Letter, Titlecase (Informative). + * + * @since 1.1 + */ + public static final byte TITLECASE_LETTER = 3; + + /** + * Mn = Mark, Non-Spacing (Normative). + * + * @since 1.1 + */ + public static final byte NON_SPACING_MARK = 6; + + /** + * Mc = Mark, Spacing Combining (Normative). + * + * @since 1.1 + */ + public static final byte COMBINING_SPACING_MARK = 8; + + /** + * Me = Mark, Enclosing (Normative). + * + * @since 1.1 + */ + public static final byte ENCLOSING_MARK = 7; + + /** + * Nd = Number, Decimal Digit (Normative). + * + * @since 1.1 + */ + public static final byte DECIMAL_DIGIT_NUMBER = 9; + + /** + * Nl = Number, Letter (Normative). + * + * @since 1.1 + */ + public static final byte LETTER_NUMBER = 10; + + /** + * No = Number, Other (Normative). + * + * @since 1.1 + */ + public static final byte OTHER_NUMBER = 11; + + /** + * Zs = Separator, Space (Normative). + * + * @since 1.1 + */ + public static final byte SPACE_SEPARATOR = 12; + + /** + * Zl = Separator, Line (Normative). + * + * @since 1.1 + */ + public static final byte LINE_SEPARATOR = 13; + + /** + * Zp = Separator, Paragraph (Normative). + * + * @since 1.1 + */ + public static final byte PARAGRAPH_SEPARATOR = 14; + + /** + * Cc = Other, Control (Normative). + * + * @since 1.1 + */ + public static final byte CONTROL = 15; + + /** + * Cf = Other, Format (Normative). + * + * @since 1.1 + */ + public static final byte FORMAT = 16; + + /** + * Cs = Other, Surrogate (Normative). + * + * @since 1.1 + */ + public static final byte SURROGATE = 19; + + /** + * Co = Other, Private Use (Normative). + * + * @since 1.1 + */ + public static final byte PRIVATE_USE = 18; + + /** + * Cn = Other, Not Assigned (Normative). + * + * @since 1.1 + */ + public static final byte UNASSIGNED = 0; + + /** + * Lm = Letter, Modifier (Informative). + * + * @since 1.1 + */ + public static final byte MODIFIER_LETTER = 4; + + /** + * Lo = Letter, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_LETTER = 5; + + /** + * Pc = Punctuation, Connector (Informative). + * + * @since 1.1 + */ + public static final byte CONNECTOR_PUNCTUATION = 23; + + /** + * Pd = Punctuation, Dash (Informative). + * + * @since 1.1 + */ + public static final byte DASH_PUNCTUATION = 20; + + /** + * Ps = Punctuation, Open (Informative). + * + * @since 1.1 + */ + public static final byte START_PUNCTUATION = 21; + + /** + * Pe = Punctuation, Close (Informative). + * + * @since 1.1 + */ + public static final byte END_PUNCTUATION = 22; + + /** + * Pi = Punctuation, Initial Quote (Informative). + * + * @since 1.4 + */ + public static final byte INITIAL_QUOTE_PUNCTUATION = 29; + + /** + * Pf = Punctuation, Final Quote (Informative). + * + * @since 1.4 + */ + public static final byte FINAL_QUOTE_PUNCTUATION = 30; + + /** + * Po = Punctuation, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_PUNCTUATION = 24; + + /** + * Sm = Symbol, Math (Informative). + * + * @since 1.1 + */ + public static final byte MATH_SYMBOL = 25; + + /** + * Sc = Symbol, Currency (Informative). + * + * @since 1.1 + */ + public static final byte CURRENCY_SYMBOL = 26; + + /** + * Sk = Symbol, Modifier (Informative). + * + * @since 1.1 + */ + public static final byte MODIFIER_SYMBOL = 27; + + /** + * So = Symbol, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_SYMBOL = 28; + + /** + * Undefined bidirectional character type. Undefined char values have + * undefined directionality in the Unicode specification. + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_UNDEFINED = -1; + + /** + * Strong bidirectional character type "L". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0; + + /** + * Strong bidirectional character type "R". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1; + + /** + * Strong bidirectional character type "AL". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2; + + /** + * Weak bidirectional character type "EN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = 3; + + /** + * Weak bidirectional character type "ES". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4; + + /** + * Weak bidirectional character type "ET". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5; + + /** + * Weak bidirectional character type "AN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_ARABIC_NUMBER = 6; + + /** + * Weak bidirectional character type "CS". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7; + + /** + * Weak bidirectional character type "NSM". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_NONSPACING_MARK = 8; + + /** + * Weak bidirectional character type "BN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 9; + + /** + * Neutral bidirectional character type "B". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10; + + /** + * Neutral bidirectional character type "S". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = 11; + + /** + * Strong bidirectional character type "WS". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_WHITESPACE = 12; + + /** + * Neutral bidirectional character type "ON". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_OTHER_NEUTRALS = 13; + + /** + * Strong bidirectional character type "LRE". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14; + + /** + * Strong bidirectional character type "LRO". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15; + + /** + * Strong bidirectional character type "RLE". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16; + + /** + * Strong bidirectional character type "RLO". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17; + + /** + * Weak bidirectional character type "PDF". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; + + /** + * Stores unicode block offset lookup table. Exploit package visibility of + * String.value to avoid copying the array. + * @see #readChar(char) + * @see CharData#BLOCKS + */ + private static final char[] blocks = String.zeroBasedStringValue(CharData.BLOCKS); + + /** + * Stores unicode attribute offset lookup table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#DATA + */ + private static final char[] data = String.zeroBasedStringValue(CharData.DATA); + + /** + * Stores unicode numeric value attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#NUM_VALUE + */ + private static final char[] numValue + = String.zeroBasedStringValue(CharData.NUM_VALUE); + + /** + * Stores unicode uppercase attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#UPPER + */ + private static final char[] upper = String.zeroBasedStringValue(CharData.UPPER); + + /** + * Stores unicode lowercase attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#LOWER + */ + private static final char[] lower = String.zeroBasedStringValue(CharData.LOWER); + + /** + * Stores unicode direction attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#DIRECTION + */ + // Package visible for use by String. + static final char[] direction = String.zeroBasedStringValue(CharData.DIRECTION); + + /** + * Stores unicode titlecase table. Exploit package visibility of + * String.value to avoid copying the array. + * @see CharData#TITLE + */ + private static final char[] title = String.zeroBasedStringValue(CharData.TITLE); + + /** + * Mask for grabbing the type out of the contents of data. + * @see CharData#DATA + */ + private static final int TYPE_MASK = 0x1F; + + /** + * Mask for grabbing the non-breaking space flag out of the contents of + * data. + * @see CharData#DATA + */ + private static final int NO_BREAK_MASK = 0x20; + + /** + * Mask for grabbing the mirrored directionality flag out of the contents + * of data. + * @see CharData#DATA + */ + private static final int MIRROR_MASK = 0x40; + + /** + * Grabs an attribute offset from the Unicode attribute database. The lower + * 5 bits are the character type, the next 2 bits are flags, and the top + * 9 bits are the offset into the attribute tables. + * + * @param ch the character to look up + * @return the character's attribute offset and type + * @see #TYPE_MASK + * @see #NO_BREAK_MASK + * @see #MIRROR_MASK + * @see CharData#DATA + * @see CharData#SHIFT + */ + // Package visible for use in String. + static char readChar(char ch) + { + // Perform 16-bit addition to find the correct entry in data. + return data[(char) (blocks[ch >> CharData.SHIFT] + ch)]; + } + + /** + * Wraps up a character. + * + * @param value the character to wrap + */ + public Character(char value) + { + this.value = value; + } + + /** + * Returns the character which has been wrapped by this class. + * + * @return the character wrapped + */ + public char charValue() + { + return value; + } + + /** + * Returns the numerical value (unsigned) of the wrapped character. + * Range of returned values: 0x0000-0xFFFF. + * + * @return the value of the wrapped character + */ + public int hashCode() + { + return value; + } + + /** + * Determines if an object is equal to this object. This is only true for + * another Character object wrapping the same value. + * + * @param o object to compare + * @return true if o is a Character with the same value + */ + public boolean equals(Object o) + { + return o instanceof Character && value == ((Character) o).value; + } + + /** + * Converts the wrapped character into a String. + * + * @return a String containing one character -- the wrapped character + * of this instance + */ + public String toString() + { + // Package constructor avoids an array copy. + return new String(new char[] { value }, 0, 1, true); + } + + /** + * Returns a String of length 1 representing the specified character. + * + * @param ch the character to convert + * @return a String containing the character + * @since 1.4 + */ + public static String toString(char ch) + { + // Package constructor avoids an array copy. + return new String(new char[] { ch }, 0, 1, true); + } + + /** + * Determines if a character is a Unicode lowercase letter. For example, + * <code>'a'</code> is lowercase. + * <br> + * lowercase = [Ll] + * + * @param ch character to test + * @return true if ch is a Unicode lowercase letter, else false + * @see #isUpperCase(char) + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #getType(char) + */ + public static boolean isLowerCase(char ch) + { + return getType(ch) == LOWERCASE_LETTER; + } + + /** + * Determines if a character is a Unicode uppercase letter. For example, + * <code>'A'</code> is uppercase. + * <br> + * uppercase = [Lu] + * + * @param ch character to test + * @return true if ch is a Unicode uppercase letter, else false + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #toUpperCase(char) + * @see #getType(char) + */ + public static boolean isUpperCase(char ch) + { + return getType(ch) == UPPERCASE_LETTER; + } + + /** + * Determines if a character is a Unicode titlecase letter. For example, + * the character "Lj" (Latin capital L with small letter j) is titlecase. + * <br> + * titlecase = [Lt] + * + * @param ch character to test + * @return true if ch is a Unicode titlecase letter, else false + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #getType(char) + */ + public static boolean isTitleCase(char ch) + { + return getType(ch) == TITLECASE_LETTER; + } + + /** + * Determines if a character is a Unicode decimal digit. For example, + * <code>'0'</code> is a digit. + * <br> + * Unicode decimal digit = [Nd] + * + * @param ch character to test + * @return true if ch is a Unicode decimal digit, else false + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see #getType(char) + */ + public static boolean isDigit(char ch) + { + return getType(ch) == DECIMAL_DIGIT_NUMBER; + } + + /** + * Determines if a character is part of the Unicode Standard. This is an + * evolving standard, but covers every character in the data file. + * <br> + * defined = not [Cn] + * + * @param ch character to test + * @return true if ch is a Unicode character, else false + * @see #isDigit(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUpperCase(char) + */ + public static boolean isDefined(char ch) + { + return getType(ch) != UNASSIGNED; + } + + /** + * Determines if a character is a Unicode letter. Not all letters have case, + * so this may return true when isLowerCase and isUpperCase return false. + * <br> + * letter = [Lu]|[Ll]|[Lt]|[Lm]|[Lo] + * + * @param ch character to test + * @return true if ch is a Unicode letter, else false + * @see #isDigit(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUnicodeIdentifierStart(char) + * @see #isUpperCase(char) + */ + public static boolean isLetter(char ch) + { + return ((1 << getType(ch)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER))) != 0; + } + + /** + * Determines if a character is a Unicode letter or a Unicode digit. This + * is the combination of isLetter and isDigit. + * <br> + * letter or digit = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nd] + * + * @param ch character to test + * @return true if ch is a Unicode letter or a Unicode digit, else false + * @see #isDigit(char) + * @see #isJavaIdentifierPart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + */ + public static boolean isLetterOrDigit(char ch) + { + return ((1 << getType(ch)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << DECIMAL_DIGIT_NUMBER))) != 0; + } + + /** + * Determines if a character can start a Java identifier. This is the + * combination of isLetter, any character where getType returns + * LETTER_NUMBER, currency symbols (like '$'), and connecting punctuation + * (like '_'). + * + * @param ch character to test + * @return true if ch can start a Java identifier, else false + * @deprecated Replaced by {@link #isJavaIdentifierStart(char)} + * @see #isJavaLetterOrDigit(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierStart(char) + */ + public static boolean isJavaLetter(char ch) + { + return isJavaIdentifierStart(ch); + } + + /** + * Determines if a character can follow the first letter in + * a Java identifier. This is the combination of isJavaLetter (isLetter, + * type of LETTER_NUMBER, currency, connecting punctuation) and digit, + * numeric letter (like Roman numerals), combining marks, non-spacing marks, + * or isIdentifierIgnorable. + * + * @param ch character to test + * @return true if ch can follow the first letter in a Java identifier + * @deprecated Replaced by {@link #isJavaIdentifierPart(char)} + * @see #isJavaLetter(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierPart(char) + * @see #isIdentifierIgnorable(char) + */ + public static boolean isJavaLetterOrDigit(char ch) + { + return isJavaIdentifierPart(ch); + } + + /** + * Determines if a character can start a Java identifier. This is the + * combination of isLetter, any character where getType returns + * LETTER_NUMBER, currency symbols (like '$'), and connecting punctuation + * (like '_'). + * <br> + * Java identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc] + * + * @param ch character to test + * @return true if ch can start a Java identifier, else false + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.1 + */ + public static boolean isJavaIdentifierStart(char ch) + { + return ((1 << getType(ch)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << LETTER_NUMBER) + | (1 << CURRENCY_SYMBOL) + | (1 << CONNECTOR_PUNCTUATION))) != 0; + } + + /** + * Determines if a character can follow the first letter in + * a Java identifier. This is the combination of isJavaLetter (isLetter, + * type of LETTER_NUMBER, currency, connecting punctuation) and digit, + * numeric letter (like Roman numerals), combining marks, non-spacing marks, + * or isIdentifierIgnorable. + * <br> + * Java identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc]|[Mn]|[Mc]|[Nd]|[Cf] + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param ch character to test + * @return true if ch can follow the first letter in a Java identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierStart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isJavaIdentifierPart(char ch) + { + int category = getType(ch); + return ((1 << category) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << NON_SPACING_MARK) + | (1 << COMBINING_SPACING_MARK) + | (1 << DECIMAL_DIGIT_NUMBER) + | (1 << LETTER_NUMBER) + | (1 << CURRENCY_SYMBOL) + | (1 << CONNECTOR_PUNCTUATION) + | (1 << FORMAT))) != 0 + || (category == CONTROL && isIdentifierIgnorable(ch)); + } + + /** + * Determines if a character can start a Unicode identifier. Only + * letters can start a Unicode identifier, but this includes characters + * in LETTER_NUMBER. + * <br> + * Unicode identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl] + * + * @param ch character to test + * @return true if ch can start a Unicode identifier, else false + * @see #isJavaIdentifierStart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isUnicodeIdentifierStart(char ch) + { + return ((1 << getType(ch)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << LETTER_NUMBER))) != 0; + } + + /** + * Determines if a character can follow the first letter in + * a Unicode identifier. This includes letters, connecting punctuation, + * digits, numeric letters, combining marks, non-spacing marks, and + * isIdentifierIgnorable. + * <br> + * Unicode identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Mn]|[Mc]|[Nd]|[Pc]|[Cf]| + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param ch character to test + * @return true if ch can follow the first letter in a Unicode identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.1 + */ + public static boolean isUnicodeIdentifierPart(char ch) + { + int category = getType(ch); + return ((1 << category) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << NON_SPACING_MARK) + | (1 << COMBINING_SPACING_MARK) + | (1 << DECIMAL_DIGIT_NUMBER) + | (1 << LETTER_NUMBER) + | (1 << CONNECTOR_PUNCTUATION) + | (1 << FORMAT))) != 0 + || (category == CONTROL && isIdentifierIgnorable(ch)); + } + + /** + * Determines if a character is ignorable in a Unicode identifier. This + * includes the non-whitespace ISO control characters (<code>'\u0000'</code> + * through <code>'\u0008'</code>, <code>'\u000E'</code> through + * <code>'\u001B'</code>, and <code>'\u007F'</code> through + * <code>'\u009F'</code>), and FORMAT characters. + * <br> + * Unicode identifier ignorable = [Cf]|U+0000-U+0008|U+000E-U+001B + * |U+007F-U+009F + * + * @param ch character to test + * @return true if ch is ignorable in a Unicode or Java identifier + * @see #isJavaIdentifierPart(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isIdentifierIgnorable(char ch) + { + return (ch <= '\u009F' && (ch < '\t' || ch >= '\u007F' + || (ch <= '\u001B' && ch >= '\u000E'))) + || getType(ch) == FORMAT; + } + + /** + * Converts a Unicode character into its lowercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isLowerCase(toLowerCase(ch)) does not always return true. + * + * @param ch character to convert to lowercase + * @return lowercase mapping of ch, or ch if lowercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #toUpperCase(char) + */ + public static char toLowerCase(char ch) + { + // Signedness doesn't matter, as result is cast back to char. + return (char) (ch + lower[readChar(ch) >> 7]); + } + + /** + * Converts a Unicode character into its uppercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isUpperCase(toUpperCase(ch)) does not always return true. + * + * @param ch character to convert to uppercase + * @return uppercase mapping of ch, or ch if uppercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toLowerCase(char) + * @see #toTitleCase(char) + */ + public static char toUpperCase(char ch) + { + // Signedness doesn't matter, as result is cast back to char. + return (char) (ch + upper[readChar(ch) >> 7]); + } + + /** + * Converts a Unicode character into its titlecase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isTitleCase(toTitleCase(ch)) does not always return true. + * + * @param ch character to convert to titlecase + * @return titlecase mapping of ch, or ch if titlecase mapping does + * not exist + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #toUpperCase(char) + */ + public static char toTitleCase(char ch) + { + // As title is short, it doesn't hurt to exhaustively iterate over it. + for (int i = title.length - 2; i >= 0; i -= 2) + if (title[i] == ch) + return title[i + 1]; + return toUpperCase(ch); + } + + /** + * Converts a character into a digit of the specified radix. If the radix + * exceeds MIN_RADIX or MAX_RADIX, or if the result of getNumericValue(ch) + * exceeds the radix, or if ch is not a decimal digit or in the case + * insensitive set of 'a'-'z', the result is -1. + * <br> + * character argument boundary = [Nd]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param ch character to convert into a digit + * @param radix radix in which ch is a digit + * @return digit which ch represents in radix, or -1 not a valid digit + * @see #MIN_RADIX + * @see #MAX_RADIX + * @see #forDigit(int, int) + * @see #isDigit(char) + * @see #getNumericValue(char) + */ + public static int digit(char ch, int radix) + { + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + char attr = readChar(ch); + if (((1 << (attr & TYPE_MASK)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << DECIMAL_DIGIT_NUMBER))) != 0) + { + // Signedness doesn't matter; 0xffff vs. -1 are both rejected. + int digit = numValue[attr >> 7]; + return (digit < radix) ? digit : -1; + } + return -1; + } + + /** + * Returns the Unicode numeric value property of a character. For example, + * <code>'\\u216C'</code> (the Roman numeral fifty) returns 50. + * + * <p>This method also returns values for the letters A through Z, (not + * specified by Unicode), in these ranges: <code>'\u0041'</code> + * through <code>'\u005A'</code> (uppercase); <code>'\u0061'</code> + * through <code>'\u007A'</code> (lowercase); and <code>'\uFF21'</code> + * through <code>'\uFF3A'</code>, <code>'\uFF41'</code> through + * <code>'\uFF5A'</code> (full width variants). + * + * <p>If the character lacks a numeric value property, -1 is returned. + * If the character has a numeric value property which is not representable + * as a nonnegative integer, such as a fraction, -2 is returned. + * + * character argument boundary = [Nd]|[Nl]|[No]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param ch character from which the numeric value property will + * be retrieved + * @return the numeric value property of ch, or -1 if it does not exist, or + * -2 if it is not representable as a nonnegative integer + * @see #forDigit(int, int) + * @see #digit(char, int) + * @see #isDigit(char) + * @since 1.1 + */ + public static int getNumericValue(char ch) + { + // Treat numValue as signed. + return (short) numValue[readChar(ch) >> 7]; + } + + /** + * Determines if a character is a ISO-LATIN-1 space. This is only the five + * characters <code>'\t'</code>, <code>'\n'</code>, <code>'\f'</code>, + * <code>'\r'</code>, and <code>' '</code>. + * <br> + * Java space = U+0020|U+0009|U+000A|U+000C|U+000D + * + * @param ch character to test + * @return true if ch is a space, else false + * @deprecated Replaced by {@link #isWhitespace(char)} + * @see #isSpaceChar(char) + * @see #isWhitespace(char) + */ + public static boolean isSpace(char ch) + { + // Performing the subtraction up front alleviates need to compare longs. + return ch-- <= ' ' && ((1 << ch) + & ((1 << (' ' - 1)) + | (1 << ('\t' - 1)) + | (1 << ('\n' - 1)) + | (1 << ('\r' - 1)) + | (1 << ('\f' - 1)))) != 0; + } + + /** + * Determines if a character is a Unicode space character. This includes + * SPACE_SEPARATOR, LINE_SEPARATOR, and PARAGRAPH_SEPARATOR. + * <br> + * Unicode space = [Zs]|[Zp]|[Zl] + * + * @param ch character to test + * @return true if ch is a Unicode space, else false + * @see #isWhitespace(char) + * @since 1.1 + */ + public static boolean isSpaceChar(char ch) + { + return ((1 << getType(ch)) + & ((1 << SPACE_SEPARATOR) + | (1 << LINE_SEPARATOR) + | (1 << PARAGRAPH_SEPARATOR))) != 0; + } + + /** + * Determines if a character is Java whitespace. This includes Unicode + * space characters (SPACE_SEPARATOR, LINE_SEPARATOR, and + * PARAGRAPH_SEPARATOR) except the non-breaking spaces + * (<code>'\u00A0'</code>, <code>'\u2007'</code>, and <code>'\u202F'</code>); + * and these characters: <code>'\u0009'</code>, <code>'\u000A'</code>, + * <code>'\u000B'</code>, <code>'\u000C'</code>, <code>'\u000D'</code>, + * <code>'\u001C'</code>, <code>'\u001D'</code>, <code>'\u001E'</code>, + * and <code>'\u001F'</code>. + * <br> + * Java whitespace = ([Zs] not Nb)|[Zl]|[Zp]|U+0009-U+000D|U+001C-U+001F + * + * @param ch character to test + * @return true if ch is Java whitespace, else false + * @see #isSpaceChar(char) + * @since 1.1 + */ + public static boolean isWhitespace(char ch) + { + int attr = readChar(ch); + return ((((1 << (attr & TYPE_MASK)) + & ((1 << SPACE_SEPARATOR) + | (1 << LINE_SEPARATOR) + | (1 << PARAGRAPH_SEPARATOR))) != 0) + && (attr & NO_BREAK_MASK) == 0) + || (ch <= '\u001F' && ((1 << ch) + & ((1 << '\t') + | (1 << '\n') + | (1 << '\u000B') + | (1 << '\u000C') + | (1 << '\r') + | (1 << '\u001C') + | (1 << '\u001D') + | (1 << '\u001E') + | (1 << '\u001F'))) != 0); + } + + /** + * Determines if a character has the ISO Control property. + * <br> + * ISO Control = [Cc] + * + * @param ch character to test + * @return true if ch is an ISO Control character, else false + * @see #isSpaceChar(char) + * @see #isWhitespace(char) + * @since 1.1 + */ + public static boolean isISOControl(char ch) + { + return getType(ch) == CONTROL; + } + + /** + * Returns the Unicode general category property of a character. + * + * @param ch character from which the general category property will + * be retrieved + * @return the character category property of ch as an integer + * @see #UNASSIGNED + * @see #UPPERCASE_LETTER + * @see #LOWERCASE_LETTER + * @see #TITLECASE_LETTER + * @see #MODIFIER_LETTER + * @see #OTHER_LETTER + * @see #NON_SPACING_MARK + * @see #ENCLOSING_MARK + * @see #COMBINING_SPACING_MARK + * @see #DECIMAL_DIGIT_NUMBER + * @see #LETTER_NUMBER + * @see #OTHER_NUMBER + * @see #SPACE_SEPARATOR + * @see #LINE_SEPARATOR + * @see #PARAGRAPH_SEPARATOR + * @see #CONTROL + * @see #FORMAT + * @see #PRIVATE_USE + * @see #SURROGATE + * @see #DASH_PUNCTUATION + * @see #START_PUNCTUATION + * @see #END_PUNCTUATION + * @see #CONNECTOR_PUNCTUATION + * @see #OTHER_PUNCTUATION + * @see #MATH_SYMBOL + * @see #CURRENCY_SYMBOL + * @see #MODIFIER_SYMBOL + * @see #INITIAL_QUOTE_PUNCTUATION + * @see #FINAL_QUOTE_PUNCTUATION + * @since 1.1 + */ + public static int getType(char ch) + { + return readChar(ch) & TYPE_MASK; + } + + /** + * Converts a digit into a character which represents that digit + * in a specified radix. If the radix exceeds MIN_RADIX or MAX_RADIX, + * or the digit exceeds the radix, then the null character <code>'\0'</code> + * is returned. Otherwise the return value is in '0'-'9' and 'a'-'z'. + * <br> + * return value boundary = U+0030-U+0039|U+0061-U+007A + * + * @param digit digit to be converted into a character + * @param radix radix of digit + * @return character representing digit in radix, or '\0' + * @see #MIN_RADIX + * @see #MAX_RADIX + * @see #digit(char, int) + */ + public static char forDigit(int digit, int radix) + { + if (radix < MIN_RADIX || radix > MAX_RADIX + || digit < 0 || digit >= radix) + return '\0'; + return Number.digits[digit]; + } + + /** + * Returns the Unicode directionality property of the character. This + * is used in the visual ordering of text. + * + * @param ch the character to look up + * @return the directionality constant, or DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_LEFT_TO_RIGHT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC + * @see #DIRECTIONALITY_EUROPEAN_NUMBER + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + * @see #DIRECTIONALITY_ARABIC_NUMBER + * @see #DIRECTIONALITY_COMMON_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_NONSPACING_MARK + * @see #DIRECTIONALITY_BOUNDARY_NEUTRAL + * @see #DIRECTIONALITY_PARAGRAPH_SEPARATOR + * @see #DIRECTIONALITY_SEGMENT_SEPARATOR + * @see #DIRECTIONALITY_WHITESPACE + * @see #DIRECTIONALITY_OTHER_NEUTRALS + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE + * @see #DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + * @since 1.4 + */ + public static byte getDirectionality(char ch) + { + // The result will correctly be signed. + return (byte) (direction[readChar(ch) >> 7] >> 2); + } + + /** + * Determines whether the character is mirrored according to Unicode. For + * example, <code>\u0028</code> (LEFT PARENTHESIS) appears as '(' in + * left-to-right text, but ')' in right-to-left text. + * + * @param ch the character to look up + * @return true if the character is mirrored + * @since 1.4 + */ + public static boolean isMirrored(char ch) + { + return (readChar(ch) & MIRROR_MASK) != 0; + } + + /** + * Compares another Character to this Character, numerically. + * + * @param anotherCharacter Character to compare with this Character + * @return a negative integer if this Character is less than + * anotherCharacter, zero if this Character is equal, and + * a positive integer if this Character is greater + * @throws NullPointerException if anotherCharacter is null + * @since 1.2 + */ + public int compareTo(Character anotherCharacter) + { + return value - anotherCharacter.value; + } + + /** + * Compares an object to this Character. Assuming the object is a + * Character object, this method performs the same comparison as + * compareTo(Character). + * + * @param o object to compare + * @return the comparison value + * @throws ClassCastException if o is not a Character object + * @throws NullPointerException if o is null + * @see #compareTo(Character) + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((Character) o); + } +} // class Character diff --git a/libjava/classpath/java/lang/Class.java b/libjava/classpath/java/lang/Class.java new file mode 100644 index 00000000000..6b8bc93ae63 --- /dev/null +++ b/libjava/classpath/java/lang/Class.java @@ -0,0 +1,1324 @@ +/* Class.java -- Representation of a Java class. + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.VMStackWalker; + +import java.io.InputStream; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + + +/** + * A Class represents a Java type. There will never be multiple Class + * objects with identical names and ClassLoaders. Primitive types, array + * types, and void also have a Class object. + * + * <p>Arrays with identical type and number of dimensions share the same class. + * The array class ClassLoader is the same as the ClassLoader of the element + * type of the array (which can be null to indicate the bootstrap classloader). + * The name of an array class is <code>[<signature format>;</code>. + * <p> For example, + * String[]'s class is <code>[Ljava.lang.String;</code>. boolean, byte, + * short, char, int, long, float and double have the "type name" of + * Z,B,S,C,I,J,F,D for the purposes of array classes. If it's a + * multidimensioned array, the same principle applies: + * <code>int[][][]</code> == <code>[[[I</code>. + * + * <p>There is no public constructor - Class objects are obtained only through + * the virtual machine, as defined in ClassLoaders. + * + * @serialData Class objects serialize specially: + * <code>TC_CLASS ClassDescriptor</code>. For more serialization information, + * see {@link ObjectStreamClass}. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.0 + * @see ClassLoader + */ +public final class Class implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3206093459760846163L; + + /** The class signers. */ + private Object[] signers = null; + /** The class protection domain. */ + private final ProtectionDomain pd; + + /* We use an inner class, so that Class doesn't have a static initializer */ + private static final class StaticData + { + static final ProtectionDomain unknownProtectionDomain; + + static + { + Permissions permissions = new Permissions(); + permissions.add(new AllPermission()); + unknownProtectionDomain = new ProtectionDomain(null, permissions); + } + } + + final transient Object vmdata; + + /** newInstance() caches the default constructor */ + private transient Constructor constructor; + + /** + * Class is non-instantiable from Java code; only the VM can create + * instances of this class. + */ + Class(Object vmdata) + { + this(vmdata, null); + } + + Class(Object vmdata, ProtectionDomain pd) + { + this.vmdata = vmdata; + // If the VM didn't supply a protection domain and the class is an array, + // we "inherit" the protection domain from the component type class. This + // saves the VM from having to worry about protection domains for array + // classes. + if (pd == null && isArray()) + this.pd = getComponentType().pd; + else + this.pd = pd; + } + + /** + * Use the classloader of the current class to load, link, and initialize + * a class. This is equivalent to your code calling + * <code>Class.forName(name, true, getClass().getClassLoader())</code>. + * + * @param name the name of the class to find + * @return the Class object representing the class + * @throws ClassNotFoundException if the class was not found by the + * classloader + * @throws LinkageError if linking the class fails + * @throws ExceptionInInitializerError if the class loads, but an exception + * occurs during initialization + */ + public static Class forName(String name) throws ClassNotFoundException + { + Class result = VMClass.forName (name); + if (result == null) + result = Class.forName(name, true, + VMStackWalker.getCallingClassLoader()); + return result; + } + + /** + * Use the specified classloader to load and link a class. If the loader + * is null, this uses the bootstrap class loader (provide the security + * check succeeds). Unfortunately, this method cannot be used to obtain + * the Class objects for primitive types or for void, you have to use + * the fields in the appropriate java.lang wrapper classes. + * + * <p>Calls <code>classloader.loadclass(name, initialize)</code>. + * + * @param name the name of the class to find + * @param initialize whether or not to initialize the class at this time + * @param classloader the classloader to use to find the class; null means + * to use the bootstrap class loader + * + * @return the class object for the given class + * + * @throws ClassNotFoundException if the class was not found by the + * classloader + * @throws LinkageError if linking the class fails + * @throws ExceptionInInitializerError if the class loads, but an exception + * occurs during initialization + * @throws SecurityException if the <code>classloader</code> argument + * is <code>null</code> and the caller does not have the + * <code>RuntimePermission("getClassLoader")</code> permission + * @see ClassLoader + * @since 1.2 + */ + public static Class forName(String name, boolean initialize, + ClassLoader classloader) + throws ClassNotFoundException + { + if (classloader == null) + { + // Check if we may access the bootstrap classloader + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + if (name.startsWith("[")) + return VMClass.loadArrayClass(name, null); + Class c = VMClassLoader.loadClass(name, true); + if (c != null) + { + if (initialize) + VMClass.initialize(c); + return c; + } + throw new ClassNotFoundException(name); + } + if (name.startsWith("[")) + return VMClass.loadArrayClass(name, classloader); + Class c = classloader.loadClass(name); + classloader.resolveClass(c); + if (initialize) + VMClass.initialize(c); + return c; + } + + /** + * Get all the public member classes and interfaces declared in this + * class or inherited from superclasses. This returns an array of length + * 0 if there are no member classes, including for primitive types. A + * security check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all public member classes in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Class[] getClasses() + { + memberAccessCheck(Member.PUBLIC); + return internalGetClasses(); + } + + /** + * Like <code>getClasses()</code> but without the security checks. + */ + private Class[] internalGetClasses() + { + ArrayList list = new ArrayList(); + list.addAll(Arrays.asList(getDeclaredClasses(true))); + Class superClass = getSuperclass(); + if (superClass != null) + list.addAll(Arrays.asList(superClass.internalGetClasses())); + return (Class[])list.toArray(new Class[list.size()]); + } + + /** + * Get the ClassLoader that loaded this class. If the class was loaded + * by the bootstrap classloader, this method will return null. + * If there is a security manager, and the caller's class loader is not + * an ancestor of the requested one, a security check of + * <code>RuntimePermission("getClassLoader")</code> + * must first succeed. Primitive types and void return null. + * + * @return the ClassLoader that loaded this class + * @throws SecurityException if the security check fails + * @see ClassLoader + * @see RuntimePermission + */ + public ClassLoader getClassLoader() + { + if (isPrimitive()) + return null; + + ClassLoader loader = VMClass.getClassLoader(this); + // Check if we may get the classloader + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && !cl.isAncestorOf(loader)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return loader; + } + + /** + * If this is an array, get the Class representing the type of array. + * Examples: "[[Ljava.lang.String;" would return "[Ljava.lang.String;", and + * calling getComponentType on that would give "java.lang.String". If + * this is not an array, returns null. + * + * @return the array type of this class, or null + * @see Array + * @since 1.1 + */ + public Class getComponentType() + { + return VMClass.getComponentType (this); + } + + /** + * Get a public constructor declared in this class. If the constructor takes + * no argument, an array of zero elements and null are equivalent for the + * types argument. A security check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @param types the type of each parameter + * @return the constructor + * @throws NoSuchMethodException if the constructor does not exist + * @throws SecurityException if the security check fails + * @see #getConstructors() + * @since 1.1 + */ + public Constructor getConstructor(Class[] types) throws NoSuchMethodException + { + memberAccessCheck(Member.PUBLIC); + Constructor[] constructors = getDeclaredConstructors(true); + for (int i = 0; i < constructors.length; i++) + { + Constructor constructor = constructors[i]; + if (matchParameters(types, constructor.getParameterTypes())) + return constructor; + } + throw new NoSuchMethodException(); + } + + /** + * Get all the public constructors of this class. This returns an array of + * length 0 if there are no constructors, including for primitive types, + * arrays, and interfaces. It does, however, include the default + * constructor if one was supplied by the compiler. A security check may + * be performed, with <code>checkMemberAccess(this, Member.PUBLIC)</code> + * as well as <code>checkPackageAccess</code> both having to succeed. + * + * @return all public constructors in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Constructor[] getConstructors() + { + memberAccessCheck(Member.PUBLIC); + return getDeclaredConstructors(true); + } + + /** + * Get a constructor declared in this class. If the constructor takes no + * argument, an array of zero elements and null are equivalent for the + * types argument. A security check may be performed, with + * <code>checkMemberAccess(this, Member.DECLARED)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @param types the type of each parameter + * @return the constructor + * @throws NoSuchMethodException if the constructor does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredConstructors() + * @since 1.1 + */ + public Constructor getDeclaredConstructor(Class[] types) + throws NoSuchMethodException + { + memberAccessCheck(Member.DECLARED); + Constructor[] constructors = getDeclaredConstructors(false); + for (int i = 0; i < constructors.length; i++) + { + Constructor constructor = constructors[i]; + if (matchParameters(types, constructor.getParameterTypes())) + return constructor; + } + throw new NoSuchMethodException(); + } + + /** + * Get all the declared member classes and interfaces in this class, but + * not those inherited from superclasses. This returns an array of length + * 0 if there are no member classes, including for primitive types. A + * security check may be performed, with + * <code>checkMemberAccess(this, Member.DECLARED)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all declared member classes in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Class[] getDeclaredClasses() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredClasses(false); + } + + Class[] getDeclaredClasses (boolean publicOnly) + { + return VMClass.getDeclaredClasses (this, publicOnly); + } + + /** + * Get all the declared constructors of this class. This returns an array of + * length 0 if there are no constructors, including for primitive types, + * arrays, and interfaces. It does, however, include the default + * constructor if one was supplied by the compiler. A security check may + * be performed, with <code>checkMemberAccess(this, Member.DECLARED)</code> + * as well as <code>checkPackageAccess</code> both having to succeed. + * + * @return all constructors in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Constructor[] getDeclaredConstructors() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredConstructors(false); + } + + Constructor[] getDeclaredConstructors (boolean publicOnly) + { + return VMClass.getDeclaredConstructors (this, publicOnly); + } + + /** + * Get a field declared in this class, where name is its simple name. The + * implicit length field of arrays is not available. A security check may + * be performed, with <code>checkMemberAccess(this, Member.DECLARED)</code> + * as well as <code>checkPackageAccess</code> both having to succeed. + * + * @param name the name of the field + * @return the field + * @throws NoSuchFieldException if the field does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredFields() + * @since 1.1 + */ + public Field getDeclaredField(String name) throws NoSuchFieldException + { + memberAccessCheck(Member.DECLARED); + Field[] fields = getDeclaredFields(false); + for (int i = 0; i < fields.length; i++) + { + if (fields[i].getName().equals(name)) + return fields[i]; + } + throw new NoSuchFieldException(); + } + + /** + * Get all the declared fields in this class, but not those inherited from + * superclasses. This returns an array of length 0 if there are no fields, + * including for primitive types. This does not return the implicit length + * field of arrays. A security check may be performed, with + * <code>checkMemberAccess(this, Member.DECLARED)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all declared fields in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Field[] getDeclaredFields() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredFields(false); + } + + Field[] getDeclaredFields (boolean publicOnly) + { + return VMClass.getDeclaredFields (this, publicOnly); + } + + /** + * Get a method declared in this class, where name is its simple name. The + * implicit methods of Object are not available from arrays or interfaces. + * Constructors (named "<init>" in the class file) and class initializers + * (name "<clinit>") are not available. The Virtual Machine allows + * multiple methods with the same signature but differing return types; in + * such a case the most specific return types are favored, then the final + * choice is arbitrary. If the method takes no argument, an array of zero + * elements and null are equivalent for the types argument. A security + * check may be performed, with + * <code>checkMemberAccess(this, Member.DECLARED)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @param methodName the name of the method + * @param types the type of each parameter + * @return the method + * @throws NoSuchMethodException if the method does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredMethods() + * @since 1.1 + */ + public Method getDeclaredMethod(String methodName, Class[] types) + throws NoSuchMethodException + { + memberAccessCheck(Member.DECLARED); + Method match = matchMethod(getDeclaredMethods(false), methodName, types); + if (match == null) + throw new NoSuchMethodException(methodName); + return match; + } + + /** + * Get all the declared methods in this class, but not those inherited from + * superclasses. This returns an array of length 0 if there are no methods, + * including for primitive types. This does include the implicit methods of + * arrays and interfaces which mirror methods of Object, nor does it + * include constructors or the class initialization methods. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types; all such methods are in the returned array. A security + * check may be performed, with + * <code>checkMemberAccess(this, Member.DECLARED)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all declared methods in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Method[] getDeclaredMethods() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredMethods(false); + } + + Method[] getDeclaredMethods (boolean publicOnly) + { + return VMClass.getDeclaredMethods (this, publicOnly); + } + + /** + * If this is a nested or inner class, return the class that declared it. + * If not, return null. + * + * @return the declaring class of this class + * @since 1.1 + */ + public Class getDeclaringClass() + { + return VMClass.getDeclaringClass (this); + } + + /** + * Get a public field declared or inherited in this class, where name is + * its simple name. If the class contains multiple accessible fields by + * that name, an arbitrary one is returned. The implicit length field of + * arrays is not available. A security check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @param fieldName the name of the field + * @return the field + * @throws NoSuchFieldException if the field does not exist + * @throws SecurityException if the security check fails + * @see #getFields() + * @since 1.1 + */ + public Field getField(String fieldName) + throws NoSuchFieldException + { + memberAccessCheck(Member.PUBLIC); + Field field = internalGetField(fieldName); + if (field == null) + throw new NoSuchFieldException(fieldName); + return field; + } + + /** + * Get all the public fields declared in this class or inherited from + * superclasses. This returns an array of length 0 if there are no fields, + * including for primitive types. This does not return the implicit length + * field of arrays. A security check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all public fields in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Field[] getFields() + { + memberAccessCheck(Member.PUBLIC); + return internalGetFields(); + } + + /** + * Like <code>getFields()</code> but without the security checks. + */ + private Field[] internalGetFields() + { + HashSet set = new HashSet(); + set.addAll(Arrays.asList(getDeclaredFields(true))); + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + set.addAll(Arrays.asList(interfaces[i].internalGetFields())); + Class superClass = getSuperclass(); + if (superClass != null) + set.addAll(Arrays.asList(superClass.internalGetFields())); + return (Field[])set.toArray(new Field[set.size()]); + } + + /** + * Returns the <code>Package</code> in which this class is defined + * Returns null when this information is not available from the + * classloader of this class or when the classloader of this class + * is null. + * + * @return the package for this class, if it is available + * @since 1.2 + */ + public Package getPackage() + { + ClassLoader cl = getClassLoader(); + if (cl != null) + return cl.getPackage(getPackagePortion(getName())); + return null; + } + + /** + * Get the interfaces this class <em>directly</em> implements, in the + * order that they were declared. This returns an empty array, not null, + * for Object, primitives, void, and classes or interfaces with no direct + * superinterface. Array types return Cloneable and Serializable. + * + * @return the interfaces this class directly implements + */ + public Class[] getInterfaces() + { + return VMClass.getInterfaces (this); + } + + private static final class MethodKey + { + private String name; + private Class[] params; + private Class returnType; + private int hash; + + MethodKey(Method m) + { + name = m.getName(); + params = m.getParameterTypes(); + returnType = m.getReturnType(); + hash = name.hashCode() ^ returnType.hashCode(); + for(int i = 0; i < params.length; i++) + { + hash ^= params[i].hashCode(); + } + } + + public boolean equals(Object o) + { + if(o instanceof MethodKey) + { + MethodKey m = (MethodKey)o; + if(m.name.equals(name) && m.params.length == params.length && m.returnType == returnType) + { + for(int i = 0; i < params.length; i++) + { + if(m.params[i] != params[i]) + { + return false; + } + } + return true; + } + } + return false; + } + + public int hashCode() + { + return hash; + } + } + + /** + * Get a public method declared or inherited in this class, where name is + * its simple name. The implicit methods of Object are not available from + * interfaces. Constructors (named "<init>" in the class file) and class + * initializers (name "<clinit>") are not available. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types, and the class can inherit multiple methods of the same + * return type; in such a case the most specific return types are favored, + * then the final choice is arbitrary. If the method takes no argument, an + * array of zero elements and null are equivalent for the types argument. + * A security check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @param methodName the name of the method + * @param types the type of each parameter + * @return the method + * @throws NoSuchMethodException if the method does not exist + * @throws SecurityException if the security check fails + * @see #getMethods() + * @since 1.1 + */ + public Method getMethod(String methodName, Class[] types) + throws NoSuchMethodException + { + memberAccessCheck(Member.PUBLIC); + Method method = internalGetMethod(methodName, types); + if (method == null) + throw new NoSuchMethodException(methodName); + return method; + } + + /** + * Like <code>getMethod(String,Class[])</code> but without the security + * checks and returns null instead of throwing NoSuchMethodException. + */ + private Method internalGetMethod(String methodName, Class[] args) + { + Method match = matchMethod(getDeclaredMethods(true), methodName, args); + if (match != null) + return match; + Class superClass = getSuperclass(); + if (superClass != null) + { + match = superClass.internalGetMethod(methodName, args); + if(match != null) + return match; + } + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + match = interfaces[i].internalGetMethod(methodName, args); + if (match != null) + return match; + } + return null; + } + + /** + * Find the best matching method in <code>list</code> according to + * the definition of ``best matching'' used by <code>getMethod()</code> + * + * <p> + * Returns the method if any, otherwise <code>null</code>. + * + * @param list List of methods to search + * @param name Name of method + * @param args Method parameter types + * @see #getMethod() + */ + private static Method matchMethod(Method[] list, String name, Class[] args) + { + Method match = null; + for (int i = 0; i < list.length; i++) + { + Method method = list[i]; + if (!method.getName().equals(name)) + continue; + if (!matchParameters(args, method.getParameterTypes())) + continue; + if (match == null + || match.getReturnType().isAssignableFrom(method.getReturnType())) + match = method; + } + return match; + } + + /** + * Check for an exact match between parameter type lists. + * Either list may be <code>null</code> to mean a list of + * length zero. + */ + private static boolean matchParameters(Class[] types1, Class[] types2) + { + if (types1 == null) + return types2 == null || types2.length == 0; + if (types2 == null) + return types1 == null || types1.length == 0; + if (types1.length != types2.length) + return false; + for (int i = 0; i < types1.length; i++) + { + if (types1[i] != types2[i]) + return false; + } + return true; + } + + /** + * Get all the public methods declared in this class or inherited from + * superclasses. This returns an array of length 0 if there are no methods, + * including for primitive types. This does not include the implicit + * methods of interfaces which mirror methods of Object, nor does it + * include constructors or the class initialization methods. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types; all such methods are in the returned array. A security + * check may be performed, with + * <code>checkMemberAccess(this, Member.PUBLIC)</code> as well as + * <code>checkPackageAccess</code> both having to succeed. + * + * @return all public methods in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Method[] getMethods() + { + memberAccessCheck(Member.PUBLIC); + // NOTE the API docs claim that no methods are returned for arrays, + // but Sun's implementation *does* return the public methods of Object + // (as would be expected), so we follow their implementation instead + // of their documentation. + return internalGetMethods(); + } + + /** + * Like <code>getMethods()</code> but without the security checks. + */ + private Method[] internalGetMethods() + { + HashMap map = new HashMap(); + Method[] methods; + Class[] interfaces = getInterfaces(); + for(int i = 0; i < interfaces.length; i++) + { + methods = interfaces[i].internalGetMethods(); + for(int j = 0; j < methods.length; j++) + { + map.put(new MethodKey(methods[j]), methods[j]); + } + } + Class superClass = getSuperclass(); + if(superClass != null) + { + methods = superClass.internalGetMethods(); + for(int i = 0; i < methods.length; i++) + { + map.put(new MethodKey(methods[i]), methods[i]); + } + } + methods = getDeclaredMethods(true); + for(int i = 0; i < methods.length; i++) + { + map.put(new MethodKey(methods[i]), methods[i]); + } + return (Method[])map.values().toArray(new Method[map.size()]); + } + + /** + * Get the modifiers of this class. These can be decoded using Modifier, + * and is limited to one of public, protected, or private, and any of + * final, static, abstract, or interface. An array class has the same + * public, protected, or private modifier as its component type, and is + * marked final but not an interface. Primitive types and void are marked + * public and final, but not an interface. + * + * @return the modifiers of this class + * @see Modifer + * @since 1.1 + */ + public int getModifiers() + { + return VMClass.getModifiers (this, false); + } + + /** + * Get the name of this class, separated by dots for package separators. + * If the class represents a primitive type, or void, then the + * name of the type as it appears in the Java programming language + * is returned. For instance, <code>Byte.TYPE.getName()</code> + * returns "byte". + * + * Arrays are specially encoded as shown on this table. + * <pre> + * array type [<em>element type</em> + * (note that the element type is encoded per + * this table) + * boolean Z + * byte B + * char C + * short S + * int I + * long J + * float F + * double D + * void V + * class or interface, alone: <dotted name> + * class or interface, as element type: L<dotted name>; + * </pre> + * + * @return the name of this class + */ + public String getName() + { + return VMClass.getName (this); + } + + /** + * Get a resource URL using this class's package using the + * getClassLoader().getResource() method. If this class was loaded using + * the system classloader, ClassLoader.getSystemResource() is used instead. + * + * <p>If the name you supply is absolute (it starts with a <code>/</code>), + * then the leading <code>/</code> is removed and it is passed on to + * getResource(). If it is relative, the package name is prepended, and + * <code>.</code>'s are replaced with <code>/</code>. + * + * <p>The URL returned is system- and classloader-dependent, and could + * change across implementations. + * + * @param resourceName the name of the resource, generally a path + * @return the URL to the resource + * @throws NullPointerException if name is null + * @since 1.1 + */ + public URL getResource(String resourceName) + { + String name = resourcePath(resourceName); + ClassLoader loader = getClassLoader(); + if (loader == null) + return ClassLoader.getSystemResource(name); + return loader.getResource(name); + } + + /** + * Get a resource using this class's package using the + * getClassLoader().getResourceAsStream() method. If this class was loaded + * using the system classloader, ClassLoader.getSystemResource() is used + * instead. + * + * <p>If the name you supply is absolute (it starts with a <code>/</code>), + * then the leading <code>/</code> is removed and it is passed on to + * getResource(). If it is relative, the package name is prepended, and + * <code>.</code>'s are replaced with <code>/</code>. + * + * <p>The URL returned is system- and classloader-dependent, and could + * change across implementations. + * + * @param resourceName the name of the resource, generally a path + * @return an InputStream with the contents of the resource in it, or null + * @throws NullPointerException if name is null + * @since 1.1 + */ + public InputStream getResourceAsStream(String resourceName) + { + String name = resourcePath(resourceName); + ClassLoader loader = getClassLoader(); + if (loader == null) + return ClassLoader.getSystemResourceAsStream(name); + return loader.getResourceAsStream(name); + } + + private String resourcePath(String resourceName) + { + if (resourceName.length() > 0) + { + if (resourceName.charAt(0) != '/') + { + String pkg = getPackagePortion(getName()); + if (pkg.length() > 0) + resourceName = pkg.replace('.','/') + '/' + resourceName; + } + else + { + resourceName = resourceName.substring(1); + } + } + return resourceName; + } + + /** + * Get the signers of this class. This returns null if there are no signers, + * such as for primitive types or void. + * + * @return the signers of this class + * @since 1.1 + */ + public Object[] getSigners() + { + return signers == null ? null : (Object[]) signers.clone (); + } + + /** + * Set the signers of this class. + * + * @param signers the signers of this class + */ + void setSigners(Object[] signers) + { + this.signers = signers; + } + + /** + * Get the direct superclass of this class. If this is an interface, + * Object, a primitive type, or void, it will return null. If this is an + * array type, it will return Object. + * + * @return the direct superclass of this class + */ + public Class getSuperclass() + { + return VMClass.getSuperclass (this); + } + + /** + * Return whether this class is an array type. + * + * @return whether this class is an array type + * @since 1.1 + */ + public boolean isArray() + { + return VMClass.isArray (this); + } + + /** + * Discover whether an instance of the Class parameter would be an + * instance of this Class as well. Think of doing + * <code>isInstance(c.newInstance())</code> or even + * <code>c.newInstance() instanceof (this class)</code>. While this + * checks widening conversions for objects, it must be exact for primitive + * types. + * + * @param c the class to check + * @return whether an instance of c would be an instance of this class + * as well + * @throws NullPointerException if c is null + * @since 1.1 + */ + public boolean isAssignableFrom(Class c) + { + return VMClass.isAssignableFrom (this, c); + } + + /** + * Discover whether an Object is an instance of this Class. Think of it + * as almost like <code>o instanceof (this class)</code>. + * + * @param o the Object to check + * @return whether o is an instance of this class + * @since 1.1 + */ + public boolean isInstance(Object o) + { + return VMClass.isInstance (this, o); + } + + /** + * Check whether this class is an interface or not. Array types are not + * interfaces. + * + * @return whether this class is an interface or not + */ + public boolean isInterface() + { + return VMClass.isInterface (this); + } + + /** + * Return whether this class is a primitive type. A primitive type class + * is a class representing a kind of "placeholder" for the various + * primitive types, or void. You can access the various primitive type + * classes through java.lang.Boolean.TYPE, java.lang.Integer.TYPE, etc., + * or through boolean.class, int.class, etc. + * + * @return whether this class is a primitive type + * @see Boolean#TYPE + * @see Byte#TYPE + * @see Character#TYPE + * @see Short#TYPE + * @see Integer#TYPE + * @see Long#TYPE + * @see Float#TYPE + * @see Double#TYPE + * @see Void#TYPE + * @since 1.1 + */ + public boolean isPrimitive() + { + return VMClass.isPrimitive (this); + } + + /** + * Get a new instance of this class by calling the no-argument constructor. + * The class is initialized if it has not been already. A security check + * may be performed, with <code>checkMemberAccess(this, Member.PUBLIC)</code> + * as well as <code>checkPackageAccess</code> both having to succeed. + * + * @return a new instance of this class + * @throws InstantiationException if there is not a no-arg constructor + * for this class, including interfaces, abstract classes, arrays, + * primitive types, and void; or if an exception occurred during + * the constructor + * @throws IllegalAccessException if you are not allowed to access the + * no-arg constructor because of scoping reasons + * @throws SecurityException if the security check fails + * @throws ExceptionInInitializerError if class initialization caused by + * this call fails with an exception + */ + public Object newInstance() + throws InstantiationException, IllegalAccessException + { + memberAccessCheck(Member.PUBLIC); + Constructor constructor; + synchronized(this) + { + constructor = this.constructor; + } + if (constructor == null) + { + Constructor[] constructors = getDeclaredConstructors(false); + for (int i = 0; i < constructors.length; i++) + { + if (constructors[i].getParameterTypes().length == 0) + { + constructor = constructors[i]; + break; + } + } + if (constructor == null) + throw new InstantiationException(getName()); + if (!Modifier.isPublic(constructor.getModifiers()) + || !Modifier.isPublic(VMClass.getModifiers(this, true))) + { + final Constructor finalConstructor = constructor; + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + finalConstructor.setAccessible(true); + return null; + } + }); + } + synchronized(this) + { + if (this.constructor == null) + this.constructor = constructor; + } + } + int modifiers = constructor.getModifiers(); + if (!Modifier.isPublic(modifiers) + || !Modifier.isPublic(VMClass.getModifiers(this, true))) + { + Class caller = VMStackWalker.getCallingClass(); + if (caller != null && + caller != this && + (Modifier.isPrivate(modifiers) + || getClassLoader() != caller.getClassLoader() + || !getPackagePortion(getName()) + .equals(getPackagePortion(caller.getName())))) + throw new IllegalAccessException(getName() + + " has an inaccessible constructor"); + } + try + { + return constructor.newInstance(null); + } + catch (InvocationTargetException e) + { + VMClass.throwException(e.getTargetException()); + throw (InternalError) new InternalError + ("VMClass.throwException returned").initCause(e); + } + } + + /** + * Returns the protection domain of this class. If the classloader did not + * record the protection domain when creating this class the unknown + * protection domain is returned which has a <code>null</code> code source + * and all permissions. A security check may be performed, with + * <code>RuntimePermission("getProtectionDomain")</code>. + * + * @return the protection domain + * @throws SecurityException if the security manager exists and the caller + * does not have <code>RuntimePermission("getProtectionDomain")</code>. + * @see RuntimePermission + * @since 1.2 + */ + public ProtectionDomain getProtectionDomain() + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkPermission(new RuntimePermission("getProtectionDomain")); + + return pd == null ? StaticData.unknownProtectionDomain : pd; + } + + /** + * Return the human-readable form of this Object. For an object, this + * is either "interface " or "class " followed by <code>getName()</code>, + * for primitive types and void it is just <code>getName()</code>. + * + * @return the human-readable form of this Object + */ + public String toString() + { + if (isPrimitive()) + return getName(); + return (isInterface() ? "interface " : "class ") + getName(); + } + + /** + * Returns the desired assertion status of this class, if it were to be + * initialized at this moment. The class assertion status, if set, is + * returned; the backup is the default package status; then if there is + * a class loader, that default is returned; and finally the system default + * is returned. This method seldom needs calling in user code, but exists + * for compilers to implement the assert statement. Note that there is no + * guarantee that the result of this method matches the class's actual + * assertion status. + * + * @return the desired assertion status + * @see ClassLoader#setClassAssertionStatus(String, boolean) + * @see ClassLoader#setPackageAssertionStatus(String, boolean) + * @see ClassLoader#setDefaultAssertionStatus(boolean) + * @since 1.4 + */ + public boolean desiredAssertionStatus() + { + ClassLoader c = getClassLoader(); + Object status; + if (c == null) + return VMClassLoader.defaultAssertionStatus(); + if (c.classAssertionStatus != null) + synchronized (c) + { + status = c.classAssertionStatus.get(getName()); + if (status != null) + return status.equals(Boolean.TRUE); + } + else + { + status = ClassLoader.StaticData. + systemClassAssertionStatus.get(getName()); + if (status != null) + return status.equals(Boolean.TRUE); + } + if (c.packageAssertionStatus != null) + synchronized (c) + { + String name = getPackagePortion(getName()); + if ("".equals(name)) + status = c.packageAssertionStatus.get(null); + else + do + { + status = c.packageAssertionStatus.get(name); + name = getPackagePortion(name); + } + while (! "".equals(name) && status == null); + if (status != null) + return status.equals(Boolean.TRUE); + } + else + { + String name = getPackagePortion(getName()); + if ("".equals(name)) + status = ClassLoader.StaticData. + systemPackageAssertionStatus.get(null); + else + do + { + status = ClassLoader.StaticData. + systemPackageAssertionStatus.get(name); + name = getPackagePortion(name); + } + while (! "".equals(name) && status == null); + if (status != null) + return status.equals(Boolean.TRUE); + } + return c.defaultAssertionStatus; + } + + /** + * Like <code>getField(String)</code> but without the security checks and returns null + * instead of throwing NoSuchFieldException. + */ + private Field internalGetField(String name) + { + Field[] fields = getDeclaredFields(true); + for (int i = 0; i < fields.length; i++) + { + Field field = fields[i]; + if (field.getName().equals(name)) + return field; + } + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + Field field = interfaces[i].internalGetField(name); + if(field != null) + return field; + } + Class superClass = getSuperclass(); + if (superClass != null) + return superClass.internalGetField(name); + return null; + } + + /** + * Strip the last portion of the name (after the last dot). + * + * @param name the name to get package of + * @return the package name, or "" if no package + */ + private static String getPackagePortion(String name) + { + int lastInd = name.lastIndexOf('.'); + if (lastInd == -1) + return ""; + return name.substring(0, lastInd); + } + + /** + * Perform security checks common to all of the methods that + * get members of this Class. + */ + private void memberAccessCheck(int which) + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + sm.checkMemberAccess(this, which); + Package pkg = getPackage(); + if (pkg != null) + sm.checkPackageAccess(pkg.getName()); + } + } +} diff --git a/libjava/classpath/java/lang/ClassCastException.java b/libjava/classpath/java/lang/ClassCastException.java new file mode 100644 index 00000000000..c490f42aaa8 --- /dev/null +++ b/libjava/classpath/java/lang/ClassCastException.java @@ -0,0 +1,76 @@ +/* ClassCastException.java -- exception thrown on bad cast + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to cast an object which is not of the + * appropriate runtime type. For example:<br> + * <pre> + * Object o = new Vector(); + * String s = (String) o; + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ClassCastException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9223365651070458532L; + + /** + * Create an exception without a message. + */ + public ClassCastException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ClassCastException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassCircularityError.java b/libjava/classpath/java/lang/ClassCircularityError.java new file mode 100644 index 00000000000..ecdfb7aaf3a --- /dev/null +++ b/libjava/classpath/java/lang/ClassCircularityError.java @@ -0,0 +1,73 @@ +/* ClassCircularityError.java -- thrown when linking circular classes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>ClassCircularityError</code> is thrown when a circular dependency + * has been detected while initializing a class. This signals binary + * incompatible versions of class files, as the compiler normally catches this. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class ClassCircularityError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1054362542914539689L; + + /** + * Create an error without a message. + */ + public ClassCircularityError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public ClassCircularityError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassFormatError.java b/libjava/classpath/java/lang/ClassFormatError.java new file mode 100644 index 00000000000..7f90f5cd83e --- /dev/null +++ b/libjava/classpath/java/lang/ClassFormatError.java @@ -0,0 +1,72 @@ +/* ClassFormatError.java -- thrown if a class file is invalid + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>ClassFormatError</code> is thrown when a Java Virtual Machine + * unable to read a class file because the file is corrupted or cannot be + * interpreted as a class file. + * + * @author Brian Jones + * @status updated to 1.4 + */ +public class ClassFormatError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8420114879011949195L; + + /** + * Create an error without a message. + */ + public ClassFormatError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public ClassFormatError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassLoader.java b/libjava/classpath/java/lang/ClassLoader.java new file mode 100644 index 00000000000..b644511cf27 --- /dev/null +++ b/libjava/classpath/java/lang/ClassLoader.java @@ -0,0 +1,1116 @@ +/* ClassLoader.java -- responsible for loading classes into the VM + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; +import gnu.java.util.DoubleEnumeration; +import gnu.java.util.EmptyEnumeration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * The ClassLoader is a way of customizing the way Java gets its classes + * and loads them into memory. The verifier and other standard Java things + * still run, but the ClassLoader is allowed great flexibility in determining + * where to get the classfiles and when to load and resolve them. For that + * matter, a custom ClassLoader can perform on-the-fly code generation or + * modification! + * + * <p>Every classloader has a parent classloader that is consulted before + * the 'child' classloader when classes or resources should be loaded. + * This is done to make sure that classes can be loaded from an hierarchy of + * multiple classloaders and classloaders do not accidentially redefine + * already loaded classes by classloaders higher in the hierarchy. + * + * <p>The grandparent of all classloaders is the bootstrap classloader, which + * loads all the standard system classes as implemented by GNU Classpath. The + * other special classloader is the system classloader (also called + * application classloader) that loads all classes from the CLASSPATH + * (<code>java.class.path</code> system property). The system classloader + * is responsible for finding the application classes from the classpath, + * and delegates all requests for the standard library classes to its parent + * the bootstrap classloader. Most programs will load all their classes + * through the system classloaders. + * + * <p>The bootstrap classloader in GNU Classpath is implemented as a couple of + * static (native) methods on the package private class + * <code>java.lang.VMClassLoader</code>, the system classloader is an + * anonymous inner class of ClassLoader and a subclass of + * <code>java.net.URLClassLoader</code>. + * + * <p>Users of a <code>ClassLoader</code> will normally just use the methods + * <ul> + * <li> <code>loadClass()</code> to load a class.</li> + * <li> <code>getResource()</code> or <code>getResourceAsStream()</code> + * to access a resource.</li> + * <li> <code>getResources()</code> to get an Enumeration of URLs to all + * the resources provided by the classloader and its parents with the + * same name.</li> + * </ul> + * + * <p>Subclasses should implement the methods + * <ul> + * <li> <code>findClass()</code> which is called by <code>loadClass()</code> + * when the parent classloader cannot provide a named class.</li> + * <li> <code>findResource()</code> which is called by + * <code>getResource()</code> when the parent classloader cannot provide + * a named resource.</li> + * <li> <code>findResources()</code> which is called by + * <code>getResource()</code> to combine all the resources with the + * same name from the classloader and its parents.</li> + * <li> <code>findLibrary()</code> which is called by + * <code>Runtime.loadLibrary()</code> when a class defined by the + * classloader wants to load a native library.</li> + * </ul> + * + * @author John Keiser + * @author Mark Wielaard + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class + * @since 1.0 + * @status still missing 1.4 functionality + */ +public abstract class ClassLoader +{ + /** + * All classes loaded by this classloader. VM's may choose to implement + * this cache natively; but it is here available for use if necessary. It + * is not private in order to allow native code (and trusted subclasses) + * access to this field. + */ + final HashMap loadedClasses = new HashMap(); + + /** + * All packages defined by this classloader. It is not private in order to + * allow native code (and trusted subclasses) access to this field. + */ + final HashMap definedPackages = new HashMap(); + + /** + * The classloader that is consulted before this classloader. + * If null then the parent is the bootstrap classloader. + */ + private final ClassLoader parent; + + /** + * This is true if this classloader was successfully initialized. + * This flag is needed to avoid a class loader attack: even if the + * security manager rejects an attempt to create a class loader, the + * malicious class could have a finalize method which proceeds to + * define classes. + */ + private final boolean initialized; + + static class StaticData + { + /** + * The System Class Loader (a.k.a. Application Class Loader). The one + * returned by ClassLoader.getSystemClassLoader. + */ + static final ClassLoader systemClassLoader = + VMClassLoader.getSystemClassLoader(); + static + { + // Find out if we have to install a default security manager. Note that + // this is done here because we potentially need the system class loader + // to load the security manager and note also that we don't need the + // security manager until the system class loader is created. + // If the runtime chooses to use a class loader that doesn't have the + // system class loader as its parent, it is responsible for setting + // up a security manager before doing so. + String secman = SystemProperties.getProperty("java.security.manager"); + if (secman != null && SecurityManager.current == null) + { + if (secman.equals("") || secman.equals("default")) + { + SecurityManager.current = new SecurityManager(); + } + else + { + try + { + Class cl = Class.forName(secman, false, StaticData.systemClassLoader); + SecurityManager.current = (SecurityManager)cl.newInstance(); + } + catch (Exception x) + { + throw (InternalError) + new InternalError("Unable to create SecurityManager") + .initCause(x); + } + } + } + } + + /** + * The default protection domain, used when defining a class with a null + * parameter for the domain. + */ + static final ProtectionDomain defaultProtectionDomain; + static + { + CodeSource cs = new CodeSource(null, null); + PermissionCollection perm = Policy.getPolicy().getPermissions(cs); + defaultProtectionDomain = new ProtectionDomain(cs, perm); + } + /** + * The command-line state of the package assertion status overrides. This + * map is never modified, so it does not need to be synchronized. + */ + // Package visible for use by Class. + static final Map systemPackageAssertionStatus + = VMClassLoader.packageAssertionStatus(); + /** + * The command-line state of the class assertion status overrides. This + * map is never modified, so it does not need to be synchronized. + */ + // Package visible for use by Class. + static final Map systemClassAssertionStatus + = VMClassLoader.classAssertionStatus(); + } + + /** + * The desired assertion status of classes loaded by this loader, if not + * overridden by package or class instructions. + */ + // Package visible for use by Class. + boolean defaultAssertionStatus = VMClassLoader.defaultAssertionStatus(); + + /** + * The map of package assertion status overrides, or null if no package + * overrides have been specified yet. The values of the map should be + * Boolean.TRUE or Boolean.FALSE, and the unnamed package is represented + * by the null key. This map must be synchronized on this instance. + */ + // Package visible for use by Class. + Map packageAssertionStatus; + + /** + * The map of class assertion status overrides, or null if no class + * overrides have been specified yet. The values of the map should be + * Boolean.TRUE or Boolean.FALSE. This map must be synchronized on this + * instance. + */ + // Package visible for use by Class. + Map classAssertionStatus; + + /** + * VM private data. + */ + transient Object vmdata; + + /** + * Create a new ClassLoader with as parent the system classloader. There + * may be a security check for <code>checkCreateClassLoader</code>. + * + * @throws SecurityException if the security check fails + */ + protected ClassLoader() throws SecurityException + { + this(StaticData.systemClassLoader); + } + + /** + * Create a new ClassLoader with the specified parent. The parent will + * be consulted when a class or resource is requested through + * <code>loadClass()</code> or <code>getResource()</code>. Only when the + * parent classloader cannot provide the requested class or resource the + * <code>findClass()</code> or <code>findResource()</code> method + * of this classloader will be called. There may be a security check for + * <code>checkCreateClassLoader</code>. + * + * @param parent the classloader's parent, or null for the bootstrap + * classloader + * @throws SecurityException if the security check fails + * @since 1.2 + */ + protected ClassLoader(ClassLoader parent) + { + // May we create a new classloader? + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkCreateClassLoader(); + this.parent = parent; + this.initialized = true; + } + + /** + * Load a class using this ClassLoader or its parent, without resolving + * it. Calls <code>loadClass(name, false)</code>. + * + * <p>Subclasses should not override this method but should override + * <code>findClass()</code> which is called by this method.</p> + * + * @param name the name of the class relative to this ClassLoader + * @return the loaded class + * @throws ClassNotFoundException if the class cannot be found + */ + public Class loadClass(String name) throws ClassNotFoundException + { + return loadClass(name, false); + } + + /** + * Load a class using this ClassLoader or its parent, possibly resolving + * it as well using <code>resolveClass()</code>. It first tries to find + * out if the class has already been loaded through this classloader by + * calling <code>findLoadedClass()</code>. Then it calls + * <code>loadClass()</code> on the parent classloader (or when there is + * no parent it uses the VM bootclassloader). If the class is still + * not loaded it tries to create a new class by calling + * <code>findClass()</code>. Finally when <code>resolve</code> is + * <code>true</code> it also calls <code>resolveClass()</code> on the + * newly loaded class. + * + * <p>Subclasses should not override this method but should override + * <code>findClass()</code> which is called by this method.</p> + * + * @param name the fully qualified name of the class to load + * @param resolve whether or not to resolve the class + * @return the loaded class + * @throws ClassNotFoundException if the class cannot be found + */ + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // Have we already loaded this class? + Class c = findLoadedClass(name); + if (c == null) + { + // Can the class be loaded by a parent? + try + { + if (parent == null) + { + c = VMClassLoader.loadClass(name, resolve); + if (c != null) + return c; + } + else + { + return parent.loadClass(name, resolve); + } + } + catch (ClassNotFoundException e) + { + } + // Still not found, we have to do it ourself. + c = findClass(name); + } + if (resolve) + resolveClass(c); + return c; + } + + /** + * Called for every class name that is needed but has not yet been + * defined by this classloader or one of its parents. It is called by + * <code>loadClass()</code> after both <code>findLoadedClass()</code> and + * <code>parent.loadClass()</code> couldn't provide the requested class. + * + * <p>The default implementation throws a + * <code>ClassNotFoundException</code>. Subclasses should override this + * method. An implementation of this method in a subclass should get the + * class bytes of the class (if it can find them), if the package of the + * requested class doesn't exist it should define the package and finally + * it should call define the actual class. It does not have to resolve the + * class. It should look something like the following:<br> + * + * <pre> + * // Get the bytes that describe the requested class + * byte[] classBytes = classLoaderSpecificWayToFindClassBytes(name); + * // Get the package name + * int lastDot = name.lastIndexOf('.'); + * if (lastDot != -1) + * { + * String packageName = name.substring(0, lastDot); + * // Look if the package already exists + * if (getPackage(packageName) == null) + * { + * // define the package + * definePackage(packageName, ...); + * } + * } + * // Define and return the class + * return defineClass(name, classBytes, 0, classBytes.length); + * </pre> + * + * <p><code>loadClass()</code> makes sure that the <code>Class</code> + * returned by <code>findClass()</code> will later be returned by + * <code>findLoadedClass()</code> when the same class name is requested. + * + * @param name class name to find (including the package name) + * @return the requested Class + * @throws ClassNotFoundException when the class can not be found + * @since 1.2 + */ + protected Class findClass(String name) throws ClassNotFoundException + { + throw new ClassNotFoundException(name); + } + + /** + * Helper to define a class using a string of bytes. This version is not + * secure. + * + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @deprecated use {@link #defineClass(String, byte[], int, int)} instead + */ + protected final Class defineClass(byte[] data, int offset, int len) + throws ClassFormatError + { + return defineClass(null, data, offset, len); + } + + /** + * Helper to define a class using a string of bytes without a + * ProtectionDomain. Subclasses should call this method from their + * <code>findClass()</code> implementation. The name should use '.' + * separators, and discard the trailing ".class". The default protection + * domain has the permissions of + * <code>Policy.getPolicy().getPermissions(new CodeSource(null, null))</code>. + * + * @param name the name to give the class, or null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @throws SecurityException if name starts with "java." + * @since 1.1 + */ + protected final Class defineClass(String name, byte[] data, int offset, + int len) throws ClassFormatError + { + return defineClass(name, data, offset, len, null); + } + + /** + * Helper to define a class using a string of bytes. Subclasses should call + * this method from their <code>findClass()</code> implementation. If the + * domain is null, the default of + * <code>Policy.getPolicy().getPermissions(new CodeSource(null, null))</code> + * is used. Once a class has been defined in a package, all further classes + * in that package must have the same set of certificates or a + * SecurityException is thrown. + * + * @param name the name to give the class. null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @param domain the ProtectionDomain to give to the class, null for the + * default protection domain + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @throws SecurityException if name starts with "java.", or if certificates + * do not match up + * @since 1.2 + */ + protected final synchronized Class defineClass(String name, byte[] data, + int offset, int len, + ProtectionDomain domain) + throws ClassFormatError + { + if (domain == null) + domain = StaticData.defaultProtectionDomain; + if (! initialized) + throw new SecurityException("attempt to define class from uninitialized class loader"); + + Class retval = VMClassLoader.defineClass(this, name, data, + offset, len, domain); + loadedClasses.put(retval.getName(), retval); + return retval; + } + + /** + * Links the class, if that has not already been done. Linking basically + * resolves all references to other classes made by this class. + * + * @param c the class to resolve + * @throws NullPointerException if c is null + * @throws LinkageError if linking fails + */ + protected final void resolveClass(Class c) + { + VMClassLoader.resolveClass(c); + } + + /** + * Helper to find a Class using the system classloader, possibly loading it. + * A subclass usually does not need to call this, if it correctly + * overrides <code>findClass(String)</code>. + * + * @param name the name of the class to find + * @return the found class + * @throws ClassNotFoundException if the class cannot be found + */ + protected final Class findSystemClass(String name) + throws ClassNotFoundException + { + return Class.forName(name, false, StaticData.systemClassLoader); + } + + /** + * Returns the parent of this classloader. If the parent of this + * classloader is the bootstrap classloader then this method returns + * <code>null</code>. A security check may be performed on + * <code>RuntimePermission("getClassLoader")</code>. + * + * @return the parent <code>ClassLoader</code> + * @throws SecurityException if the security check fails + * @since 1.2 + */ + public final ClassLoader getParent() + { + // Check if we may return the parent classloader. + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && ! cl.isAncestorOf(this)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return parent; + } + + /** + * Helper to set the signers of a class. This should be called after + * defining the class. + * + * @param c the Class to set signers of + * @param signers the signers to set + * @since 1.1 + */ + protected final void setSigners(Class c, Object[] signers) + { + c.setSigners(signers); + } + + /** + * Helper to find an already-loaded class in this ClassLoader. + * + * @param name the name of the class to find + * @return the found Class, or null if it is not found + * @since 1.1 + */ + protected final synchronized Class findLoadedClass(String name) + { + // NOTE: If the VM is keeping its own cache, it may make sense to have + // this method be native. + return (Class) loadedClasses.get(name); + } + + /** + * Get the URL to a resource using this classloader or one of its parents. + * First tries to get the resource by calling <code>getResource()</code> + * on the parent classloader. If the parent classloader returns null then + * it tries finding the resource by calling <code>findResource()</code> on + * this classloader. The resource name should be separated by '/' for path + * elements. + * + * <p>Subclasses should not override this method but should override + * <code>findResource()</code> which is called by this method. + * + * @param name the name of the resource relative to this classloader + * @return the URL to the resource or null when not found + */ + public URL getResource(String name) + { + URL result; + + if (parent == null) + result = VMClassLoader.getResource(name); + else + result = parent.getResource(name); + + if (result == null) + result = findResource(name); + return result; + } + + /** + * Returns an Enumeration of all resources with a given name that can + * be found by this classloader and its parents. Certain classloaders + * (such as the URLClassLoader when given multiple jar files) can have + * multiple resources with the same name that come from multiple locations. + * It can also occur that a parent classloader offers a resource with a + * certain name and the child classloader also offers a resource with that + * same name. <code>getResource()</code> only offers the first resource (of the + * parent) with a given name. This method lists all resources with the + * same name. The name should use '/' as path separators. + * + * <p>The Enumeration is created by first calling <code>getResources()</code> + * on the parent classloader and then calling <code>findResources()</code> + * on this classloader.</p> + * + * @param name the resource name + * @return an enumaration of all resources found + * @throws IOException if I/O errors occur in the process + * @since 1.2 + */ + public final Enumeration getResources(String name) throws IOException + { + Enumeration parentResources; + if (parent == null) + parentResources = VMClassLoader.getResources(name); + else + parentResources = parent.getResources(name); + return new DoubleEnumeration(parentResources, findResources(name)); + } + + /** + * Called whenever all locations of a named resource are needed. + * It is called by <code>getResources()</code> after it has called + * <code>parent.getResources()</code>. The results are combined by + * the <code>getResources()</code> method. + * + * <p>The default implementation always returns an empty Enumeration. + * Subclasses should override it when they can provide an Enumeration of + * URLs (possibly just one element) to the named resource. + * The first URL of the Enumeration should be the same as the one + * returned by <code>findResource</code>. + * + * @param name the name of the resource to be found + * @return a possibly empty Enumeration of URLs to the named resource + * @throws IOException if I/O errors occur in the process + * @since 1.2 + */ + protected Enumeration findResources(String name) throws IOException + { + return EmptyEnumeration.getInstance(); + } + + /** + * Called whenever a resource is needed that could not be provided by + * one of the parents of this classloader. It is called by + * <code>getResource()</code> after <code>parent.getResource()</code> + * couldn't provide the requested resource. + * + * <p>The default implementation always returns null. Subclasses should + * override this method when they can provide a way to return a URL + * to a named resource. + * + * @param name the name of the resource to be found + * @return a URL to the named resource or null when not found + * @since 1.2 + */ + protected URL findResource(String name) + { + return null; + } + + /** + * Get the URL to a resource using the system classloader. + * + * @param name the name of the resource relative to the system classloader + * @return the URL to the resource + * @since 1.1 + */ + public static final URL getSystemResource(String name) + { + return StaticData.systemClassLoader.getResource(name); + } + + /** + * Get an Enumeration of URLs to resources with a given name using the + * the system classloader. The enumeration firsts lists the resources with + * the given name that can be found by the bootstrap classloader followed + * by the resources with the given name that can be found on the classpath. + * + * @param name the name of the resource relative to the system classloader + * @return an Enumeration of URLs to the resources + * @throws IOException if I/O errors occur in the process + * @since 1.2 + */ + public static Enumeration getSystemResources(String name) throws IOException + { + return StaticData.systemClassLoader.getResources(name); + } + + /** + * Get a resource as stream using this classloader or one of its parents. + * First calls <code>getResource()</code> and if that returns a URL to + * the resource then it calls and returns the InputStream given by + * <code>URL.openStream()</code>. + * + * <p>Subclasses should not override this method but should override + * <code>findResource()</code> which is called by this method. + * + * @param name the name of the resource relative to this classloader + * @return an InputStream to the resource, or null + * @since 1.1 + */ + public InputStream getResourceAsStream(String name) + { + try + { + URL url = getResource(name); + if (url == null) + return null; + return url.openStream(); + } + catch (IOException e) + { + return null; + } + } + + /** + * Get a resource using the system classloader. + * + * @param name the name of the resource relative to the system classloader + * @return an input stream for the resource, or null + * @since 1.1 + */ + public static final InputStream getSystemResourceAsStream(String name) + { + try + { + URL url = getSystemResource(name); + if (url == null) + return null; + return url.openStream(); + } + catch (IOException e) + { + return null; + } + } + + /** + * Returns the system classloader. The system classloader (also called + * the application classloader) is the classloader that is used to + * load the application classes on the classpath (given by the system + * property <code>java.class.path</code>. This is set as the context + * class loader for a thread. The system property + * <code>java.system.class.loader</code>, if defined, is taken to be the + * name of the class to use as the system class loader, which must have + * a public constructor which takes a ClassLoader as a parent. The parent + * class loader passed in the constructor is the default system class + * loader. + * + * <p>Note that this is different from the bootstrap classloader that + * actually loads all the real "system" classes. + * + * <p>A security check will be performed for + * <code>RuntimePermission("getClassLoader")</code> if the calling class + * is not a parent of the system class loader. + * + * @return the system class loader + * @throws SecurityException if the security check fails + * @throws IllegalStateException if this is called recursively + * @throws Error if <code>java.system.class.loader</code> fails to load + * @since 1.2 + */ + public static ClassLoader getSystemClassLoader() + { + // Check if we may return the system classloader + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && cl != StaticData.systemClassLoader) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + + return StaticData.systemClassLoader; + } + + /** + * Defines a new package and creates a Package object. The package should + * be defined before any class in the package is defined with + * <code>defineClass()</code>. The package should not yet be defined + * before in this classloader or in one of its parents (which means that + * <code>getPackage()</code> should return <code>null</code>). All + * parameters except the <code>name</code> of the package may be + * <code>null</code>. + * + * <p>Subclasses should call this method from their <code>findClass()</code> + * implementation before calling <code>defineClass()</code> on a Class + * in a not yet defined Package (which can be checked by calling + * <code>getPackage()</code>). + * + * @param name the name of the Package + * @param specTitle the name of the specification + * @param specVendor the name of the specification designer + * @param specVersion the version of this specification + * @param implTitle the name of the implementation + * @param implVendor the vendor that wrote this implementation + * @param implVersion the version of this implementation + * @param sealed if sealed the origin of the package classes + * @return the Package object for the specified package + * @throws IllegalArgumentException if the package name is null or it + * was already defined by this classloader or one of its parents + * @see Package + * @since 1.2 + */ + protected Package definePackage(String name, String specTitle, + String specVendor, String specVersion, + String implTitle, String implVendor, + String implVersion, URL sealed) + { + if (getPackage(name) != null) + throw new IllegalArgumentException("Package " + name + + " already defined"); + Package p = new Package(name, specTitle, specVendor, specVersion, + implTitle, implVendor, implVersion, sealed); + synchronized (definedPackages) + { + definedPackages.put(name, p); + } + return p; + } + + /** + * Returns the Package object for the requested package name. It returns + * null when the package is not defined by this classloader or one of its + * parents. + * + * @param name the package name to find + * @return the package, if defined + * @since 1.2 + */ + protected Package getPackage(String name) + { + Package p; + if (parent == null) + p = VMClassLoader.getPackage(name); + else + p = parent.getPackage(name); + + if (p == null) + { + synchronized (definedPackages) + { + p = (Package) definedPackages.get(name); + } + } + return p; + } + + /** + * Returns all Package objects defined by this classloader and its parents. + * + * @return an array of all defined packages + * @since 1.2 + */ + protected Package[] getPackages() + { + // Get all our packages. + Package[] packages; + synchronized(definedPackages) + { + packages = new Package[definedPackages.size()]; + definedPackages.values().toArray(packages); + } + + // If we have a parent get all packages defined by our parents. + Package[] parentPackages; + if (parent == null) + parentPackages = VMClassLoader.getPackages(); + else + parentPackages = parent.getPackages(); + + Package[] allPackages = new Package[parentPackages.length + + packages.length]; + System.arraycopy(parentPackages, 0, allPackages, 0, + parentPackages.length); + System.arraycopy(packages, 0, allPackages, parentPackages.length, + packages.length); + return allPackages; + } + + /** + * Called by <code>Runtime.loadLibrary()</code> to get an absolute path + * to a (system specific) library that was requested by a class loaded + * by this classloader. The default implementation returns + * <code>null</code>. It should be implemented by subclasses when they + * have a way to find the absolute path to a library. If this method + * returns null the library is searched for in the default locations + * (the directories listed in the <code>java.library.path</code> system + * property). + * + * @param name the (system specific) name of the requested library + * @return the full pathname to the requested library, or null + * @see Runtime#loadLibrary() + * @since 1.2 + */ + protected String findLibrary(String name) + { + return null; + } + + /** + * Set the default assertion status for classes loaded by this classloader, + * used unless overridden by a package or class request. + * + * @param enabled true to set the default to enabled + * @see #setClassAssertionStatus(String, boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public void setDefaultAssertionStatus(boolean enabled) + { + defaultAssertionStatus = enabled; + } + + /** + * Set the default assertion status for packages, used unless overridden + * by a class request. This default also covers subpackages, unless they + * are also specified. The unnamed package should use null for the name. + * + * @param name the package (and subpackages) to affect + * @param enabled true to set the default to enabled + * @see #setDefaultAssertionStatus(String, boolean) + * @see #setClassAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public synchronized void setPackageAssertionStatus(String name, + boolean enabled) + { + if (packageAssertionStatus == null) + packageAssertionStatus + = new HashMap(StaticData.systemPackageAssertionStatus); + packageAssertionStatus.put(name, Boolean.valueOf(enabled)); + } + + /** + * Set the default assertion status for a class. This only affects the + * status of top-level classes, any other string is harmless. + * + * @param name the class to affect + * @param enabled true to set the default to enabled + * @throws NullPointerException if name is null + * @see #setDefaultAssertionStatus(String, boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public synchronized void setClassAssertionStatus(String name, + boolean enabled) + { + if (classAssertionStatus == null) + classAssertionStatus = + new HashMap(StaticData.systemClassAssertionStatus); + // The toString() hack catches null, as required. + classAssertionStatus.put(name.toString(), Boolean.valueOf(enabled)); + } + + /** + * Resets the default assertion status of this classloader, its packages + * and classes, all to false. This allows overriding defaults inherited + * from the command line. + * + * @see #setDefaultAssertionStatus(boolean) + * @see #setClassAssertionStatus(String, boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @since 1.4 + */ + public synchronized void clearAssertionStatus() + { + defaultAssertionStatus = false; + packageAssertionStatus = new HashMap(); + classAssertionStatus = new HashMap(); + } + + /** + * Return true if this loader is either the specified class loader + * or an ancestor thereof. + * @param loader the class loader to check + */ + final boolean isAncestorOf(ClassLoader loader) + { + while (loader != null) + { + if (this == loader) + return true; + loader = loader.parent; + } + return false; + } + + private static URL[] getExtClassLoaderUrls() + { + String classpath = SystemProperties.getProperty("java.ext.dirs", ""); + StringTokenizer tok = new StringTokenizer(classpath, File.pathSeparator); + ArrayList list = new ArrayList(); + while (tok.hasMoreTokens()) + { + try + { + File f = new File(tok.nextToken()); + File[] files = f.listFiles(); + if (files != null) + for (int i = 0; i < files.length; i++) + list.add(files[i].toURL()); + } + catch(Exception x) + { + } + } + URL[] urls = new URL[list.size()]; + list.toArray(urls); + return urls; + } + + private static void addFileURL(ArrayList list, String file) + { + try + { + list.add(new File(file).toURL()); + } + catch(java.net.MalformedURLException x) + { + } + } + + private static URL[] getSystemClassLoaderUrls() + { + String classpath = SystemProperties.getProperty("java.class.path", "."); + StringTokenizer tok = new StringTokenizer(classpath, File.pathSeparator, true); + ArrayList list = new ArrayList(); + while (tok.hasMoreTokens()) + { + String s = tok.nextToken(); + if (s.equals(File.pathSeparator)) + addFileURL(list, "."); + else + { + addFileURL(list, s); + if (tok.hasMoreTokens()) + { + // Skip the separator. + tok.nextToken(); + // If the classpath ended with a separator, + // append the current directory. + if (!tok.hasMoreTokens()) + addFileURL(list, "."); + } + } + } + URL[] urls = new URL[list.size()]; + list.toArray(urls); + return urls; + } + + static ClassLoader defaultGetSystemClassLoader() + { + return createAuxiliarySystemClassLoader( + createSystemClassLoader(getSystemClassLoaderUrls(), + createExtClassLoader(getExtClassLoaderUrls(), null))); + } + + static ClassLoader createExtClassLoader(URL[] urls, ClassLoader parent) + { + if (urls.length > 0) + return new URLClassLoader(urls, parent); + else + return parent; + } + + static ClassLoader createSystemClassLoader(URL[] urls, ClassLoader parent) + { + return + new URLClassLoader(urls, parent) + { + protected synchronized Class loadClass(String name, + boolean resolve) + throws ClassNotFoundException + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) + sm.checkPackageAccess(name.substring(0, lastDot)); + } + return super.loadClass(name, resolve); + } + }; + } + + static ClassLoader createAuxiliarySystemClassLoader(ClassLoader parent) + { + String loader = SystemProperties.getProperty("java.system.class.loader", null); + if (loader == null) + { + return parent; + } + try + { + Constructor c = Class.forName(loader, false, parent) + .getConstructor(new Class[] { ClassLoader.class }); + return (ClassLoader)c.newInstance(new Object[] { parent }); + } + catch (Exception e) + { + System.err.println("Requested system classloader " + loader + " failed."); + throw (Error) + new Error("Requested system classloader " + loader + " failed.") + .initCause(e); + } + } +} diff --git a/libjava/classpath/java/lang/ClassNotFoundException.java b/libjava/classpath/java/lang/ClassNotFoundException.java new file mode 100644 index 00000000000..6b6ae949dd2 --- /dev/null +++ b/libjava/classpath/java/lang/ClassNotFoundException.java @@ -0,0 +1,125 @@ +/* ClassNotFoundException.java -- thrown when class definition cannot be found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a class is requested by reflection, but the class definition + * cannot be found. This exception is often chained from another Throwable. + * + * @author Brian Jones + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class#forName(String) + * @see ClassLoader#findSystemClass(String) + * @see ClassLoader#loadClass(String, boolean) + * @status updated to 1.4 + */ +public class ClassNotFoundException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 9176873029745254542L; + + /** + * The cause of this exception (duplicates the one stored in Throwable). + * + * @serial the exception cause + * @since 1.2 + */ + private final Throwable ex; + + /** + * Create an exception without a message. Note that this initializes the + * cause to null. + */ + public ClassNotFoundException() + { + this(null, null); + } + + /** + * Create an exception with a message. Note that this initializes the + * cause to null. + * + * @param s the message + */ + public ClassNotFoundException(String s) + { + this(s, null); + } + + /** + * Create an exception with a message and chain it to the exception + * which occurred while loading the class. + * + * @param s the message + * @param ex the chained exception + * @since 1.2 + */ + public ClassNotFoundException(String s, Throwable ex) + { + super(s, ex); + this.ex = ex; + } + + /** + * Returns the exception which occurred while loading the class, + * otherwise returns null. This is a legacy method; the preferred choice + * now is {@link Throwable#getCause()}. + * + * @return the cause of this exception + * @since 1.2 + */ + public Throwable getException() + { + return ex; + } + + /** + * Returns the exception which occurred while loading the class, + * otherwise returns null. + * + * @return the cause of this exception + * @since 1.4 + */ + public Throwable getCause() + { + return ex; + } +} diff --git a/libjava/classpath/java/lang/CloneNotSupportedException.java b/libjava/classpath/java/lang/CloneNotSupportedException.java new file mode 100644 index 00000000000..9d10cf389f9 --- /dev/null +++ b/libjava/classpath/java/lang/CloneNotSupportedException.java @@ -0,0 +1,92 @@ +/* CloneNotSupportedException.java -- thrown when an object cannot be cloned + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate an object should not or could not be cloned. This + * includes the case when {@link Object#clone()} is called on an object + * which does not implement the {@link Cloneable} interface. For example:<br> + * <pre> + * void m() throws CloneNotSupportedException + * { + * clone(); + * } + * </pre> + * + * <p>Notice that calling <code>clone()</code> on an array will never produce + * this exception, as the VM will always succeed in copying the array, or + * cause an OutOfMemoryError first. For example:<br> + * <pre> + * void m(int[] array) + * { + * int[] copy = (int[]) array.clone(); + * } + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Cloneable + * @see Object#clone() + * @status updated to 1.4 + */ +public class CloneNotSupportedException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5195511250079656443L; + + /** + * Create an exception without a message. + */ + public CloneNotSupportedException() + { + } + + /** + * Create an exception with a message. + * + * @param s the error message + */ + public CloneNotSupportedException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Cloneable.java b/libjava/classpath/java/lang/Cloneable.java new file mode 100644 index 00000000000..10f20ce3b6e --- /dev/null +++ b/libjava/classpath/java/lang/Cloneable.java @@ -0,0 +1,78 @@ +/* Cloneable.java -- Interface for marking objects cloneable by Object.clone() + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This interface should be implemented by classes wishing to + * support of override <code>Object.clone()</code>. The default + * behaviour of <code>clone()</code> performs a shallow copy, but + * subclasses often change this to perform a deep copy. Therefore, + * it is a good idea to document how deep your clone will go. + * If <code>clone()</code> is called on an object which does not + * implement this interface, a <code>CloneNotSupportedException</code> + * will be thrown. + * + * <p>This interface is simply a tagging interface; it carries no + * requirements on methods to implement. However, it is typical for + * a Cloneable class to implement at least <code>equals</code>, + * <code>hashCode</code>, and <code>clone</code>, sometimes + * increasing the accessibility of clone to be public. The typical + * implementation of <code>clone</code> invokes <code>super.clone()</code> + * rather than a constructor, but this is not a requirement. + * + * <p>If an object that implement Cloneable should not be cloned, + * simply override the <code>clone</code> method to throw a + * <code>CloneNotSupportedException</code>. + * + * <p>All array types implement Cloneable, and have a public + * <code>clone</code> method that will never fail with a + * <code>CloneNotSupportedException</code>. + * + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Warren Levy (warrenl@cygnus.com) + * @see Object#clone() + * @see CloneNotSupportedException + * @since 1.0 + * @status updated to 1.4 + */ +public interface Cloneable +{ + // Tagging interface only. +} diff --git a/libjava/classpath/java/lang/Comparable.java b/libjava/classpath/java/lang/Comparable.java new file mode 100644 index 00000000000..a8afe1ec33e --- /dev/null +++ b/libjava/classpath/java/lang/Comparable.java @@ -0,0 +1,98 @@ +/* Comparable.java -- Interface for comparaing objects to obtain an ordering + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Interface for objects that can be ordering among other objects. The + * ordering can be <em>total</em>, such that two objects only compare equal + * if they are also equal by the equals method, or <em>partial</em> such + * that this is not necessarily true. For example, a case-sensitive + * dictionary order comparison of Strings is total, but if it is + * case-insensitive it is partial, because "abc" and "ABC" compare as + * equal even though "abc".equals("ABC") returns false. However, if you use + * a partial ordering, it is a good idea to document your class as + * "inconsistent with equals", because the behavior of your class in a + * SortedMap will be different than in a HashMap. + * + * <p>Lists, arrays, and sets of objects that implement this interface can + * be sorted automatically, without the need for an explicit + * {@link java.util.Comparator}. Note that <code>e1.compareTo(null)</code> + * should throw an Exception; as should comparison between incompatible + * classes. + * + * @author Geoff Berry + * @author Warren Levy (warrenl@cygnus.com) + * @see java.util.Comparator + * @see java.util.Collections#sort(java.util.List) + * @see java.util.Arrays#sort(Object[]) + * @see java.util.SortedSet + * @see java.util.SortedMap + * @see java.util.TreeSet + * @see java.util.TreeMap + * @since 1.2 + * @status updated to 1.4 + */ +public interface Comparable +{ + /** + * Compares this object with another, and returns a numerical result based + * on the comparison. If the result is negative, this object sorts less + * than the other; if 0, the two are equal, and if positive, this object + * sorts greater than the other. To translate this into boolean, simply + * perform <code>o1.compareTo(o2) <em><op></em> 0</code>, where op + * is one of <, <=, =, !=, >, or >=. + * + * <p>You must make sure that the comparison is mutual, ie. + * <code>sgn(x.compareTo(y)) == -sgn(y.compareTo(x))</code> (where sgn() is + * defined as -1, 0, or 1 based on the sign). This includes throwing an + * exception in either direction if the two are not comparable; hence, + * <code>compareTo(null)</code> should always throw an Exception. + * + * <p>You should also ensure transitivity, in two forms: + * <code>x.compareTo(y) > 0 && y.compareTo(z) > 0</code> implies + * <code>x.compareTo(z) > 0</code>; and <code>x.compareTo(y) == 0</code> + * implies <code>x.compareTo(z) == y.compareTo(z)</code>. + * + * @param o the object to be compared + * @return an integer describing the comparison + * @throws NullPointerException if o is null + * @throws ClassCastException if o cannot be compared + */ + int compareTo(Object o); +} diff --git a/libjava/classpath/java/lang/Compiler.java b/libjava/classpath/java/lang/Compiler.java new file mode 100644 index 00000000000..56fb951bbd9 --- /dev/null +++ b/libjava/classpath/java/lang/Compiler.java @@ -0,0 +1,127 @@ +/* Compiler.java -- placeholder for Java-to-native runtime compilers + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The <code>Compiler</code> class is a placeholder for a JIT compiler + * implementation, and does nothing unless there is such a compiler. + * + * <p>The system property <code>java.compiler</code> may contain the name + * of a library to load with <code>System.loadLibrary</code> when the + * virtual machine first starts. If so, and loading the library succeeds, + * then a function by the name of <code>java_lang_Compiler_start()</code> + * in that library is called. + * + * <p>Note that a VM might not have implemented any of this. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see System#getProperty(String) + * @see System#getProperty(String, String) + * @see System#loadLibrary(String) + * @since JDK 1.0 + * @status updated to 1.4 + */ +public final class Compiler +{ + /** + * Don't allow new `Compiler's to be made. + */ + private Compiler() + { + } + + /** + * Compile the class named by <code>oneClass</code>. + * + * @param oneClass the class to compile + * @return <code>false</code> if no compiler is available or + * compilation failed, <code>true</code> if compilation succeeded + * @throws NullPointerException if oneClass is null + */ + public static boolean compileClass(Class oneClass) + { + return VMCompiler.compileClass(oneClass); + } + + /** + * Compile the classes whose name matches <code>classNames</code>. + * + * @param classNames the name of classes to compile + * @return <code>false</code> if no compiler is available or + * compilation failed, <code>true</code> if compilation succeeded + * @throws NullPointerException if classNames is null + */ + public static boolean compileClasses(String classNames) + { + return VMCompiler.compileClasses(classNames); + } + + /** + * This method examines the argument and performs an operation + * according to the compilers documentation. No specific operation + * is required. + * + * @param arg a compiler-specific argument + * @return a compiler-specific value, including null + * @throws NullPointerException if the compiler doesn't like a null arg + */ + public static Object command(Object arg) + { + return VMCompiler.command(arg); + } + + /** + * Calling <code>Compiler.enable()</code> will cause the compiler + * to resume operation if it was previously disabled; provided that a + * compiler even exists. + */ + public static void enable() + { + VMCompiler.enable(); + } + + /** + * Calling <code>Compiler.disable()</code> will cause the compiler + * to be suspended; provided that a compiler even exists. + */ + public static void disable() + { + VMCompiler.disable(); + } +} diff --git a/libjava/classpath/java/lang/Double.java b/libjava/classpath/java/lang/Double.java new file mode 100644 index 00000000000..4fa47f46ddd --- /dev/null +++ b/libjava/classpath/java/lang/Double.java @@ -0,0 +1,521 @@ +/* Double.java -- object wrapper for double + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.Configuration; + +/** + * Instances of class <code>Double</code> represent primitive + * <code>double</code> values. + * + * Additionally, this class provides various helper functions and variables + * related to doubles. + * + * @author Paul Fisher + * @author Andrew Haley (aph@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public final class Double extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9172774392245257468L; + + /** + * The maximum positive value a <code>double</code> may represent + * is 1.7976931348623157e+308. + */ + public static final double MAX_VALUE = 1.7976931348623157e+308; + + /** + * The minimum positive value a <code>double</code> may represent + * is 5e-324. + */ + public static final double MIN_VALUE = 5e-324; + + /** + * The value of a double representation -1.0/0.0, negative + * infinity. + */ + public static final double NEGATIVE_INFINITY = -1.0 / 0.0; + + /** + * The value of a double representing 1.0/0.0, positive infinity. + */ + public static final double POSITIVE_INFINITY = 1.0 / 0.0; + + /** + * All IEEE 754 values of NaN have the same value in Java. + */ + public static final double NaN = 0.0 / 0.0; + + /** + * The primitive type <code>double</code> is represented by this + * <code>Class</code> object. + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('D'); + + /** + * The immutable value of this Double. + * + * @serial the wrapped double + */ + private final double value; + + /** + * Create a <code>Double</code> from the primitive <code>double</code> + * specified. + * + * @param value the <code>double</code> argument + */ + public Double(double value) + { + this.value = value; + } + + /** + * Create a <code>Double</code> from the specified <code>String</code>. + * This method calls <code>Double.parseDouble()</code>. + * + * @param s the <code>String</code> to convert + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>double</code> + * @throws NullPointerException if <code>s</code> is null + * @see #parseDouble(String) + */ + public Double(String s) + { + value = parseDouble(s); + } + + /** + * Convert the <code>double</code> to a <code>String</code>. + * Floating-point string representation is fairly complex: here is a + * rundown of the possible values. "<code>[-]</code>" indicates that a + * negative sign will be printed if the value (or exponent) is negative. + * "<code><number></code>" means a string of digits ('0' to '9'). + * "<code><digit></code>" means a single digit ('0' to '9').<br> + * + * <table border=1> + * <tr><th>Value of Double</th><th>String Representation</th></tr> + * <tr><td>[+-] 0</td> <td><code>[-]0.0</code></td></tr> + * <tr><td>Between [+-] 10<sup>-3</sup> and 10<sup>7</sup>, exclusive</td> + * <td><code>[-]number.number</code></td></tr> + * <tr><td>Other numeric value</td> + * <td><code>[-]<digit>.<number> + * E[-]<number></code></td></tr> + * <tr><td>[+-] infinity</td> <td><code>[-]Infinity</code></td></tr> + * <tr><td>NaN</td> <td><code>NaN</code></td></tr> + * </table> + * + * Yes, negative zero <em>is</em> a possible value. Note that there is + * <em>always</em> a <code>.</code> and at least one digit printed after + * it: even if the number is 3, it will be printed as <code>3.0</code>. + * After the ".", all digits will be printed except trailing zeros. The + * result is rounded to the shortest decimal number which will parse back + * to the same double. + * + * <p>To create other output formats, use {@link java.text.NumberFormat}. + * + * @XXX specify where we are not in accord with the spec. + * + * @param d the <code>double</code> to convert + * @return the <code>String</code> representing the <code>double</code> + */ + public static String toString(double d) + { + return VMDouble.toString(d, false); + } + + /** + * Create a new <code>Double</code> object using the <code>String</code>. + * + * @param s the <code>String</code> to convert + * @return the new <code>Double</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>double</code> + * @throws NullPointerException if <code>s</code> is null. + * @see #parseDouble(String) + */ + public static Double valueOf(String s) + { + return new Double(parseDouble(s)); + } + + /** + * Parse the specified <code>String</code> as a <code>double</code>. The + * extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> | <code>+</code> ] <code>NaN</code> ) + * | ( [ <code>-</code> | <code>+</code> ] <code>Infinity</code> ) + * | ( [ <code>-</code> | <code>+</code> ] <em>FloatingPoint</em> + * [ <code>f</code> | <code>F</code> | <code>d</code> + * | <code>D</code>] ) + * <em>FloatingPoint</em>: + * ( { <em>Digit</em> }+ [ <code>.</code> { <em>Digit</em> } ] + * [ <em>Exponent</em> ] ) + * | ( <code>.</code> { <em>Digit</em> }+ [ <em>Exponent</em> ] ) + * <em>Exponent</em>: + * ( ( <code>e</code> | <code>E</code> ) + * [ <code>-</code> | <code>+</code> ] { <em>Digit</em> }+ ) + * <em>Digit</em>: <em><code>'0'</code> through <code>'9'</code></em> + * </pre> + * + * <p>NaN and infinity are special cases, to allow parsing of the output + * of toString. Otherwise, the result is determined by calculating + * <em>n * 10<sup>exponent</sup></em> to infinite precision, then rounding + * to the nearest double. Remember that many numbers cannot be precisely + * represented in floating point. In case of overflow, infinity is used, + * and in case of underflow, signed zero is used. Unlike Integer.parseInt, + * this does not accept Unicode digits outside the ASCII range. + * + * <p>If an unexpected character is found in the <code>String</code>, a + * <code>NumberFormatException</code> will be thrown. Leading and trailing + * 'whitespace' is ignored via <code>String.trim()</code>, but spaces + * internal to the actual number are not allowed. + * + * <p>To parse numbers according to another format, consider using + * {@link java.text.NumberFormat}. + * + * @XXX specify where/how we are not in accord with the spec. + * + * @param str the <code>String</code> to convert + * @return the <code>double</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>double</code> + * @throws NullPointerException if <code>s</code> is null + * @see #MIN_VALUE + * @see #MAX_VALUE + * @see #POSITIVE_INFINITY + * @see #NEGATIVE_INFINITY + * @since 1.2 + */ + public static double parseDouble(String str) + { + return VMDouble.parseDouble(str); + } + + /** + * Return <code>true</code> if the <code>double</code> has the same + * value as <code>NaN</code>, otherwise return <code>false</code>. + * + * @param v the <code>double</code> to compare + * @return whether the argument is <code>NaN</code>. + */ + public static boolean isNaN(double v) + { + // This works since NaN != NaN is the only reflexive inequality + // comparison which returns true. + return v != v; + } + + /** + * Return <code>true</code> if the <code>double</code> has a value + * equal to either <code>NEGATIVE_INFINITY</code> or + * <code>POSITIVE_INFINITY</code>, otherwise return <code>false</code>. + * + * @param v the <code>double</code> to compare + * @return whether the argument is (-/+) infinity. + */ + public static boolean isInfinite(double v) + { + return v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY; + } + + /** + * Return <code>true</code> if the value of this <code>Double</code> + * is the same as <code>NaN</code>, otherwise return <code>false</code>. + * + * @return whether this <code>Double</code> is <code>NaN</code> + */ + public boolean isNaN() + { + return isNaN(value); + } + + /** + * Return <code>true</code> if the value of this <code>Double</code> + * is the same as <code>NEGATIVE_INFINITY</code> or + * <code>POSITIVE_INFINITY</code>, otherwise return <code>false</code>. + * + * @return whether this <code>Double</code> is (-/+) infinity + */ + public boolean isInfinite() + { + return isInfinite(value); + } + + /** + * Convert the <code>double</code> value of this <code>Double</code> + * to a <code>String</code>. This method calls + * <code>Double.toString(double)</code> to do its dirty work. + * + * @return the <code>String</code> representation + * @see #toString(double) + */ + public String toString() + { + return toString(value); + } + + /** + * Return the value of this <code>Double</code> as a <code>byte</code>. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this <code>Double</code> as a <code>short</code>. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this <code>Double</code> as an <code>int</code>. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this <code>Double</code> as a <code>long</code>. + * + * @return the long value + */ + public long longValue() + { + return (long) value; + } + + /** + * Return the value of this <code>Double</code> as a <code>float</code>. + * + * @return the float value + */ + public float floatValue() + { + return (float) value; + } + + /** + * Return the value of this <code>Double</code>. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Return a hashcode representing this Object. <code>Double</code>'s hash + * code is calculated by:<br> + * <code>long v = Double.doubleToLongBits(doubleValue());<br> + * int hash = (int)(v^(v>>32))</code>. + * + * @return this Object's hash code + * @see #doubleToLongBits(double) + */ + public int hashCode() + { + long v = doubleToLongBits(value); + return (int) (v ^ (v >>> 32)); + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Double</code> and represents the same double value. Unlike comparing + * two doubles with <code>==</code>, this treats two instances of + * <code>Double.NaN</code> as equal, but treats <code>0.0</code> and + * <code>-0.0</code> as unequal. + * + * <p>Note that <code>d1.equals(d2)</code> is identical to + * <code>doubleToLongBits(d1.doubleValue()) == + * doubleToLongBits(d2.doubleValue())</code>. + * + * @param obj the object to compare + * @return whether the objects are semantically equal + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Double)) + return false; + + double d = ((Double) obj).value; + + // Avoid call to native method. However, some implementations, like gcj, + // are better off using floatToIntBits(value) == floatToIntBits(f). + // Check common case first, then check NaN and 0. + if (value == d) + return (value != 0) || (1 / value == 1 / d); + return isNaN(value) && isNaN(d); + } + + /** + * Convert the double to the IEEE 754 floating-point "double format" bit + * layout. Bit 63 (the most significant) is the sign bit, bits 62-52 + * (masked by 0x7ff0000000000000L) represent the exponent, and bits 51-0 + * (masked by 0x000fffffffffffffL) are the mantissa. This function + * collapses all versions of NaN to 0x7ff8000000000000L. The result of this + * function can be used as the argument to + * <code>Double.longBitsToDouble(long)</code> to obtain the original + * <code>double</code> value. + * + * @param value the <code>double</code> to convert + * @return the bits of the <code>double</code> + * @see #longBitsToDouble(long) + */ + public static long doubleToLongBits(double value) + { + return VMDouble.doubleToLongBits(value); + } + + /** + * Convert the double to the IEEE 754 floating-point "double format" bit + * layout. Bit 63 (the most significant) is the sign bit, bits 62-52 + * (masked by 0x7ff0000000000000L) represent the exponent, and bits 51-0 + * (masked by 0x000fffffffffffffL) are the mantissa. This function + * leaves NaN alone, rather than collapsing to a canonical value. The + * result of this function can be used as the argument to + * <code>Double.longBitsToDouble(long)</code> to obtain the original + * <code>double</code> value. + * + * @param value the <code>double</code> to convert + * @return the bits of the <code>double</code> + * @see #longBitsToDouble(long) + */ + public static long doubleToRawLongBits(double value) + { + return VMDouble.doubleToRawLongBits(value); + } + + /** + * Convert the argument in IEEE 754 floating-point "double format" bit + * layout to the corresponding float. Bit 63 (the most significant) is the + * sign bit, bits 62-52 (masked by 0x7ff0000000000000L) represent the + * exponent, and bits 51-0 (masked by 0x000fffffffffffffL) are the mantissa. + * This function leaves NaN alone, so that you can recover the bit pattern + * with <code>Double.doubleToRawLongBits(double)</code>. + * + * @param bits the bits to convert + * @return the <code>double</code> represented by the bits + * @see #doubleToLongBits(double) + * @see #doubleToRawLongBits(double) + */ + public static double longBitsToDouble(long bits) + { + return VMDouble.longBitsToDouble(bits); + } + + /** + * Compare two Doubles numerically by comparing their <code>double</code> + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. However, this special + * cases NaN and signed zero as follows: NaN is considered greater than + * all other doubles, including <code>POSITIVE_INFINITY</code>, and positive + * zero is considered greater than negative zero. + * + * @param d the Double to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Double d) + { + return compare(value, d.value); + } + + /** + * Behaves like <code>compareTo(Double)</code> unless the Object + * is not an <code>Double</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not a <code>Double</code> + * @see #compareTo(Double) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compare(value, ((Double) o).value); + } + + /** + * Behaves like <code>new Double(x).compareTo(new Double(y))</code>; in + * other words this compares two doubles, special casing NaN and zero, + * without the overhead of objects. + * + * @param x the first double to compare + * @param y the second double to compare + * @return the comparison + * @since 1.4 + */ + public static int compare(double x, double y) + { + if (isNaN(x)) + return isNaN(y) ? 0 : 1; + if (isNaN(y)) + return -1; + // recall that 0.0 == -0.0, so we convert to infinites and try again + if (x == 0 && y == 0) + return (int) (1 / x - 1 / y); + if (x == y) + return 0; + + return x > y ? 1 : -1; + } +} diff --git a/libjava/classpath/java/lang/Error.java b/libjava/classpath/java/lang/Error.java new file mode 100644 index 00000000000..f66c7548b33 --- /dev/null +++ b/libjava/classpath/java/lang/Error.java @@ -0,0 +1,107 @@ +/* Error.java -- Indication of fatal abnormal conditions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Applications should not try to catch errors since they indicate + * abnormal conditions. An abnormal condition is something which should not + * occur, or which should not be recovered from. This latter category + * includes <code>ThreadDeath</code> and <code>AssertionError</code>. + * + * <p>A method is not required to declare any subclass of <code>Error</code> in + * its <code>throws</code> clause which might be thrown but not caught while + * executing the method. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Error extends Throwable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4980196508277280342L; + + /** + * Create an error without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public Error() + { + } + + /** + * Create an error with a message. The cause remains uninitialized. + * + * @param s the message string + * @see #initCause(Throwable) + */ + public Error(String s) + { + super(s); + } + + /** + * Create an error with a message and a cause. + * + * @param s the message string + * @param cause the cause of this error + * @since 1.4 + */ + public Error(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an error with a given cause, and a message of + * <code>cause == null ? null : cause.toString()</code>. + * + * @param cause the cause of this error + * @since 1.4 + */ + public Error(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/Exception.java b/libjava/classpath/java/lang/Exception.java new file mode 100644 index 00000000000..42f7c640dd3 --- /dev/null +++ b/libjava/classpath/java/lang/Exception.java @@ -0,0 +1,104 @@ +/* Exception.java -- generic exception thrown to indicate an exceptional + condition has occurred. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The root class of all exceptions worth catching in a program. This + * includes the special category of <code>RuntimeException</code>, which + * does not need to be declared in a throws clause. Exceptions can be used + * to represent almost any exceptional behavior, such as programming errors, + * mouse movements, keyboard clicking, etc. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class Exception extends Throwable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3387516993124229948L; + + /** + * Create an exception without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public Exception() + { + } + + /** + * Create an exception with a message. The cause remains uninitialized. + * + * @param s the message + * @see #initCause(Throwable) + */ + public Exception(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message string + * @param cause the cause of this error + * @since 1.4 + */ + public Exception(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an exception with a given cause, and a message of + * <code>cause == null ? null : cause.toString()</code>. + * + * @param cause the cause of this exception + * @since 1.4 + */ + public Exception(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/ExceptionInInitializerError.java b/libjava/classpath/java/lang/ExceptionInInitializerError.java new file mode 100644 index 00000000000..1e580958054 --- /dev/null +++ b/libjava/classpath/java/lang/ExceptionInInitializerError.java @@ -0,0 +1,123 @@ +/* ExceptionInInitializerError.java -- thrown when class initialization fails + with an uncaught exception + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>ExceptionInInitializerError</code> is thrown when an uncaught + * exception has occurred in a static initializer or the initializer for a + * static variable. In general, this wraps only RuntimeExceptions, since the + * compiler does not allow a checked exception to be uncaught in an + * initializer. This exception only occurs during reflection, when a class + * is initialized as part of another action. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class ExceptionInInitializerError extends LinkageError +{ + /** + * Compatible with JDK 1.1+. + */ + static final long serialVersionUID = 1521711792217232256L; + + /** + * The cause of this exception (duplicates the one stored in Throwable). + * + * @serial the exception cause + */ + private final Throwable exception; + + /** + * Create an error without a message. The cause is initialized as null. + */ + public ExceptionInInitializerError() + { + this((String) null); + } + + /** + * Create an error with a message. The cause is initialized as null. + * + * @param s the message + */ + public ExceptionInInitializerError(String s) + { + super(s); + exception = null; + } + + /** + * Creates an error an saves a reference to the <code>Throwable</code> + * object. The message string is null. + * + * @param t the exception thrown + */ + public ExceptionInInitializerError(Throwable t) + { + super(null); + initCause(t); + exception = t; + } + + /** + * Return the exception that caused this error to be created. This is a + * legacy method; the preferred choice now is {@link Throwable#getCause()}. + * + * @return the cause, or null if unknown + */ + public Throwable getException() + { + return exception; + } + + /** + * Return the exception that cause this error to be created. + * + * @return the cause, or null if unknown + * @since 1.4 + */ + public Throwable getCause() + { + return exception; + } +} diff --git a/libjava/classpath/java/lang/Float.java b/libjava/classpath/java/lang/Float.java new file mode 100644 index 00000000000..e6200dabddc --- /dev/null +++ b/libjava/classpath/java/lang/Float.java @@ -0,0 +1,527 @@ +/* Float.java -- object wrapper for float + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class <code>Float</code> represent primitive + * <code>float</code> values. + * + * Additionally, this class provides various helper functions and variables + * related to floats. + * + * @author Paul Fisher + * @author Andrew Haley (aph@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public final class Float extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2671257302660747028L; + + /** + * The maximum positive value a <code>double</code> may represent + * is 3.4028235e+38f. + */ + public static final float MAX_VALUE = 3.4028235e+38f; + + /** + * The minimum positive value a <code>float</code> may represent + * is 1.4e-45. + */ + public static final float MIN_VALUE = 1.4e-45f; + + /** + * The value of a float representation -1.0/0.0, negative infinity. + */ + public static final float NEGATIVE_INFINITY = -1.0f / 0.0f; + + /** + * The value of a float representation 1.0/0.0, positive infinity. + */ + public static final float POSITIVE_INFINITY = 1.0f / 0.0f; + + /** + * All IEEE 754 values of NaN have the same value in Java. + */ + public static final float NaN = 0.0f / 0.0f; + + /** + * The primitive type <code>float</code> is represented by this + * <code>Class</code> object. + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('F'); + + /** + * The immutable value of this Float. + * + * @serial the wrapped float + */ + private final float value; + + /** + * Create a <code>Float</code> from the primitive <code>float</code> + * specified. + * + * @param value the <code>float</code> argument + */ + public Float(float value) + { + this.value = value; + } + + /** + * Create a <code>Float</code> from the primitive <code>double</code> + * specified. + * + * @param value the <code>double</code> argument + */ + public Float(double value) + { + this.value = (float) value; + } + + /** + * Create a <code>Float</code> from the specified <code>String</code>. + * This method calls <code>Float.parseFloat()</code>. + * + * @param s the <code>String</code> to convert + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>float</code> + * @throws NullPointerException if <code>s</code> is null + * @see #parseFloat(String) + */ + public Float(String s) + { + value = parseFloat(s); + } + + /** + * Convert the <code>float</code> to a <code>String</code>. + * Floating-point string representation is fairly complex: here is a + * rundown of the possible values. "<code>[-]</code>" indicates that a + * negative sign will be printed if the value (or exponent) is negative. + * "<code><number></code>" means a string of digits ('0' to '9'). + * "<code><digit></code>" means a single digit ('0' to '9').<br> + * + * <table border=1> + * <tr><th>Value of Float</th><th>String Representation</th></tr> + * <tr><td>[+-] 0</td> <td><code>[-]0.0</code></td></tr> + * <tr><td>Between [+-] 10<sup>-3</sup> and 10<sup>7</sup>, exclusive</td> + * <td><code>[-]number.number</code></td></tr> + * <tr><td>Other numeric value</td> + * <td><code>[-]<digit>.<number> + * E[-]<number></code></td></tr> + * <tr><td>[+-] infinity</td> <td><code>[-]Infinity</code></td></tr> + * <tr><td>NaN</td> <td><code>NaN</code></td></tr> + * </table> + * + * Yes, negative zero <em>is</em> a possible value. Note that there is + * <em>always</em> a <code>.</code> and at least one digit printed after + * it: even if the number is 3, it will be printed as <code>3.0</code>. + * After the ".", all digits will be printed except trailing zeros. The + * result is rounded to the shortest decimal number which will parse back + * to the same float. + * + * <p>To create other output formats, use {@link java.text.NumberFormat}. + * + * @XXX specify where we are not in accord with the spec. + * + * @param f the <code>float</code> to convert + * @return the <code>String</code> representing the <code>float</code> + */ + public static String toString(float f) + { + return VMDouble.toString(f, true); + } + + /** + * Creates a new <code>Float</code> object using the <code>String</code>. + * + * @param s the <code>String</code> to convert + * @return the new <code>Float</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>float</code> + * @throws NullPointerException if <code>s</code> is null + * @see #parseFloat(String) + */ + public static Float valueOf(String s) + { + return new Float(parseFloat(s)); + } + + /** + * Parse the specified <code>String</code> as a <code>float</code>. The + * extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> | <code>+</code> ] <code>NaN</code> ) + * | ( [ <code>-</code> | <code>+</code> ] <code>Infinity</code> ) + * | ( [ <code>-</code> | <code>+</code> ] <em>FloatingPoint</em> + * [ <code>f</code> | <code>F</code> | <code>d</code> + * | <code>D</code>] ) + * <em>FloatingPoint</em>: + * ( { <em>Digit</em> }+ [ <code>.</code> { <em>Digit</em> } ] + * [ <em>Exponent</em> ] ) + * | ( <code>.</code> { <em>Digit</em> }+ [ <em>Exponent</em> ] ) + * <em>Exponent</em>: + * ( ( <code>e</code> | <code>E</code> ) + * [ <code>-</code> | <code>+</code> ] { <em>Digit</em> }+ ) + * <em>Digit</em>: <em><code>'0'</code> through <code>'9'</code></em> + * </pre> + * + * <p>NaN and infinity are special cases, to allow parsing of the output + * of toString. Otherwise, the result is determined by calculating + * <em>n * 10<sup>exponent</sup></em> to infinite precision, then rounding + * to the nearest float. Remember that many numbers cannot be precisely + * represented in floating point. In case of overflow, infinity is used, + * and in case of underflow, signed zero is used. Unlike Integer.parseInt, + * this does not accept Unicode digits outside the ASCII range. + * + * <p>If an unexpected character is found in the <code>String</code>, a + * <code>NumberFormatException</code> will be thrown. Leading and trailing + * 'whitespace' is ignored via <code>String.trim()</code>, but spaces + * internal to the actual number are not allowed. + * + * <p>To parse numbers according to another format, consider using + * {@link java.text.NumberFormat}. + * + * @XXX specify where/how we are not in accord with the spec. + * + * @param str the <code>String</code> to convert + * @return the <code>float</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>float</code> + * @throws NullPointerException if <code>s</code> is null + * @see #MIN_VALUE + * @see #MAX_VALUE + * @see #POSITIVE_INFINITY + * @see #NEGATIVE_INFINITY + * @since 1.2 + */ + public static float parseFloat(String str) + { + // XXX Rounding parseDouble() causes some errors greater than 1 ulp from + // the infinitely precise decimal. + return (float) Double.parseDouble(str); + } + + /** + * Return <code>true</code> if the <code>float</code> has the same + * value as <code>NaN</code>, otherwise return <code>false</code>. + * + * @param v the <code>float</code> to compare + * @return whether the argument is <code>NaN</code> + */ + public static boolean isNaN(float v) + { + // This works since NaN != NaN is the only reflexive inequality + // comparison which returns true. + return v != v; + } + + /** + * Return <code>true</code> if the <code>float</code> has a value + * equal to either <code>NEGATIVE_INFINITY</code> or + * <code>POSITIVE_INFINITY</code>, otherwise return <code>false</code>. + * + * @param v the <code>float</code> to compare + * @return whether the argument is (-/+) infinity + */ + public static boolean isInfinite(float v) + { + return v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY; + } + + /** + * Return <code>true</code> if the value of this <code>Float</code> + * is the same as <code>NaN</code>, otherwise return <code>false</code>. + * + * @return whether this <code>Float</code> is <code>NaN</code> + */ + public boolean isNaN() + { + return isNaN(value); + } + + /** + * Return <code>true</code> if the value of this <code>Float</code> + * is the same as <code>NEGATIVE_INFINITY</code> or + * <code>POSITIVE_INFINITY</code>, otherwise return <code>false</code>. + * + * @return whether this <code>Float</code> is (-/+) infinity + */ + public boolean isInfinite() + { + return isInfinite(value); + } + + /** + * Convert the <code>float</code> value of this <code>Float</code> + * to a <code>String</code>. This method calls + * <code>Float.toString(float)</code> to do its dirty work. + * + * @return the <code>String</code> representation + * @see #toString(float) + */ + public String toString() + { + return toString(value); + } + + /** + * Return the value of this <code>Float</code> as a <code>byte</code>. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this <code>Float</code> as a <code>short</code>. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this <code>Integer</code> as an <code>int</code>. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this <code>Integer</code> as a <code>long</code>. + * + * @return the long value + */ + public long longValue() + { + return (long) value; + } + + /** + * Return the value of this <code>Float</code>. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this <code>Float</code> as a <code>double</code> + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Return a hashcode representing this Object. <code>Float</code>'s hash + * code is calculated by calling <code>floatToIntBits(floatValue())</code>. + * + * @return this Object's hash code + * @see #floatToIntBits(float) + */ + public int hashCode() + { + return floatToIntBits(value); + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Float</code> and represents the same float value. Unlike comparing + * two floats with <code>==</code>, this treats two instances of + * <code>Float.NaN</code> as equal, but treats <code>0.0</code> and + * <code>-0.0</code> as unequal. + * + * <p>Note that <code>f1.equals(f2)</code> is identical to + * <code>floatToIntBits(f1.floatValue()) == + * floatToIntBits(f2.floatValue())</code>. + * + * @param obj the object to compare + * @return whether the objects are semantically equal + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Float)) + return false; + + float f = ((Float) obj).value; + + // Avoid call to native method. However, some implementations, like gcj, + // are better off using floatToIntBits(value) == floatToIntBits(f). + // Check common case first, then check NaN and 0. + if (value == f) + return (value != 0) || (1 / value == 1 / f); + return isNaN(value) && isNaN(f); + } + + /** + * Convert the float to the IEEE 754 floating-point "single format" bit + * layout. Bit 31 (the most significant) is the sign bit, bits 30-23 + * (masked by 0x7f800000) represent the exponent, and bits 22-0 + * (masked by 0x007fffff) are the mantissa. This function collapses all + * versions of NaN to 0x7fc00000. The result of this function can be used + * as the argument to <code>Float.intBitsToFloat(int)</code> to obtain the + * original <code>float</code> value. + * + * @param value the <code>float</code> to convert + * @return the bits of the <code>float</code> + * @see #intBitsToFloat(int) + */ + public static int floatToIntBits(float value) + { + return VMFloat.floatToIntBits(value); + } + + /** + * Convert the float to the IEEE 754 floating-point "single format" bit + * layout. Bit 31 (the most significant) is the sign bit, bits 30-23 + * (masked by 0x7f800000) represent the exponent, and bits 22-0 + * (masked by 0x007fffff) are the mantissa. This function leaves NaN alone, + * rather than collapsing to a canonical value. The result of this function + * can be used as the argument to <code>Float.intBitsToFloat(int)</code> to + * obtain the original <code>float</code> value. + * + * @param value the <code>float</code> to convert + * @return the bits of the <code>float</code> + * @see #intBitsToFloat(int) + */ + public static int floatToRawIntBits(float value) + { + return VMFloat.floatToRawIntBits(value); + } + + /** + * Convert the argument in IEEE 754 floating-point "single format" bit + * layout to the corresponding float. Bit 31 (the most significant) is the + * sign bit, bits 30-23 (masked by 0x7f800000) represent the exponent, and + * bits 22-0 (masked by 0x007fffff) are the mantissa. This function leaves + * NaN alone, so that you can recover the bit pattern with + * <code>Float.floatToRawIntBits(float)</code>. + * + * @param bits the bits to convert + * @return the <code>float</code> represented by the bits + * @see #floatToIntBits(float) + * @see #floatToRawIntBits(float) + */ + public static float intBitsToFloat(int bits) + { + return VMFloat.intBitsToFloat(bits); + } + + /** + * Compare two Floats numerically by comparing their <code>float</code> + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. However, this special + * cases NaN and signed zero as follows: NaN is considered greater than + * all other floats, including <code>POSITIVE_INFINITY</code>, and positive + * zero is considered greater than negative zero. + * + * @param f the Float to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Float f) + { + return compare(value, f.value); + } + + /** + * Behaves like <code>compareTo(Float)</code> unless the Object + * is not an <code>Float</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not a <code>Float</code> + * @see #compareTo(Float) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compare(value, ((Float) o).value); + } + + /** + * Behaves like <code>new Float(x).compareTo(new Float(y))</code>; in + * other words this compares two floats, special casing NaN and zero, + * without the overhead of objects. + * + * @param x the first float to compare + * @param y the second float to compare + * @return the comparison + * @since 1.4 + */ + public static int compare(float x, float y) + { + if (isNaN(x)) + return isNaN(y) ? 0 : 1; + if (isNaN(y)) + return -1; + // recall that 0.0 == -0.0, so we convert to infinities and try again + if (x == 0 && y == 0) + return (int) (1 / x - 1 / y); + if (x == y) + return 0; + + return x > y ? 1 : -1; + } +} diff --git a/libjava/classpath/java/lang/IllegalAccessError.java b/libjava/classpath/java/lang/IllegalAccessError.java new file mode 100644 index 00000000000..e4821606a41 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalAccessError.java @@ -0,0 +1,76 @@ +/* IllegalAccessError.java -- thrown when linking to an inaccessible member + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>IllegalAccessError</code> is thrown when an attempt is made to + * call a method, or access or modify a field that the application does not + * have access to. Because this error is usually caught by a compiler, + * the error only occurs at runtime when the definition of a class has + * changed in a way that is incompatible with the previously compiled + * application. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalAccessError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8988904074992417891L; + + /** + * Create an error without a message. + */ + public IllegalAccessError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public IllegalAccessError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalAccessException.java b/libjava/classpath/java/lang/IllegalAccessException.java new file mode 100644 index 00000000000..a352c8b1b31 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalAccessException.java @@ -0,0 +1,99 @@ +/* IllegalAccessException.java -- thrown on attempt to reflect on + inaccessible data + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Thrown whenever a reflective method tries to do something that the + * compiler would not allow. For example, using reflection to set a private + * variable that belongs to a class in another package is bad. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Class#newInstance() + * @see Field#set(Object, Object) + * @see Field#setBoolean(Object, boolean) + * @see Field#setByte(Object, byte) + * @see Field#setShort(Object, short) + * @see Field#setChar(Object, char) + * @see Field#setInt(Object, int) + * @see Field#setLong(Object, long) + * @see Field#setFloat(Object, float) + * @see Field#setDouble(Object, double) + * @see Field#get(Object) + * @see Field#getBoolean(Object) + * @see Field#getByte(Object) + * @see Field#getShort(Object) + * @see Field#getChar(Object) + * @see Field#getInt(Object) + * @see Field#getLong(Object) + * @see Field#getFloat(Object) + * @see Field#getDouble(Object) + * @see Method#invoke(Object, Object[]) + * @see Constructor#newInstance(Object[]) + * @status updated to 1.4 + */ +public class IllegalAccessException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6616958222490762034L; + + /** + * Create an exception without a message. + */ + public IllegalAccessException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalAccessException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalArgumentException.java b/libjava/classpath/java/lang/IllegalArgumentException.java new file mode 100644 index 00000000000..7b822b91665 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalArgumentException.java @@ -0,0 +1,75 @@ +/* IllegalArgumentException.java -- thrown when a method is passed an + illegal or inappropriate argument + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +/** + * Thrown when a method is passed an illegal or inappropriate argument. For + * example:<br> + * <pre> + * wait(-1); + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalArgumentException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5365630128856068164L; + + /** + * Create an exception without a message. + */ + public IllegalArgumentException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalArgumentException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalMonitorStateException.java b/libjava/classpath/java/lang/IllegalMonitorStateException.java new file mode 100644 index 00000000000..13b3f952bb5 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalMonitorStateException.java @@ -0,0 +1,78 @@ +/* IllegalMonitorStateException.java -- thrown when trying to wait or + notify a monitor that is not owned + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a thread attempts to wait or notify on a monitor that it + * does not own (ie. it has not synchronized on the object). For example:<br> + * <pre> + * void m() { + * notify(); + * } + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalMonitorStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3713306369498869069L; + + /** + * Create an exception without a message. + */ + public IllegalMonitorStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalMonitorStateException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalStateException.java b/libjava/classpath/java/lang/IllegalStateException.java new file mode 100644 index 00000000000..5c2bbad37a2 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalStateException.java @@ -0,0 +1,80 @@ +/* IllegalStateException.java -- thrown when invoking a method at + an illegal or inappropriate time + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a method is invoked at an illegal or inappropriate time. For + * example:<br> + * <pre> + * void m(Collecion c) + * { + * c.iterator().remove(); + * } + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class IllegalStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1848914673093119416L; + + /** + * Create an exception without a message. + */ + public IllegalStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalStateException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalThreadStateException.java b/libjava/classpath/java/lang/IllegalThreadStateException.java new file mode 100644 index 00000000000..e14385a3e83 --- /dev/null +++ b/libjava/classpath/java/lang/IllegalThreadStateException.java @@ -0,0 +1,75 @@ +/* IllegalThreadStateException.java -- thrown when trying to manipulate a + Thread when it is not in an appropriate state + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown When trying to manipulate a Thread which is in an inappropriate + * state. Since the documentation suggests that this can happen with + * <code>Thread.suspend</code> or <code>Thread.resume</code>, but these + * two methods are deprecated, this exception is likely very rare. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalThreadStateException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7626246362397460174L; + + /** + * Create an exception without a message. + */ + public IllegalThreadStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalThreadStateException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IncompatibleClassChangeError.java b/libjava/classpath/java/lang/IncompatibleClassChangeError.java new file mode 100644 index 00000000000..637410a9009 --- /dev/null +++ b/libjava/classpath/java/lang/IncompatibleClassChangeError.java @@ -0,0 +1,73 @@ +/* IncompatibleClassChangeError.java -- thrown for binary incompatible classes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>IncompatibleClassChangeError</code> is thrown when the definition + * of a class used by the currently executing method has changed in an + * incompatible way. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IncompatibleClassChangeError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4914975503642802119L; + + /** + * Create an error without a message. + */ + public IncompatibleClassChangeError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public IncompatibleClassChangeError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IndexOutOfBoundsException.java b/libjava/classpath/java/lang/IndexOutOfBoundsException.java new file mode 100644 index 00000000000..c53c67e5574 --- /dev/null +++ b/libjava/classpath/java/lang/IndexOutOfBoundsException.java @@ -0,0 +1,75 @@ +/* IndexOutOfBoundsException.java -- thrown for an invalid index + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception can be thrown to indicate an attempt to access an + * index which is out of bounds on objects like String, Array, or Vector. + * Usually any negative integer less than or equal to -1 and positive + * integer greater than or equal to the size of the object is an index + * which would be out of bounds. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IndexOutOfBoundsException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 234122996006267687L; + + /** + * Create an exception without a message. + */ + public IndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IndexOutOfBoundsException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InheritableThreadLocal.java b/libjava/classpath/java/lang/InheritableThreadLocal.java new file mode 100644 index 00000000000..69ff6138dc6 --- /dev/null +++ b/libjava/classpath/java/lang/InheritableThreadLocal.java @@ -0,0 +1,116 @@ +/* InheritableThreadLocal -- a ThreadLocal which inherits values across threads + Copyright (C) 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.util.Iterator; +import java.util.WeakHashMap; + +/** + * A ThreadLocal whose value is inherited by child Threads. The value of the + * InheritableThreadLocal associated with the (parent) Thread is copied to + * the new (child) Thread at the moment of creation. + * + * <p>It is possible to make the value associated with the child Thread a + * function of the value that is associated with the parent Thread by + * overriding the <code>childValue()</code> method. The utility of this class + * is in transferring items like User ID or Transaction ID across threads + * automatically. + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @see ThreadLocal + * @since 1.2 + * @status updated to 1.4 + */ +public class InheritableThreadLocal extends ThreadLocal +{ + /** + * Creates a new InheritableThreadLocal that has no values associated + * with it yet. + */ + public InheritableThreadLocal() + { + } + + /** + * Determines the value associated with a newly created child Thread as a + * function of the value associated with the currently executing (parent) + * Thread. The default implementation just returns the parentValue. + * + * @param parentValue the value of this object in the parent thread at + * the moment of creation of the child + * @return the initial value for the child thread + */ + protected Object childValue(Object parentValue) + { + return parentValue; + } + + /** + * Generates the childValues of all <code>InheritableThreadLocal</code>s + * that are in the heritage of the current Thread for the newly created + * childThread. Should be called from the contructor Thread. + * + * @param childThread the newly created thread, to inherit from this thread + * @see Thread#Thread(ThreadGroup, Runnable, String) + */ + static void newChildThread(Thread childThread) + { + // The currentThread is the parent of the new thread. + Thread parentThread = Thread.currentThread(); + if (parentThread.locals != null) + { + Iterator keys = parentThread.locals.keySet().iterator(); + while (keys.hasNext()) + { + Key key = (Key)keys.next(); + if (key.get() instanceof InheritableThreadLocal) + { + InheritableThreadLocal local = (InheritableThreadLocal)key.get(); + Object parentValue = parentThread.locals.get(key); + Object childValue = local.childValue(parentValue == NULL + ? null : parentValue); + if (childThread.locals == null) + childThread.locals = new WeakHashMap(); + childThread.locals.put(key, (childValue == null + ? NULL : childValue)); + } + } + } + } +} diff --git a/libjava/classpath/java/lang/InstantiationError.java b/libjava/classpath/java/lang/InstantiationError.java new file mode 100644 index 00000000000..dd12b513a22 --- /dev/null +++ b/libjava/classpath/java/lang/InstantiationError.java @@ -0,0 +1,75 @@ +/* InstantiationError.java -- thrown when the linker cannot create an instance + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>InstantiationError</code> is thrown when an attempt is made to + * create an instance of an abstract class or an interface. Because this + * error is usually caught by a compiler, the error only occurs at runtime + * when the definition of a class has changed in a way that is incompatible + * with the previously compiled application. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class InstantiationError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4885810657349421204L; + + /** + * Create an error without a message. + */ + public InstantiationError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public InstantiationError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InstantiationException.java b/libjava/classpath/java/lang/InstantiationException.java new file mode 100644 index 00000000000..367b14bd278 --- /dev/null +++ b/libjava/classpath/java/lang/InstantiationException.java @@ -0,0 +1,74 @@ +/* InstantiationException.java -- thrown when reflection cannot create an + instance + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to use reflection to build a + * non-instantiable class (an interface or abstract class). + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Class#newInstance() + * @status updated to 1.4 + */ +public class InstantiationException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8441929162975509110L; + + /** + * Create an exception without a message. + */ + public InstantiationException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public InstantiationException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Integer.java b/libjava/classpath/java/lang/Integer.java new file mode 100644 index 00000000000..07be4e30728 --- /dev/null +++ b/libjava/classpath/java/lang/Integer.java @@ -0,0 +1,772 @@ +/* Integer.java -- object wrapper for int + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class <code>Integer</code> represent primitive + * <code>int</code> values. + * + * Additionally, this class provides various helper functions and variables + * related to ints. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @since 1.0 + * @status largely updated to 1.5 + */ +public final class Integer extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = 1360826667806852920L; + + /** + * The minimum value an <code>int</code> can represent is -2147483648 (or + * -2<sup>31</sup>). + */ + public static final int MIN_VALUE = 0x80000000; + + /** + * The maximum value an <code>int</code> can represent is 2147483647 (or + * 2<sup>31</sup> - 1). + */ + public static final int MAX_VALUE = 0x7fffffff; + + /** + * The primitive type <code>int</code> is represented by this + * <code>Class</code> object. + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('I'); + + /** + * The number of bits needed to represent an <code>int</code>. + * @since 1.5 + */ + public static final int SIZE = 32; + + // This caches some Integer values, and is used by boxing + // conversions via valueOf(). We must cache at least -128..127; + // these constants control how much we actually cache. + private static final int MIN_CACHE = -128; + private static final int MAX_CACHE = 127; + private static Integer[] intCache = new Integer[MAX_CACHE - MIN_CACHE + 1]; + + /** + * The immutable value of this Integer. + * + * @serial the wrapped int + */ + private final int value; + + /** + * Create an <code>Integer</code> object representing the value of the + * <code>int</code> argument. + * + * @param value the value to use + */ + public Integer(int value) + { + this.value = value; + } + + /** + * Create an <code>Integer</code> object representing the value of the + * argument after conversion to an <code>int</code>. + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain an int + * @see #valueOf(String) + */ + public Integer(String s) + { + value = parseInt(s, 10, false); + } + + /** + * Converts the <code>int</code> to a <code>String</code> using + * the specified radix (base). If the radix exceeds + * <code>Character.MIN_RADIX</code> or <code>Character.MAX_RADIX</code>, 10 + * is used instead. If the result is negative, the leading character is + * '-' ('\\u002D'). The remaining characters come from + * <code>Character.forDigit(digit, radix)</code> ('0'-'9','a'-'z'). + * + * @param num the <code>int</code> to convert to <code>String</code> + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> representation of the argument + */ + public static String toString(int num, int radix) + { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + radix = 10; + + // For negative numbers, print out the absolute value w/ a leading '-'. + // Use an array large enough for a binary number. + char[] buffer = new char[33]; + int i = 33; + boolean isNeg = false; + if (num < 0) + { + isNeg = true; + num = -num; + + // When the value is MIN_VALUE, it overflows when made positive + if (num < 0) + { + buffer[--i] = digits[(int) (-(num + radix) % radix)]; + num = -(num / radix); + } + } + + do + { + buffer[--i] = digits[num % radix]; + num /= radix; + } + while (num > 0); + + if (isNeg) + buffer[--i] = '-'; + + // Package constructor avoids an array copy. + return new String(buffer, i, 33 - i, true); + } + + /** + * Converts the <code>int</code> to a <code>String</code> assuming it is + * unsigned in base 16. + * + * @param i the <code>int</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toHexString(int i) + { + return toUnsignedString(i, 4); + } + + /** + * Converts the <code>int</code> to a <code>String</code> assuming it is + * unsigned in base 8. + * + * @param i the <code>int</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toOctalString(int i) + { + return toUnsignedString(i, 3); + } + + /** + * Converts the <code>int</code> to a <code>String</code> assuming it is + * unsigned in base 2. + * + * @param i the <code>int</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toBinaryString(int i) + { + return toUnsignedString(i, 1); + } + + /** + * Converts the <code>int</code> to a <code>String</code> and assumes + * a radix of 10. + * + * @param i the <code>int</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + * @see #toString(int, int) + */ + public static String toString(int i) + { + // This is tricky: in libgcj, String.valueOf(int) is a fast native + // implementation. In Classpath it just calls back to + // Integer.toString(int, int). + return String.valueOf(i); + } + + /** + * Converts the specified <code>String</code> into an <code>int</code> + * using the specified radix (base). The string must not be <code>null</code> + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * <code>Character.digit(d, radix)</code>, and must be in the range + * <code>0</code> to <code>radix - 1</code>. Finally, the result must be + * within <code>MIN_VALUE</code> to <code>MAX_VALUE</code>, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param str the <code>String</code> to convert + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> argument converted to <code>int</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as an + * <code>int</code> + */ + public static int parseInt(String str, int radix) + { + return parseInt(str, radix, false); + } + + /** + * Converts the specified <code>String</code> into an <code>int</code>. + * This function assumes a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the <code>int</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as an + * <code>int</code> + * @see #parseInt(String, int) + */ + public static int parseInt(String s) + { + return parseInt(s, 10, false); + } + + /** + * Creates a new <code>Integer</code> object using the <code>String</code> + * and specified radix (base). + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to convert with + * @return the new <code>Integer</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as an + * <code>int</code> + * @see #parseInt(String, int) + */ + public static Integer valueOf(String s, int radix) + { + return new Integer(parseInt(s, radix, false)); + } + + /** + * Creates a new <code>Integer</code> object using the <code>String</code>, + * assuming a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the new <code>Integer</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as an + * <code>int</code> + * @see #Integer(String) + * @see #parseInt(String) + */ + public static Integer valueOf(String s) + { + return new Integer(parseInt(s, 10, false)); + } + + /** + * Returns an <code>Integer</code> object wrapping the value. + * In contrast to the <code>Integer</code> constructor, this method + * will cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the <code>Integer</code> + */ + public static Integer valueOf(int val) + { + if (val < MIN_CACHE || val > MAX_CACHE) + return new Integer(val); + synchronized (intCache) + { + if (intCache[val - MIN_CACHE] == null) + intCache[val - MIN_CACHE] = new Integer(val); + return intCache[val - MIN_CACHE]; + } + } + + /** + * Return the value of this <code>Integer</code> as a <code>byte</code>. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this <code>Integer</code> as a <code>short</code>. + * + * @return the short value + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this <code>Integer</code>. + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this <code>Integer</code> as a <code>long</code>. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this <code>Integer</code> as a <code>float</code>. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this <code>Integer</code> as a <code>double</code>. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the <code>Integer</code> value to a <code>String</code> and + * assumes a radix of 10. + * + * @return the <code>String</code> representation + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. <code>Integer</code>'s hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Integer</code> and represents the same int value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Integer && value == ((Integer) obj).value; + } + + /** + * Get the specified system property as an <code>Integer</code>. The + * <code>decode()</code> method will be used to interpret the value of + * the property. + * + * @param nm the name of the system property + * @return the system property as an <code>Integer</code>, or null if the + * property is not found or cannot be decoded + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm) + { + return getInteger(nm, null); + } + + /** + * Get the specified system property as an <code>Integer</code>, or use a + * default <code>int</code> value if the property is not found or is not + * decodable. The <code>decode()</code> method will be used to interpret + * the value of the property. + * + * @param nm the name of the system property + * @param val the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm, int val) + { + Integer result = getInteger(nm, null); + return result == null ? new Integer(val) : result; + } + + /** + * Get the specified system property as an <code>Integer</code>, or use a + * default <code>Integer</code> value if the property is not found or is + * not decodable. The <code>decode()</code> method will be used to + * interpret the value of the property. + * + * @param nm the name of the system property + * @param def the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm, Integer def) + { + if (nm == null || "".equals(nm)) + return def; + nm = System.getProperty(nm); + if (nm == null) + return def; + try + { + return decode(nm); + } + catch (NumberFormatException e) + { + return def; + } + } + + /** + * Convert the specified <code>String</code> into an <code>Integer</code>. + * The <code>String</code> may represent decimal, hexadecimal, or + * octal numbers. + * + * <p>The extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> ] <em>DecimalNumber</em> ) + * | ( [ <code>-</code> ] ( <code>0x</code> | <code>0X</code> + * | <code>#</code> ) <em>HexDigit</em> { <em>HexDigit</em> } ) + * | ( [ <code>-</code> ] <code>0</code> { <em>OctalDigit</em> } ) + * <em>DecimalNumber</em>: + * <em>DecimalDigit except '0'</em> { <em>DecimalDigit</em> } + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 10) has value 0 to 9</em> + * <em>OctalDigit</em>: + * <em>Character.digit(d, 8) has value 0 to 7</em> + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 16) has value 0 to 15</em> + * </pre> + * Finally, the value must be in the range <code>MIN_VALUE</code> to + * <code>MAX_VALUE</code>, or an exception is thrown. + * + * @param str the <code>String</code> to interpret + * @return the value of the String as an <code>Integer</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>int</code> + * @throws NullPointerException if <code>s</code> is null + * @since 1.2 + */ + public static Integer decode(String str) + { + return new Integer(parseInt(str, 10, true)); + } + + /** + * Compare two Integers numerically by comparing their <code>int</code> + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param i the Integer to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Integer i) + { + if (value == i.value) + return 0; + // Returns just -1 or 1 on inequality; doing math might overflow. + return value > i.value ? 1 : -1; + } + + /** + * Behaves like <code>compareTo(Integer)</code> unless the Object + * is not an <code>Integer</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not an <code>Integer</code> + * @see #compareTo(Integer) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((Integer) o); + } + + /** + * Return the number of bits set in x. + * @param x value to examine + * @since 1.5 + */ + public static int bitCount(int x) + { + // Successively collapse alternating bit groups into a sum. + x = ((x >> 1) & 0x55555555) + (x & 0x55555555); + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + x = ((x >> 4) & 0x0f0f0f0f) + (x & 0x0f0f0f0f); + x = ((x >> 8) & 0x00ff00ff) + (x & 0x00ff00ff); + return ((x >> 16) & 0x0000ffff) + (x & 0x0000ffff); + } + + /** + * Rotate x to the left by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static int rotateLeft(int x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << distance) | (x >>> - distance); + } + + /** + * Rotate x to the right by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static int rotateRight(int x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << - distance) | (x >>> distance); + } + + /** + * Find the highest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static int highestOneBit(int value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + return value ^ (value >>> 1); + } + + /** + * Return the number of leading zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfLeadingZeros(int value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + return bitCount(~value); + } + + /** + * Find the lowest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static int lowestOneBit(int value) + { + // Classic assembly trick. + return value & - value; + } + + /** + * Find the number of trailing zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfTrailingZeros(int value) + { + return bitCount((value & -value) - 1); + } + + /** + * Return 1 if x is positive, -1 if it is negative, and 0 if it is + * zero. + * @param x the value to examine + * @since 1.5 + */ + public static int signum(int x) + { + return x < 0 ? -1 : (x > 0 ? 1 : 0); + } + + /** + * Reverse the bytes in val. + * @since 1.5 + */ + public static int reverseBytes(int val) + { + return ( ((val >> 24) & 0xff) + | ((val >> 8) & 0xff00) + | ((val << 8) & 0xff0000) + | ((val << 24) & 0xff000000)); + } + + /** + * Reverse the bits in val. + * @since 1.5 + */ + public static int reverse(int val) + { + // Successively swap alternating bit groups. + val = ((val >> 1) & 0x55555555) + ((val << 1) & ~0x55555555); + val = ((val >> 2) & 0x33333333) + ((val << 2) & ~0x33333333); + val = ((val >> 4) & 0x0f0f0f0f) + ((val << 4) & ~0x0f0f0f0f); + val = ((val >> 8) & 0x00ff00ff) + ((val << 8) & ~0x00ff00ff); + return ((val >> 16) & 0x0000ffff) + ((val << 16) & ~0x0000ffff); + } + + /** + * Helper for converting unsigned numbers to String. + * + * @param num the number + * @param exp log2(digit) (ie. 1, 3, or 4 for binary, oct, hex) + */ + // Package visible for use by Long. + static String toUnsignedString(int num, int exp) + { + // Use an array large enough for a binary number. + int mask = (1 << exp) - 1; + char[] buffer = new char[32]; + int i = 32; + do + { + buffer[--i] = digits[num & mask]; + num >>>= exp; + } + while (num != 0); + + // Package constructor avoids an array copy. + return new String(buffer, i, 32 - i, true); + } + + /** + * Helper for parsing ints, used by Integer, Short, and Byte. + * + * @param str the string to parse + * @param radix the radix to use, must be 10 if decode is true + * @param decode if called from decode + * @return the parsed int value + * @throws NumberFormatException if there is an error + * @throws NullPointerException if decode is true and str if null + * @see #parseInt(String, int) + * @see #decode(String) + * @see Byte#parseInt(String, int) + * @see Short#parseInt(String, int) + */ + static int parseInt(String str, int radix, boolean decode) + { + if (! decode && str == null) + throw new NumberFormatException(); + int index = 0; + int len = str.length(); + boolean isNeg = false; + if (len == 0) + throw new NumberFormatException(); + int ch = str.charAt(index); + if (ch == '-') + { + if (len == 1) + throw new NumberFormatException(); + isNeg = true; + ch = str.charAt(++index); + } + if (decode) + { + if (ch == '0') + { + if (++index == len) + return 0; + if ((str.charAt(index) & ~('x' ^ 'X')) == 'X') + { + radix = 16; + index++; + } + else + radix = 8; + } + else if (ch == '#') + { + radix = 16; + index++; + } + } + if (index == len) + throw new NumberFormatException(); + + int max = MAX_VALUE / radix; + // We can't directly write `max = (MAX_VALUE + 1) / radix'. + // So instead we fake it. + if (isNeg && MAX_VALUE % radix == radix - 1) + ++max; + + int val = 0; + while (index < len) + { + if (val < 0 || val > max) + throw new NumberFormatException(); + + ch = Character.digit(str.charAt(index++), radix); + val = val * radix + ch; + if (ch < 0 || (val < 0 && (! isNeg || val != MIN_VALUE))) + throw new NumberFormatException(); + } + return isNeg ? -val : val; + } +} diff --git a/libjava/classpath/java/lang/InternalError.java b/libjava/classpath/java/lang/InternalError.java new file mode 100644 index 00000000000..3a95bbeaf57 --- /dev/null +++ b/libjava/classpath/java/lang/InternalError.java @@ -0,0 +1,72 @@ +/* InternalError.java -- thrown when the VM encounters an internal error + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>InternalError</code> is thrown when a mystical error has + * occurred in the Java Virtual Machine. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class InternalError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9062593416125562365L; + + /** + * Create an error without a message. + */ + public InternalError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public InternalError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InterruptedException.java b/libjava/classpath/java/lang/InterruptedException.java new file mode 100644 index 00000000000..da2173c8b4e --- /dev/null +++ b/libjava/classpath/java/lang/InterruptedException.java @@ -0,0 +1,80 @@ +/* InterruptedException.java -- thrown when a thread is interrupted + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a thread interrupts another thread which was previously + * sleeping, waiting, or paused in some other way. See the + * <code>interrupt</code> method of class <code>Thread</code>. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Object#wait() + * @see Object#wait(long) + * @see Object#wait(long, int) + * @see Thread#sleep(long) + * @see Thread#interrupt() + * @see Thread#interrupted() + * @status updated to 1.4 + */ +public class InterruptedException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6700697376100628473L; + + /** + * Create an exception without a message. + */ + public InterruptedException() + { + } + + /** + * Create an exception with a message. + * + * + * @param s the message + */ + public InterruptedException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/LinkageError.java b/libjava/classpath/java/lang/LinkageError.java new file mode 100644 index 00000000000..028702081ba --- /dev/null +++ b/libjava/classpath/java/lang/LinkageError.java @@ -0,0 +1,74 @@ +/* LinkageError.java -- thrown when classes valid at separate compile times + cannot be linked to each other + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Subclasses of <code>LinkageError</code> are thrown to indicate that two + * classes which were compatible at separate compilation times cannot be + * linked to one another. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class LinkageError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3579600108157160122L; + + /** + * Create an error without a message. + */ + public LinkageError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public LinkageError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Long.java b/libjava/classpath/java/lang/Long.java new file mode 100644 index 00000000000..703eab8a0f3 --- /dev/null +++ b/libjava/classpath/java/lang/Long.java @@ -0,0 +1,614 @@ +/* Long.java -- object wrapper for long + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class <code>Long</code> represent primitive + * <code>long</code> values. + * + * Additionally, this class provides various helper functions and variables + * related to longs. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public final class Long extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = 4290774380558885855L; + + /** + * The minimum value a <code>long</code> can represent is + * -9223372036854775808L (or -2<sup>63</sup>). + */ + public static final long MIN_VALUE = 0x8000000000000000L; + + /** + * The maximum value a <code>long</code> can represent is + * 9223372036854775807 (or 2<sup>63</sup> - 1). + */ + public static final long MAX_VALUE = 0x7fffffffffffffffL; + + /** + * The primitive type <code>long</code> is represented by this + * <code>Class</code> object. + * @since 1.1 + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass ('J'); + + /** + * The immutable value of this Long. + * + * @serial the wrapped long + */ + private final long value; + + /** + * Create a <code>Long</code> object representing the value of the + * <code>long</code> argument. + * + * @param value the value to use + */ + public Long(long value) + { + this.value = value; + } + + /** + * Create a <code>Long</code> object representing the value of the + * argument after conversion to a <code>long</code>. + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain a long + * @see #valueOf(String) + */ + public Long(String s) + { + value = parseLong(s, 10, false); + } + + /** + * Converts the <code>long</code> to a <code>String</code> using + * the specified radix (base). If the radix exceeds + * <code>Character.MIN_RADIX</code> or <code>Character.MAX_RADIX</code>, 10 + * is used instead. If the result is negative, the leading character is + * '-' ('\\u002D'). The remaining characters come from + * <code>Character.forDigit(digit, radix)</code> ('0'-'9','a'-'z'). + * + * @param num the <code>long</code> to convert to <code>String</code> + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> representation of the argument + */ + public static String toString(long num, int radix) + { + // Use the Integer toString for efficiency if possible. + if ((int) num == num) + return Integer.toString((int) num, radix); + + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + radix = 10; + + // For negative numbers, print out the absolute value w/ a leading '-'. + // Use an array large enough for a binary number. + char[] buffer = new char[65]; + int i = 65; + boolean isNeg = false; + if (num < 0) + { + isNeg = true; + num = -num; + + // When the value is MIN_VALUE, it overflows when made positive + if (num < 0) + { + buffer[--i] = digits[(int) (-(num + radix) % radix)]; + num = -(num / radix); + } + } + + do + { + buffer[--i] = digits[(int) (num % radix)]; + num /= radix; + } + while (num > 0); + + if (isNeg) + buffer[--i] = '-'; + + // Package constructor avoids an array copy. + return new String(buffer, i, 65 - i, true); + } + + /** + * Converts the <code>long</code> to a <code>String</code> assuming it is + * unsigned in base 16. + * + * @param l the <code>long</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toHexString(long l) + { + return toUnsignedString(l, 4); + } + + /** + * Converts the <code>long</code> to a <code>String</code> assuming it is + * unsigned in base 8. + * + * @param l the <code>long</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toOctalString(long l) + { + return toUnsignedString(l, 3); + } + + /** + * Converts the <code>long</code> to a <code>String</code> assuming it is + * unsigned in base 2. + * + * @param l the <code>long</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toBinaryString(long l) + { + return toUnsignedString(l, 1); + } + + /** + * Converts the <code>long</code> to a <code>String</code> and assumes + * a radix of 10. + * + * @param num the <code>long</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + * @see #toString(long, int) + */ + public static String toString(long num) + { + return toString(num, 10); + } + + /** + * Converts the specified <code>String</code> into an <code>int</code> + * using the specified radix (base). The string must not be <code>null</code> + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * <code>Character.digit(d, radix)</code>, and must be in the range + * <code>0</code> to <code>radix - 1</code>. Finally, the result must be + * within <code>MIN_VALUE</code> to <code>MAX_VALUE</code>, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'; and 'l' or + * 'L' as the last character is only valid in radices 22 or greater, where + * it is a digit and not a type indicator. + * + * @param str the <code>String</code> to convert + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> argument converted to <code>long</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>long</code> + */ + public static long parseLong(String str, int radix) + { + return parseLong(str, radix, false); + } + + /** + * Converts the specified <code>String</code> into a <code>long</code>. + * This function assumes a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the <code>int</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>long</code> + * @see #parseLong(String, int) + */ + public static long parseLong(String s) + { + return parseLong(s, 10, false); + } + + /** + * Creates a new <code>Long</code> object using the <code>String</code> + * and specified radix (base). + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to convert with + * @return the new <code>Long</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>long</code> + * @see #parseLong(String, int) + */ + public static Long valueOf(String s, int radix) + { + return new Long(parseLong(s, radix, false)); + } + + /** + * Creates a new <code>Long</code> object using the <code>String</code>, + * assuming a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the new <code>Long</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>long</code> + * @see #Long(String) + * @see #parseLong(String) + */ + public static Long valueOf(String s) + { + return new Long(parseLong(s, 10, false)); + } + + /** + * Convert the specified <code>String</code> into a <code>Long</code>. + * The <code>String</code> may represent decimal, hexadecimal, or + * octal numbers. + * + * <p>The extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> ] <em>DecimalNumber</em> ) + * | ( [ <code>-</code> ] ( <code>0x</code> | <code>0X</code> + * | <code>#</code> ) <em>HexDigit</em> { <em>HexDigit</em> } ) + * | ( [ <code>-</code> ] <code>0</code> { <em>OctalDigit</em> } ) + * <em>DecimalNumber</em>: + * <em>DecimalDigit except '0'</em> { <em>DecimalDigit</em> } + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 10) has value 0 to 9</em> + * <em>OctalDigit</em>: + * <em>Character.digit(d, 8) has value 0 to 7</em> + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 16) has value 0 to 15</em> + * </pre> + * Finally, the value must be in the range <code>MIN_VALUE</code> to + * <code>MAX_VALUE</code>, or an exception is thrown. Note that you cannot + * use a trailing 'l' or 'L', unlike in Java source code. + * + * @param str the <code>String</code> to interpret + * @return the value of the String as a <code>Long</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>long</code> + * @throws NullPointerException if <code>s</code> is null + * @since 1.2 + */ + public static Long decode(String str) + { + return new Long(parseLong(str, 10, true)); + } + + /** + * Return the value of this <code>Long</code> as a <code>byte</code>. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this <code>Long</code> as a <code>short</code>. + * + * @return the short value + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this <code>Long</code> as an <code>int</code>. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this <code>Long</code>. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this <code>Long</code> as a <code>float</code>. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this <code>Long</code> as a <code>double</code>. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the <code>Long</code> value to a <code>String</code> and + * assumes a radix of 10. + * + * @return the <code>String</code> representation + */ + public String toString() + { + return toString(value, 10); + } + + /** + * Return a hashcode representing this Object. <code>Long</code>'s hash + * code is calculated by <code>(int) (value ^ (value >> 32))</code>. + * + * @return this Object's hash code + */ + public int hashCode() + { + return (int) (value ^ (value >>> 32)); + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Long</code> and represents the same long value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Long && value == ((Long) obj).value; + } + + /** + * Get the specified system property as a <code>Long</code>. The + * <code>decode()</code> method will be used to interpret the value of + * the property. + * + * @param nm the name of the system property + * @return the system property as a <code>Long</code>, or null if the + * property is not found or cannot be decoded + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm) + { + return getLong(nm, null); + } + + /** + * Get the specified system property as a <code>Long</code>, or use a + * default <code>long</code> value if the property is not found or is not + * decodable. The <code>decode()</code> method will be used to interpret + * the value of the property. + * + * @param nm the name of the system property + * @param val the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm, long val) + { + Long result = getLong(nm, null); + return result == null ? new Long(val) : result; + } + + /** + * Get the specified system property as a <code>Long</code>, or use a + * default <code>Long</code> value if the property is not found or is + * not decodable. The <code>decode()</code> method will be used to + * interpret the value of the property. + * + * @param nm the name of the system property + * @param def the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm, Long def) + { + if (nm == null || "".equals(nm)) + return def; + nm = System.getProperty(nm); + if (nm == null) + return def; + try + { + return decode(nm); + } + catch (NumberFormatException e) + { + return def; + } + } + + /** + * Compare two Longs numerically by comparing their <code>long</code> + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param l the Long to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Long l) + { + if (value == l.value) + return 0; + // Returns just -1 or 1 on inequality; doing math might overflow the long. + return value > l.value ? 1 : -1; + } + + /** + * Behaves like <code>compareTo(Long)</code> unless the Object + * is not a <code>Long</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not a <code>Long</code> + * @see #compareTo(Long) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((Long) o); + } + + /** + * Helper for converting unsigned numbers to String. + * + * @param num the number + * @param exp log2(digit) (ie. 1, 3, or 4 for binary, oct, hex) + */ + private static String toUnsignedString(long num, int exp) + { + // Use the Integer toUnsignedString for efficiency if possible. + // If NUM<0 then this particular optimization doesn't work + // properly. + if (num >= 0 && (int) num == num) + return Integer.toUnsignedString((int) num, exp); + + // Use an array large enough for a binary number. + int mask = (1 << exp) - 1; + char[] buffer = new char[64]; + int i = 64; + do + { + buffer[--i] = digits[(int) num & mask]; + num >>>= exp; + } + while (num != 0); + + // Package constructor avoids an array copy. + return new String(buffer, i, 64 - i, true); + } + + /** + * Helper for parsing longs. + * + * @param str the string to parse + * @param radix the radix to use, must be 10 if decode is true + * @param decode if called from decode + * @return the parsed long value + * @throws NumberFormatException if there is an error + * @throws NullPointerException if decode is true and str is null + * @see #parseLong(String, int) + * @see #decode(String) + */ + private static long parseLong(String str, int radix, boolean decode) + { + if (! decode && str == null) + throw new NumberFormatException(); + int index = 0; + int len = str.length(); + boolean isNeg = false; + if (len == 0) + throw new NumberFormatException(); + int ch = str.charAt(index); + if (ch == '-') + { + if (len == 1) + throw new NumberFormatException(); + isNeg = true; + ch = str.charAt(++index); + } + if (decode) + { + if (ch == '0') + { + if (++index == len) + return 0; + if ((str.charAt(index) & ~('x' ^ 'X')) == 'X') + { + radix = 16; + index++; + } + else + radix = 8; + } + else if (ch == '#') + { + radix = 16; + index++; + } + } + if (index == len) + throw new NumberFormatException(); + + long max = MAX_VALUE / radix; + // We can't directly write `max = (MAX_VALUE + 1) / radix'. + // So instead we fake it. + if (isNeg && MAX_VALUE % radix == radix - 1) + ++max; + + long val = 0; + while (index < len) + { + if (val < 0 || val > max) + throw new NumberFormatException(); + + ch = Character.digit(str.charAt(index++), radix); + val = val * radix + ch; + if (ch < 0 || (val < 0 && (! isNeg || val != MIN_VALUE))) + throw new NumberFormatException(); + } + return isNeg ? -val : val; + } +} diff --git a/libjava/classpath/java/lang/Math.java b/libjava/classpath/java/lang/Math.java new file mode 100644 index 00000000000..08081e2523a --- /dev/null +++ b/libjava/classpath/java/lang/Math.java @@ -0,0 +1,650 @@ +/* java.lang.Math -- common mathematical functions, native allowed + Copyright (C) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.Configuration; + +import java.util.Random; + +/** + * Helper class containing useful mathematical functions and constants. + * <P> + * + * Note that angles are specified in radians. Conversion functions are + * provided for your convenience. + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + */ +public final class Math +{ + /** + * Math is non-instantiable + */ + private Math() + { + } + + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javalang"); + } + } + + /** + * A random number generator, initialized on first use. + */ + private static Random rand; + + /** + * The most accurate approximation to the mathematical constant <em>e</em>: + * <code>2.718281828459045</code>. Used in natural log and exp. + * + * @see #log(double) + * @see #exp(double) + */ + public static final double E = 2.718281828459045; + + /** + * The most accurate approximation to the mathematical constant <em>pi</em>: + * <code>3.141592653589793</code>. This is the ratio of a circle's diameter + * to its circumference. + */ + public static final double PI = 3.141592653589793; + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + * <P> + * + * Note that the the largest negative value (Integer.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a <em>negative</em> value. You have been warned. + * + * @param i the number to take the absolute value of + * @return the absolute value + * @see Integer#MIN_VALUE + */ + public static int abs(int i) + { + return (i < 0) ? -i : i; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + * <P> + * + * Note that the the largest negative value (Long.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a <em>negative</em> value. You have been warned. + * + * @param l the number to take the absolute value of + * @return the absolute value + * @see Long#MIN_VALUE + */ + public static long abs(long l) + { + return (l < 0) ? -l : l; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + * <P> + * + * This is equivalent, but faster than, calling + * <code>Float.intBitsToFloat(0x7fffffff & Float.floatToIntBits(a))</code>. + * + * @param f the number to take the absolute value of + * @return the absolute value + */ + public static float abs(float f) + { + return (f <= 0) ? 0 - f : f; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + * + * This is equivalent, but faster than, calling + * <code>Double.longBitsToDouble(Double.doubleToLongBits(a) + * << 1) >>> 1);</code>. + * + * @param d the number to take the absolute value of + * @return the absolute value + */ + public static double abs(double d) + { + return (d <= 0) ? 0 - d : d; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static int min(int a, int b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static long min(long a, long b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static float min(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static double min(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static int max(int a, int b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static long max(long a, long b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static float max(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static double max(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * The trigonometric function <em>sin</em>. The sine of NaN or infinity is + * NaN, and the sine of 0 retains its sign. This is accurate within 1 ulp, + * and is semi-monotonic. + * + * @param a the angle (in radians) + * @return sin(a) + */ + public static native double sin(double a); + + /** + * The trigonometric function <em>cos</em>. The cosine of NaN or infinity is + * NaN. This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the angle (in radians) + * @return cos(a) + */ + public static native double cos(double a); + + /** + * The trigonometric function <em>tan</em>. The tangent of NaN or infinity + * is NaN, and the tangent of 0 retains its sign. This is accurate within 1 + * ulp, and is semi-monotonic. + * + * @param a the angle (in radians) + * @return tan(a) + */ + public static native double tan(double a); + + /** + * The trigonometric function <em>arcsin</em>. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN; and the arcsine of + * 0 retains its sign. This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the sin to turn back into an angle + * @return arcsin(a) + */ + public static native double asin(double a); + + /** + * The trigonometric function <em>arccos</em>. The range of angles returned + * is 0 to pi radians (0 to 180 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN. This is accurate + * within 1 ulp, and is semi-monotonic. + * + * @param a the cos to turn back into an angle + * @return arccos(a) + */ + public static native double acos(double a); + + /** + * The trigonometric function <em>arcsin</em>. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN, the + * result is NaN; and the arctangent of 0 retains its sign. This is accurate + * within 1 ulp, and is semi-monotonic. + * + * @param a the tan to turn back into an angle + * @return arcsin(a) + * @see #atan2(double, double) + */ + public static native double atan(double a); + + /** + * A special version of the trigonometric function <em>arctan</em>, for + * converting rectangular coordinates <em>(x, y)</em> to polar + * <em>(r, theta)</em>. This computes the arctangent of x/y in the range + * of -pi to pi radians (-180 to 180 degrees). Special cases:<ul> + * <li>If either argument is NaN, the result is NaN.</li> + * <li>If the first argument is positive zero and the second argument is + * positive, or the first argument is positive and finite and the second + * argument is positive infinity, then the result is positive zero.</li> + * <li>If the first argument is negative zero and the second argument is + * positive, or the first argument is negative and finite and the second + * argument is positive infinity, then the result is negative zero.</li> + * <li>If the first argument is positive zero and the second argument is + * negative, or the first argument is positive and finite and the second + * argument is negative infinity, then the result is the double value + * closest to pi.</li> + * <li>If the first argument is negative zero and the second argument is + * negative, or the first argument is negative and finite and the second + * argument is negative infinity, then the result is the double value + * closest to -pi.</li> + * <li>If the first argument is positive and the second argument is + * positive zero or negative zero, or the first argument is positive + * infinity and the second argument is finite, then the result is the + * double value closest to pi/2.</li> + * <li>If the first argument is negative and the second argument is + * positive zero or negative zero, or the first argument is negative + * infinity and the second argument is finite, then the result is the + * double value closest to -pi/2.</li> + * <li>If both arguments are positive infinity, then the result is the + * double value closest to pi/4.</li> + * <li>If the first argument is positive infinity and the second argument + * is negative infinity, then the result is the double value closest to + * 3*pi/4.</li> + * <li>If the first argument is negative infinity and the second argument + * is positive infinity, then the result is the double value closest to + * -pi/4.</li> + * <li>If both arguments are negative infinity, then the result is the + * double value closest to -3*pi/4.</li> + * + * </ul><p>This is accurate within 2 ulps, and is semi-monotonic. To get r, + * use sqrt(x*x+y*y). + * + * @param y the y position + * @param x the x position + * @return <em>theta</em> in the conversion of (x, y) to (r, theta) + * @see #atan(double) + */ + public static native double atan2(double y, double x); + + /** + * Take <em>e</em><sup>a</sup>. The opposite of <code>log()</code>. If the + * argument is NaN, the result is NaN; if the argument is positive infinity, + * the result is positive infinity; and if the argument is negative + * infinity, the result is positive zero. This is accurate within 1 ulp, + * and is semi-monotonic. + * + * @param a the number to raise to the power + * @return the number raised to the power of <em>e</em> + * @see #log(double) + * @see #pow(double, double) + */ + public static native double exp(double a); + + /** + * Take ln(a) (the natural log). The opposite of <code>exp()</code>. If the + * argument is NaN or negative, the result is NaN; if the argument is + * positive infinity, the result is positive infinity; and if the argument + * is either zero, the result is negative infinity. This is accurate within + * 1 ulp, and is semi-monotonic. + * + * <p>Note that the way to get log<sub>b</sub>(a) is to do this: + * <code>ln(a) / ln(b)</code>. + * + * @param a the number to take the natural log of + * @return the natural log of <code>a</code> + * @see #exp(double) + */ + public static native double log(double a); + + /** + * Take a square root. If the argument is NaN or negative, the result is + * NaN; if the argument is positive infinity, the result is positive + * infinity; and if the result is either zero, the result is the same. + * This is accurate within the limits of doubles. + * + * <p>For other roots, use pow(a, 1 / rootNumber). + * + * @param a the numeric argument + * @return the square root of the argument + * @see #pow(double, double) + */ + public static native double sqrt(double a); + + /** + * Raise a number to a power. Special cases:<ul> + * <li>If the second argument is positive or negative zero, then the result + * is 1.0.</li> + * <li>If the second argument is 1.0, then the result is the same as the + * first argument.</li> + * <li>If the second argument is NaN, then the result is NaN.</li> + * <li>If the first argument is NaN and the second argument is nonzero, + * then the result is NaN.</li> + * <li>If the absolute value of the first argument is greater than 1 and + * the second argument is positive infinity, or the absolute value of the + * first argument is less than 1 and the second argument is negative + * infinity, then the result is positive infinity.</li> + * <li>If the absolute value of the first argument is greater than 1 and + * the second argument is negative infinity, or the absolute value of the + * first argument is less than 1 and the second argument is positive + * infinity, then the result is positive zero.</li> + * <li>If the absolute value of the first argument equals 1 and the second + * argument is infinite, then the result is NaN.</li> + * <li>If the first argument is positive zero and the second argument is + * greater than zero, or the first argument is positive infinity and the + * second argument is less than zero, then the result is positive zero.</li> + * <li>If the first argument is positive zero and the second argument is + * less than zero, or the first argument is positive infinity and the + * second argument is greater than zero, then the result is positive + * infinity.</li> + * <li>If the first argument is negative zero and the second argument is + * greater than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is less than zero but not a + * finite odd integer, then the result is positive zero.</li> + * <li>If the first argument is negative zero and the second argument is a + * positive finite odd integer, or the first argument is negative infinity + * and the second argument is a negative finite odd integer, then the result + * is negative zero.</li> + * <li>If the first argument is negative zero and the second argument is + * less than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is greater than zero but not a + * finite odd integer, then the result is positive infinity.</li> + * <li>If the first argument is negative zero and the second argument is a + * negative finite odd integer, or the first argument is negative infinity + * and the second argument is a positive finite odd integer, then the result + * is negative infinity.</li> + * <li>If the first argument is less than zero and the second argument is a + * finite even integer, then the result is equal to the result of raising + * the absolute value of the first argument to the power of the second + * argument.</li> + * <li>If the first argument is less than zero and the second argument is a + * finite odd integer, then the result is equal to the negative of the + * result of raising the absolute value of the first argument to the power + * of the second argument.</li> + * <li>If the first argument is finite and less than zero and the second + * argument is finite and not an integer, then the result is NaN.</li> + * <li>If both arguments are integers, then the result is exactly equal to + * the mathematical result of raising the first argument to the power of + * the second argument if that result can in fact be represented exactly as + * a double value.</li> + * + * </ul><p>(In the foregoing descriptions, a floating-point value is + * considered to be an integer if and only if it is a fixed point of the + * method {@link #ceil(double)} or, equivalently, a fixed point of the + * method {@link #floor(double)}. A value is a fixed point of a one-argument + * method if and only if the result of applying the method to the value is + * equal to the value.) This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the number to raise + * @param b the power to raise it to + * @return a<sup>b</sup> + */ + public static native double pow(double a, double b); + + /** + * Get the IEEE 754 floating point remainder on two numbers. This is the + * value of <code>x - y * <em>n</em></code>, where <em>n</em> is the closest + * double to <code>x / y</code> (ties go to the even n); for a zero + * remainder, the sign is that of <code>x</code>. If either argument is NaN, + * the first argument is infinite, or the second argument is zero, the result + * is NaN; if x is finite but y is infinite, the result is x. This is + * accurate within the limits of doubles. + * + * @param x the dividend (the top half) + * @param y the divisor (the bottom half) + * @return the IEEE 754-defined floating point remainder of x/y + * @see #rint(double) + */ + public static native double IEEEremainder(double x, double y); + + /** + * Take the nearest integer that is that is greater than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same; if the argument is between -1 and 0, the result is negative zero. + * Note that <code>Math.ceil(x) == -Math.floor(-x)</code>. + * + * @param a the value to act upon + * @return the nearest integer >= <code>a</code> + */ + public static native double ceil(double a); + + /** + * Take the nearest integer that is that is less than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same. Note that <code>Math.ceil(x) == -Math.floor(-x)</code>. + * + * @param a the value to act upon + * @return the nearest integer <= <code>a</code> + */ + public static native double floor(double a); + + /** + * Take the nearest integer to the argument. If it is exactly between + * two integers, the even integer is taken. If the argument is NaN, + * infinite, or zero, the result is the same. + * + * @param a the value to act upon + * @return the nearest integer to <code>a</code> + */ + public static native double rint(double a); + + /** + * Take the nearest integer to the argument. This is equivalent to + * <code>(int) Math.floor(a + 0.5f)</code>. If the argument is NaN, the result + * is 0; otherwise if the argument is outside the range of int, the result + * will be Integer.MIN_VALUE or Integer.MAX_VALUE, as appropriate. + * + * @param a the argument to round + * @return the nearest integer to the argument + * @see Integer#MIN_VALUE + * @see Integer#MAX_VALUE + */ + public static int round(float a) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return 0; + return (int) floor(a + 0.5f); + } + + /** + * Take the nearest long to the argument. This is equivalent to + * <code>(long) Math.floor(a + 0.5)</code>. If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of long, the + * result will be Long.MIN_VALUE or Long.MAX_VALUE, as appropriate. + * + * @param a the argument to round + * @return the nearest long to the argument + * @see Long#MIN_VALUE + * @see Long#MAX_VALUE + */ + public static long round(double a) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return 0; + return (long) floor(a + 0.5d); + } + + /** + * Get a random number. This behaves like Random.nextDouble(), seeded by + * System.currentTimeMillis() when first called. In other words, the number + * is from a pseudorandom sequence, and lies in the range [+0.0, 1.0). + * This random sequence is only used by this method, and is threadsafe, + * although you may want your own random number generator if it is shared + * among threads. + * + * @return a random number + * @see Random#nextDouble() + * @see System#currentTimeMillis() + */ + public static synchronized double random() + { + if (rand == null) + rand = new Random(); + return rand.nextDouble(); + } + + /** + * Convert from degrees to radians. The formula for this is + * radians = degrees * (pi/180); however it is not always exact given the + * limitations of floating point numbers. + * + * @param degrees an angle in degrees + * @return the angle in radians + * @since 1.2 + */ + public static double toRadians(double degrees) + { + return (degrees * PI) / 180; + } + + /** + * Convert from radians to degrees. The formula for this is + * degrees = radians * (180/pi); however it is not always exact given the + * limitations of floating point numbers. + * + * @param rads an angle in radians + * @return the angle in degrees + * @since 1.2 + */ + public static double toDegrees(double rads) + { + return (rads * 180) / PI; + } +} diff --git a/libjava/classpath/java/lang/NegativeArraySizeException.java b/libjava/classpath/java/lang/NegativeArraySizeException.java new file mode 100644 index 00000000000..fcfa52e475c --- /dev/null +++ b/libjava/classpath/java/lang/NegativeArraySizeException.java @@ -0,0 +1,77 @@ +/* NegativeArraySizeException.java -- thrown on attempt to create array + with a negative size + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to create an array with a negative + * size. For example:<br> + * <pre> + * int i = -1; + * int[] array = new int[i]; + * </pre> + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NegativeArraySizeException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8960118058596991861L; + + /** + * Create an exception without a message. + */ + public NegativeArraySizeException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NegativeArraySizeException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoClassDefFoundError.java b/libjava/classpath/java/lang/NoClassDefFoundError.java new file mode 100644 index 00000000000..7e8e6caff80 --- /dev/null +++ b/libjava/classpath/java/lang/NoClassDefFoundError.java @@ -0,0 +1,76 @@ +/* NoClassDefFoundError.java -- thrown when a ClassLoader cannot find a class + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>NoClassDefFoundError</code> is thrown when a classloader or the + * Java Virtual Machine tries to load a class and no definition of the class + * can be found. This could happen when using the <code>new</code> expression + * or during a normal method call. The reason this would occur at runtime is + * because the missing class definition existed when the currently executing + * class was compiled, but now that definition cannot be found. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoClassDefFoundError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 9095859863287012458L; + + /** + * Create an error without a message. + */ + public NoClassDefFoundError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoClassDefFoundError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchFieldError.java b/libjava/classpath/java/lang/NoSuchFieldError.java new file mode 100644 index 00000000000..af42e35dcf2 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchFieldError.java @@ -0,0 +1,74 @@ +/* NoSuchFieldError.java -- thrown when the linker does not find a field + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>NoSuchFieldError</code> is thrown if an application attempts + * to access a field of a class, and that class no longer has that field. + * This is normally detected by the compiler, so it signals that you are + * using binary incompatible class versions. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchFieldError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3456430195886129035L; + + /** + * Create an error without a message. + */ + public NoSuchFieldError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoSuchFieldError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchFieldException.java b/libjava/classpath/java/lang/NoSuchFieldException.java new file mode 100644 index 00000000000..74d52d137c4 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchFieldException.java @@ -0,0 +1,73 @@ +/* NoSuchFieldException.java -- thrown when reflecting a non-existant field + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate the class does not have the specified field. This is + * caused by a variety of reflection methods, when looking up a field by name. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoSuchFieldException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -6143714805279938260L; + + /** + * Create an exception without a message. + */ + public NoSuchFieldException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchFieldException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchMethodError.java b/libjava/classpath/java/lang/NoSuchMethodError.java new file mode 100644 index 00000000000..2bda776e8e9 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchMethodError.java @@ -0,0 +1,74 @@ +/* NoSuchMethodError.java -- thrown when the linker does not find a method + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>NoSuchMethodError</code> is thrown if an application attempts + * to access a method of a class, and that class no longer has that method. + * This is normally detected by the compiler, so it signals that you are + * using binary incompatible class versions. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchMethodError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + static final long serialVersionUID = -3765521442372831335L; + + /** + * Create an error without a message. + */ + public NoSuchMethodError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoSuchMethodError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchMethodException.java b/libjava/classpath/java/lang/NoSuchMethodException.java new file mode 100644 index 00000000000..e423efb79f6 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchMethodException.java @@ -0,0 +1,72 @@ +/* NoSuchMethodException.java -- thrown when reflecting a non-existant method + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate the class does not have the specified method. This is + * caused by a variety of reflection methods, when looking up a method by name. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchMethodException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5034388446362600923L; + + /** + * Create an exception without a message. + */ + public NoSuchMethodException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchMethodException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NullPointerException.java b/libjava/classpath/java/lang/NullPointerException.java new file mode 100644 index 00000000000..29a4ee086c5 --- /dev/null +++ b/libjava/classpath/java/lang/NullPointerException.java @@ -0,0 +1,82 @@ +/* NullPointerException.java -- thrown when using null instead of an object + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when attempting to use <code>null</code> where an object + * is required. The Virtual Machine automatically throws this exception + * for the following:<br><ul> + * <li>Calling an instance method on a null object</li> + * <li>Accessing or modifying a field of a null object</li> + * <li>Taking the array length of a null array</li> + * <li>Accessing or modifying the slots of a null array</li> + * <li>Throwing a null Throwable</li> + * <li>Synchronizing on a null object</li> + * </ul> + * <p>Applications should also throw NullPointerExceptions whenever + * <code>null</code> is an inappropriate parameter to a method. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NullPointerException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5162710183389028792L; + + /** + * Create an exception without a message. + */ + public NullPointerException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NullPointerException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Number.java b/libjava/classpath/java/lang/Number.java new file mode 100644 index 00000000000..eb81f78c86e --- /dev/null +++ b/libjava/classpath/java/lang/Number.java @@ -0,0 +1,131 @@ +/* Number.java =- abstract superclass of numeric objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * Number is a generic superclass of all the numeric classes, including + * the wrapper classes {@link Byte}, {@link Short}, {@link Integer}, + * {@link Long}, {@link Float}, and {@link Double}. Also worth mentioning + * are the classes in {@link java.math}. + * + * It provides ways to convert numeric objects to any primitive. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Number implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8742448824652078965L; + + /** + * Table for calculating digits, used in Character, Long, and Integer. + */ + static final char[] digits = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + }; + + /** + * The basic constructor (often called implicitly). + */ + public Number() + { + } + + /** + * Return the value of this <code>Number</code> as an <code>int</code>. + * + * @return the int value + */ + public abstract int intValue(); + + /** + * Return the value of this <code>Number</code> as a <code>long</code>. + * + * @return the long value + */ + public abstract long longValue(); + + /** + * Return the value of this <code>Number</code> as a <code>float</code>. + * + * @return the float value + */ + public abstract float floatValue(); + + /** + * Return the value of this <code>Number</code> as a <code>float</code>. + * + * @return the double value + */ + public abstract double doubleValue(); + + /** + * Return the value of this <code>Number</code> as a <code>byte</code>. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) intValue(); + } + + /** + * Return the value of this <code>Number</code> as a <code>short</code>. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) intValue(); + } +} diff --git a/libjava/classpath/java/lang/NumberFormatException.java b/libjava/classpath/java/lang/NumberFormatException.java new file mode 100644 index 00000000000..bf98156d13c --- /dev/null +++ b/libjava/classpath/java/lang/NumberFormatException.java @@ -0,0 +1,73 @@ +/* NumberFormatException.java -- thrown when parsing a bad string as a number + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Can be thrown when attempting to convert a <code>String</code> to + * one of the numeric types, but the operation fails because the string + * has the wrong format. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NumberFormatException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2848938806368998894L; + + /** + * Create an exception without a message. + */ + public NumberFormatException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NumberFormatException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Object.java b/libjava/classpath/java/lang/Object.java new file mode 100644 index 00000000000..f8c389a57ee --- /dev/null +++ b/libjava/classpath/java/lang/Object.java @@ -0,0 +1,530 @@ +/* java.lang.Object - The universal superclass in Java + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + + +/** + * Object is the ultimate superclass of every class + * (excepting interfaces). When you define a class that + * does not extend any other class, it implicitly extends + * java.lang.Object. Also, an anonymous class based on + * an interface will extend Object. + * + * <p>It provides general-purpose methods that every single + * Object, regardless of race, sex or creed, implements. + * All of the public methods may be invoked on arrays or + * interfaces. The protected methods <code>clone</code> + * and <code>finalize</code> are not accessible on arrays + * or interfaces, but all array types have a public version + * of <code>clone</code> which is accessible. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class Object +{ + // WARNING: Object is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + // Many JVMs do not allow for static initializers in this class, + // hence we do not use them in the default implementation. + + // Some VM's rely on the order that these methods appear when laying + // out their internal structure. Therefore, do not haphazardly + // rearrange these methods. + + /** + * The basic constructor. Object is special, because it has no + * superclass, so there is no call to super(). + * + * @throws OutOfMemoryError Technically, this constructor never + * throws an OutOfMemoryError, because the memory has + * already been allocated by this point. But as all + * instance creation expressions eventually trace back + * to this constructor, and creating an object allocates + * memory, we list that possibility here. + */ + // This could be implicit, but then javadoc would not document it! + public Object() {} + + /** + * Determine whether this Object is semantically equal + * to another Object. + * + * <p>There are some fairly strict requirements on this + * method which subclasses must follow:<br> + * <ul> + * <li>It must be transitive. If <code>a.equals(b)</code> and + * <code>b.equals(c)</code>, then <code>a.equals(c)</code> + * must be true as well.</li> + * <li>It must be symmetric. <code>a.equals(b)</code> and + * <code>b.equals(a)</code> must have the same value.</li> + * <li>It must be reflexive. <code>a.equals(a)</code> must + * always be true.</li> + * <li>It must be consistent. Whichever value a.equals(b) + * returns on the first invocation must be the value + * returned on all later invocations.</li> + * <li><code>a.equals(null)</code> must be false.</li> + * <li>It must be consistent with hashCode(). That is, + * <code>a.equals(b)</code> must imply + * <code>a.hashCode() == b.hashCode()</code>. + * The reverse is not true; two objects that are not + * equal may have the same hashcode, but that has + * the potential to harm hashing performance.</li> + * </ul> + * + * <p>This is typically overridden to throw a {@link ClassCastException} + * if the argument is not comparable to the class performing + * the comparison, but that is not a requirement. It is legal + * for <code>a.equals(b)</code> to be true even though + * <code>a.getClass() != b.getClass()</code>. Also, it + * is typical to never cause a {@link NullPointerException}. + * + * <p>In general, the Collections API ({@link java.util}) use the + * <code>equals</code> method rather than the <code>==</code> + * operator to compare objects. However, {@link java.util.IdentityHashMap} + * is an exception to this rule, for its own good reasons. + * + * <p>The default implementation returns <code>this == o</code>. + * + * @param obj the Object to compare to + * @return whether this Object is semantically equal to another + * @see #hashCode() + */ + public boolean equals(Object obj) + { + return this == obj; + } + + /** + * Get a value that represents this Object, as uniquely as + * possible within the confines of an int. + * + * <p>There are some requirements on this method which + * subclasses must follow:<br> + * + * <ul> + * <li>Semantic equality implies identical hashcodes. In other + * words, if <code>a.equals(b)</code> is true, then + * <code>a.hashCode() == b.hashCode()</code> must be as well. + * However, the reverse is not necessarily true, and two + * objects may have the same hashcode without being equal.</li> + * <li>It must be consistent. Whichever value o.hashCode() + * returns on the first invocation must be the value + * returned on all later invocations as long as the object + * exists. Notice, however, that the result of hashCode may + * change between separate executions of a Virtual Machine, + * because it is not invoked on the same object.</li> + * </ul> + * + * <p>Notice that since <code>hashCode</code> is used in + * {@link java.util.Hashtable} and other hashing classes, + * a poor implementation will degrade the performance of hashing + * (so don't blindly implement it as returning a constant!). Also, + * if calculating the hash is time-consuming, a class may consider + * caching the results. + * + * <p>The default implementation returns + * <code>System.identityHashCode(this)</code> + * + * @return the hash code for this Object + * @see #equals(Object) + * @see System#identityHashCode(Object) + */ + public int hashCode() + { + return System.identityHashCode(this); + } + + /** + * Convert this Object to a human-readable String. + * There are no limits placed on how long this String + * should be or what it should contain. We suggest you + * make it as intuitive as possible to be able to place + * it into {@link java.io.PrintStream#println() System.out.println()} + * and such. + * + * <p>It is typical, but not required, to ensure that this method + * never completes abruptly with a {@link RuntimeException}. + * + * <p>This method will be called when performing string + * concatenation with this object. If the result is + * <code>null</code>, string concatenation will instead + * use <code>"null"</code>. + * + * <p>The default implementation returns + * <code>getClass().getName() + "@" + + * Integer.toHexString(hashCode())</code>. + * + * @return the String representing this Object, which may be null + * @throws OutOfMemoryError The default implementation creates a new + * String object, therefore it must allocate memory + * @see #getClass() + * @see #hashCode() + * @see Class#getName() + * @see Integer#toHexString(int) + */ + public String toString() + { + return getClass().getName() + '@' + Integer.toHexString(hashCode()); + } + + /** + * Called on an object by the Virtual Machine at most once, + * at some point after the Object is determined unreachable + * but before it is destroyed. You would think that this + * means it eventually is called on every Object, but this is + * not necessarily the case. If execution terminates + * abnormally, garbage collection does not always happen. + * Thus you cannot rely on this method to always work. + * For finer control over garbage collection, use references + * from the {@link java.lang.ref} package. + * + * <p>Virtual Machines are free to not call this method if + * they can determine that it does nothing important; for + * example, if your class extends Object and overrides + * finalize to do simply <code>super.finalize()</code>. + * + * <p>finalize() will be called by a {@link Thread} that has no + * locks on any Objects, and may be called concurrently. + * There are no guarantees on the order in which multiple + * objects are finalized. This means that finalize() is + * usually unsuited for performing actions that must be + * thread-safe, and that your implementation must be + * use defensive programming if it is to always work. + * + * <p>If an Exception is thrown from finalize() during garbage + * collection, it will be patently ignored and the Object will + * still be destroyed. + * + * <p>It is allowed, although not typical, for user code to call + * finalize() directly. User invocation does not affect whether + * automatic invocation will occur. It is also permitted, + * although not recommended, for a finalize() method to "revive" + * an object by making it reachable from normal code again. + * + * <p>Unlike constructors, finalize() does not get called + * for an object's superclass unless the implementation + * specifically calls <code>super.finalize()</code>. + * + * <p>The default implementation does nothing. + * + * @throws Throwable permits a subclass to throw anything in an + * overridden version; but the default throws nothing + * @see System#gc() + * @see System#runFinalizersOnExit(boolean) + * @see java.lang.ref + */ + protected void finalize() throws Throwable + { + } + + /** + * This method may be called to create a new copy of the + * Object. The typical behavior is as follows:<br> + * <ul> + * <li><code>o == o.clone()</code> is false</li> + * <li><code>o.getClass() == o.clone().getClass()</code> + * is true</li> + * <li><code>o.equals(o)</code> is true</li> + * </ul> + * + * <p>However, these are not strict requirements, and may + * be violated if necessary. Of the three requirements, the + * last is the most commonly violated, particularly if the + * subclass does not override {@link #equals(Object)}. + * + * <p>If the Object you call clone() on does not implement + * {@link Cloneable} (which is a placeholder interface), then + * a CloneNotSupportedException is thrown. Notice that + * Object does not implement Cloneable; this method exists + * as a convenience for subclasses that do. + * + * <p>Object's implementation of clone allocates space for the + * new Object using the correct class, without calling any + * constructors, and then fills in all of the new field values + * with the old field values. Thus, it is a shallow copy. + * However, subclasses are permitted to make a deep copy. + * + * <p>All array types implement Cloneable, and override + * this method as follows (it should never fail):<br> + * <pre> + * public Object clone() + * { + * try + * { + * super.clone(); + * } + * catch (CloneNotSupportedException e) + * { + * throw new InternalError(e.getMessage()); + * } + * } + * </pre> + * + * @return a copy of the Object + * @throws CloneNotSupportedException If this Object does not + * implement Cloneable + * @throws OutOfMemoryError Since cloning involves memory allocation, + * even though it may bypass constructors, you might run + * out of memory + * @see Cloneable + */ + protected Object clone() throws CloneNotSupportedException + { + if (this instanceof Cloneable) + return VMObject.clone((Cloneable) this); + throw new CloneNotSupportedException("Object not cloneable"); + } + + /** + * Returns the runtime {@link Class} of this Object. + * + * <p>The class object can also be obtained without a runtime + * instance by using the class literal, as in: + * <code>Foo.class</code>. Notice that the class literal + * also works on primitive types, making it useful for + * reflection purposes. + * + * @return the class of this Object + */ + public final Class getClass() + { + return VMObject.getClass(this); + } + + /** + * Wakes up one of the {@link Thread}s that has called + * <code>wait</code> on this Object. Only the owner + * of a lock on this Object may call this method. This lock + * is obtained by a <code>synchronized</code> method or statement. + * + * <p>The Thread to wake up is chosen arbitrarily. The + * awakened thread is not guaranteed to be the next thread + * to actually obtain the lock on this object. + * + * <p>This thread still holds a lock on the object, so it is + * typical to release the lock by exiting the synchronized + * code, calling wait(), or calling {@link Thread#sleep()}, so + * that the newly awakened thread can actually resume. The + * awakened thread will most likely be awakened with an + * {@link InterruptedException}, but that is not guaranteed. + * + * @throws IllegalMonitorStateException if this Thread + * does not own the lock on the Object + * @see #notifyAll() + * @see #wait() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void notify() throws IllegalMonitorStateException + { + VMObject.notify(this); + } + + /** + * Wakes up all of the {@link Thread}s that have called + * <code>wait</code> on this Object. Only the owner + * of a lock on this Object may call this method. This lock + * is obtained by a <code>synchronized</code> method or statement. + * + * <p>There are no guarantees as to which thread will next + * obtain the lock on the object. + * + * <p>This thread still holds a lock on the object, so it is + * typical to release the lock by exiting the synchronized + * code, calling wait(), or calling {@link Thread#sleep()}, so + * that one of the newly awakened threads can actually resume. + * The resuming thread will most likely be awakened with an + * {@link InterruptedException}, but that is not guaranteed. + * + * @throws IllegalMonitorStateException if this Thread + * does not own the lock on the Object + * @see #notify() + * @see #wait() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void notifyAll() throws IllegalMonitorStateException + { + VMObject.notifyAll(this); + } + + /** + * Waits indefinitely for notify() or notifyAll() to be + * called on the Object in question. Implementation is + * identical to wait(0). + * + * <p>The Thread that calls wait must have a lock on this Object, + * obtained by a <code>synchronized</code> method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + * <p>While it is typical that this method will complete abruptly + * with an {@link InterruptedException}, it is not guaranteed. So, + * it is typical to call wait inside an infinite loop:<br> + * + * <pre> + * try + * { + * while (true) + * lock.wait(); + * } + * catch (InterruptedException e) + * { + * } + * </pre> + * + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void wait() + throws IllegalMonitorStateException, InterruptedException + { + VMObject.wait(this, 0, 0); + } + + /** + * Waits a specified amount of time (or indefinitely if + * the time specified is 0) for someone to call notify() + * or notifyAll() on this Object, waking up this Thread. + * + * <p>The Thread that calls wait must have a lock on this Object, + * obtained by a <code>synchronized</code> method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + * <p>Usually, this call will complete normally if the time + * expires, or abruptly with {@link InterruptedException} + * if another thread called notify, but neither result + * is guaranteed. + * + * <p>The waiting period is only *roughly* the amount of time + * you requested. It cannot be exact because of the overhead + * of the call itself. Most Virtual Machiness treat the + * argument as a lower limit on the time spent waiting, but + * even that is not guaranteed. Besides, some other thread + * may hold the lock on the object when the time expires, so + * the current thread may still have to wait to reobtain the + * lock. + * + * @param ms the minimum number of milliseconds to wait (1000 + * milliseconds = 1 second), or 0 for an indefinite wait + * @throws IllegalArgumentException if ms < 0 + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long, int) + * @see Thread + */ + public final void wait(long ms) + throws IllegalMonitorStateException, InterruptedException + { + wait(ms, 0); + } + + /** + * Waits a specified amount of time (or indefinitely if + * the time specified is 0) for someone to call notify() + * or notifyAll() on this Object, waking up this Thread. + * + * <p>The Thread that calls wait must have a lock on this Object, + * obtained by a <code>synchronized</code> method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + * <p>Usually, this call will complete normally if the time + * expires, or abruptly with {@link InterruptedException} + * if another thread called notify, but neither result + * is guaranteed. + * + * <p>The waiting period is nowhere near as precise as + * nanoseconds; considering that even wait(int) is inaccurate, + * how much can you expect? But on supporting + * implementations, this offers somewhat more granularity + * than milliseconds. + * + * @param ms the number of milliseconds to wait (1,000 + * milliseconds = 1 second) + * @param ns the number of nanoseconds to wait over and + * above ms (1,000,000 nanoseconds = 1 millisecond) + * @throws IllegalArgumentException if ms < 0 or ns is not + * in the range 0 to 999,999 + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long) + * @see Thread + */ + public final void wait(long ms, int ns) + throws IllegalMonitorStateException, InterruptedException + { + if (ms < 0 || ns < 0 || ns > 999999) + throw new IllegalArgumentException("argument out of range"); + VMObject.wait(this, ms, ns); + } +} // class Object diff --git a/libjava/classpath/java/lang/OutOfMemoryError.java b/libjava/classpath/java/lang/OutOfMemoryError.java new file mode 100644 index 00000000000..66da563a0fb --- /dev/null +++ b/libjava/classpath/java/lang/OutOfMemoryError.java @@ -0,0 +1,73 @@ +/* OutOfMemoryError.java -- thrown when a memory allocation fails + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when the Java Virtual Machine is unable to allocate an object + * because it is out of memory and no more memory could be made available + * by the garbage collector. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class OutOfMemoryError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8228564086184010517L; + + /** + * Create an error without a message. + */ + public OutOfMemoryError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public OutOfMemoryError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Package.java b/libjava/classpath/java/lang/Package.java new file mode 100644 index 00000000000..4cded0af304 --- /dev/null +++ b/libjava/classpath/java/lang/Package.java @@ -0,0 +1,318 @@ +/* Package.java -- information about a package + Copyright (C) 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.VMStackWalker; + +import java.net.URL; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + + +/** + * Everything you ever wanted to know about a package. This class makes it + * possible to attach specification and implementation information to a + * package as explained in the + * <a href="http://java.sun.com/products/jdk/1.3/docs/guide/versioning/spec/VersioningSpecification.html#PackageVersionSpecification">Package Versioning Specification</a> + * section of the + * <a href="http://java.sun.com/products/jdk/1.3/docs/guide/versioning/spec/VersioningSpecification.html">Product Versioning Specification</a>. + * It also allows packages to be sealed with respect to the originating URL. + * + * <p>The most useful method is the <code>isCompatibleWith()</code> method that + * compares a desired version of a specification with the version of the + * specification as implemented by a package. A package is considered + * compatible with another version if the version of the specification is + * equal or higher then the requested version. Version numbers are represented + * as strings of positive numbers separated by dots (e.g. "1.2.0"). + * The first number is called the major number, the second the minor, + * the third the micro, etc. A version is considered higher then another + * version if it has a bigger major number then the another version or when + * the major numbers of the versions are equal if it has a bigger minor number + * then the other version, etc. (If a version has no minor, micro, etc numbers + * then they are considered the be 0.) + * + * @author Mark Wielaard (mark@klomp.org) + * @see ClassLoader#definePackage(String, String, String, String, String, + * String, String, URL) + * @since 1.2 + * @status updated to 1.4 + */ +public class Package +{ + /** The name of the Package */ + private final String name; + + /** The name if the implementation */ + private final String implTitle; + + /** The vendor that wrote this implementation */ + private final String implVendor; + + /** The version of this implementation */ + private final String implVersion; + + /** The name of the specification */ + private final String specTitle; + + /** The name of the specification designer */ + private final String specVendor; + + /** The version of this specification */ + private final String specVersion; + + /** If sealed the origin of the package classes, otherwise null */ + private final URL sealed; + + /** + * A package local constructor for the Package class. All parameters except + * the <code>name</code> of the package may be <code>null</code>. + * There are no public constructors defined for Package; this is a package + * local constructor that is used by java.lang.Classloader.definePackage(). + * + * @param name The name of the Package + * @param specTitle The name of the specification + * @param specVendor The name of the specification designer + * @param specVersion The version of this specification + * @param implTitle The name of the implementation + * @param implVendor The vendor that wrote this implementation + * @param implVersion The version of this implementation + * @param sealed If sealed the origin of the package classes + */ + Package(String name, + String specTitle, String specVendor, String specVersion, + String implTitle, String implVendor, String implVersion, URL sealed) + { + if (name == null) + throw new IllegalArgumentException("null Package name"); + + this.name = name; + this.implTitle = implTitle; + this.implVendor = implVendor; + this.implVersion = implVersion; + this.specTitle = specTitle; + this.specVendor = specVendor; + this.specVersion = specVersion; + this.sealed = sealed; + } + + /** + * Returns the Package name in dot-notation. + * + * @return the non-null package name + */ + public String getName() + { + return name; + } + + /** + * Returns the name of the specification, or null if unknown. + * + * @return the specification title + */ + public String getSpecificationTitle() + { + return specTitle; + } + + /** + * Returns the version of the specification, or null if unknown. + * + * @return the specification version + */ + public String getSpecificationVersion() + { + return specVersion; + } + + /** + * Returns the name of the specification designer, or null if unknown. + * + * @return the specification vendor + */ + public String getSpecificationVendor() + { + return specVendor; + } + + /** + * Returns the name of the implementation, or null if unknown. + * + * @return the implementation title + */ + public String getImplementationTitle() + { + return implTitle; + } + + /** + * Returns the version of this implementation, or null if unknown. + * + * @return the implementation version + */ + public String getImplementationVersion() + { + return implVersion; + } + + /** + * Returns the vendor that wrote this implementation, or null if unknown. + * + * @return the implementation vendor + */ + public String getImplementationVendor() + { + return implVendor; + } + + /** + * Returns true if this Package is sealed. + * + * @return true if the package is sealed + */ + public boolean isSealed() + { + return sealed != null; + } + + /** + * Returns true if this Package is sealed and the origin of the classes is + * the given URL. + * + * @param url the URL to test + * @return true if the package is sealed by this URL + * @throws NullPointerException if url is null + */ + public boolean isSealed(URL url) + { + return url.equals(sealed); + } + + /** + * Checks if the version of the specification is higher or at least as high + * as the desired version. Comparison is done by sequentially comparing + * dotted decimal numbers from the parameter and from + * <code>getSpecificationVersion</code>. + * + * @param version the (minimal) desired version of the specification + * + * @return true if the version is compatible, false otherwise + * + * @Throws NumberFormatException if either version string is invalid + * @throws NullPointerException if either version string is null + */ + public boolean isCompatibleWith(String version) + { + StringTokenizer versionTokens = new StringTokenizer(version, "."); + StringTokenizer specTokens = new StringTokenizer(specVersion, "."); + try + { + while (versionTokens.hasMoreElements()) + { + int vers = Integer.parseInt(versionTokens.nextToken()); + int spec = Integer.parseInt(specTokens.nextToken()); + if (spec < vers) + return false; + else if (spec > vers) + return true; + // They must be equal, next Token please! + } + } + catch (NoSuchElementException e) + { + // This must have been thrown by spec.nextToken() so return false. + return false; + } + // They must have been exactly the same version. + // Or the specVersion has more subversions. That is also good. + return true; + } + + /** + * Returns the named package if it is known by the callers class loader. + * It may return null if the package is unknown, when there is no + * information on that particular package available or when the callers + * classloader is null. + * + * @param name the name of the desired package + * @return the package by that name in the current ClassLoader + */ + public static Package getPackage(String name) + { + // Get the caller's classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + return cl != null ? cl.getPackage(name) : VMClassLoader.getPackage(name); + } + + /** + * Returns all the packages that are known to the callers class loader. + * It may return an empty array if the classloader of the caller is null. + * + * @return an array of all known packages + */ + public static Package[] getPackages() + { + // Get the caller's classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + return cl != null ? cl.getPackages() : VMClassLoader.getPackages(); + } + + /** + * Returns the hashCode of the name of this package. + * + * @return the hash code + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * Returns a string representation of this package. It is specified to + * be <code>"package " + getName() + (getSpecificationTitle() == null + * ? "" : ", " + getSpecificationTitle()) + (getSpecificationVersion() + * == null ? "" : ", version " + getSpecificationVersion())</code>. + * + * @return the string representation of the package + */ + public String toString() + { + return ("package " + name + (specTitle == null ? "" : ", " + specTitle) + + (specVersion == null ? "" : ", version " + specVersion)); + } +} // class Package diff --git a/libjava/classpath/java/lang/Process.java b/libjava/classpath/java/lang/Process.java new file mode 100644 index 00000000000..b6e18ca4df3 --- /dev/null +++ b/libjava/classpath/java/lang/Process.java @@ -0,0 +1,129 @@ +/* Process.java - Represent spawned system process + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An instance of a subclass of <code>Process</code> is created by the + * <code>Runtime.exec</code> methods. Methods in <code>Process</code> + * provide a means to send input to a process, obtain the output from a + * subprocess, destroy a subprocess, obtain the exit value from a + * subprocess, and wait for a subprocess to complete. + * + * <p>This is dependent on the platform, and some processes (like native + * windowing processes, 16-bit processes in Windows, or shell scripts) may + * be limited in functionality. Because some platforms have limited buffers + * between processes, you may need to provide input and read output to prevent + * the process from blocking, or even deadlocking. + * + * <p>Even if all references to this object disapper, the process continues + * to execute to completion. There are no guarantees that the + * subprocess execute asynchronously or concurrently with the process which + * owns this object. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @see Runtime#exec(String[], String[], File) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Process +{ + /** + * Empty constructor does nothing. + */ + public Process() + { + } + + /** + * Obtain the output stream that sends data to the subprocess. This is + * the STDIN of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the output stream that pipes to the process input + */ + public abstract OutputStream getOutputStream(); + + /** + * Obtain the input stream that receives data from the subprocess. This is + * the STDOUT of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the input stream that pipes data from the process output + */ + public abstract InputStream getInputStream(); + + /** + * Obtain the input stream that receives data from the subprocess. This is + * the STDERR of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the input stream that pipes data from the process error output + */ + public abstract InputStream getErrorStream(); + + /** + * The thread calling <code>waitFor</code> will block until the subprocess + * has terminated. If the process has already terminated then the method + * immediately returns with the exit value of the subprocess. + * + * @return the subprocess exit value; 0 conventionally denotes success + * @throws InterruptedException if another thread interrupts the blocked one + */ + public abstract int waitFor() throws InterruptedException; + + /** + * When a process terminates there is associated with that termination + * an exit value for the process to indicate why it terminated. A return + * of <code>0</code> denotes normal process termination by convention. + * + * @return the exit value of the subprocess + * @throws IllegalThreadStateException if the subprocess has not terminated + */ + public abstract int exitValue(); + + /** + * Kills the subprocess and all of its children forcibly. + */ + public abstract void destroy(); +} // class Process diff --git a/libjava/classpath/java/lang/Readable.java b/libjava/classpath/java/lang/Readable.java new file mode 100644 index 00000000000..efc1985d606 --- /dev/null +++ b/libjava/classpath/java/lang/Readable.java @@ -0,0 +1,71 @@ +/* Readable.java -- A character source + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.IOException; +import java.nio.CharBuffer; + +/** + * A <code>Readable</code> object is simply a source for Unicode character + * data. On request, a <code>Readable</code> will provide its data in + * a supplied <code>CharBuffer</code>. + * + * @author Tom Tromey <tromey@redhat.com> + * @author Andrew John Hughes <gnu_andrew@member.fsf.org> + * @since 1.5 + */ +public interface Readable +{ + + /** + * Adds the character data supplied by this <code>Readable</code> + * to the specified character buffer. This method simply places + * each character into the buffer as supplied, using <code>put()</code>, + * without flipping or rewinding. + * + * @param buf the buffer to place the character data in. + * @return the number of <code>char</code> values placed in the buffer, + * or -1 if no more characters are available. + * @throws IOException if an I/O error occurs. + * @throws NullPointerException if buf is null. + * @throws ReadOnlyBufferException if buf is read only. + */ + int read(CharBuffer buf) + throws IOException; + +} diff --git a/libjava/classpath/java/lang/Runnable.java b/libjava/classpath/java/lang/Runnable.java new file mode 100644 index 00000000000..32c52b94a05 --- /dev/null +++ b/libjava/classpath/java/lang/Runnable.java @@ -0,0 +1,62 @@ +/* Runnable -- interface for a method tied to an Object; often for Threads + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Runnable is an interface you implement to indicate that your class can be + * executed as the main part of a Thread, among other places. When you want + * an entry point to run a piece of code, implement this interface and + * override run. + * + * @author Paul Fisher + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread + * @since 1.0 + * @status updated to 1.4 + */ +public interface Runnable +{ + /** + * This method will be called by whoever wishes to run your class + * implementing Runnable. Note that there are no restrictions on what + * you are allowed to do in the run method, except that you cannot + * throw a checked exception. + */ + void run(); +} diff --git a/libjava/classpath/java/lang/Runtime.java b/libjava/classpath/java/lang/Runtime.java new file mode 100644 index 00000000000..64ca5d912d9 --- /dev/null +++ b/libjava/classpath/java/lang/Runtime.java @@ -0,0 +1,796 @@ +/* Runtime.java -- access to the VM process + Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * Runtime represents the Virtual Machine. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Jeroen Frijters + */ +// No idea why this class isn't final, since you can't build a subclass! +public class Runtime +{ + /** + * The library path, to search when loading libraries. We can also safely use + * this as a lock for synchronization. + */ + private final String[] libpath; + + /** + * The thread that started the exit sequence. Access to this field must + * be thread-safe; lock on libpath to avoid deadlock with user code. + * <code>runFinalization()</code> may want to look at this to see if ALL + * finalizers should be run, because the virtual machine is about to halt. + */ + private Thread exitSequence; + + /** + * All shutdown hooks. This is initialized lazily, and set to null once all + * shutdown hooks have run. Access to this field must be thread-safe; lock + * on libpath to avoid deadlock with user code. + */ + private Set shutdownHooks; + + /** + * The one and only runtime instance. + */ + private static final Runtime current = new Runtime(); + + /** + * Not instantiable by a user, this should only create one instance. + */ + private Runtime() + { + if (current != null) + throw new InternalError("Attempt to recreate Runtime"); + + // If used by underlying VM this contains the directories where Classpath's own + // native libraries are located. + String bootPath = SystemProperties.getProperty("gnu.classpath.boot.library.path", ""); + + // If properly set by the user this contains the directories where the application's + // native libraries are located. On operating systems where a LD_LIBRARY_PATH environment + // variable is available a VM should preset java.library.path with value of this + // variable. + String path = SystemProperties.getProperty("java.library.path", "."); + String pathSep = SystemProperties.getProperty("path.separator", ":"); + String fileSep = SystemProperties.getProperty("file.separator", "/"); + + StringTokenizer t1 = new StringTokenizer(bootPath, pathSep); + StringTokenizer t2 = new StringTokenizer(path, pathSep); + libpath = new String[t1.countTokens() + t2.countTokens()]; + + int i = 0; + while(t1.hasMoreTokens()) { + String prefix = t1.nextToken(); + if (! prefix.endsWith(fileSep)) + prefix += fileSep; + + libpath[i] = prefix; + i++; + } + + while(t2.hasMoreTokens()) { + String prefix = t2.nextToken(); + if (! prefix.endsWith(fileSep)) + prefix += fileSep; + + libpath[i] = prefix; + i++; + } + } + + /** + * Get the current Runtime object for this JVM. This is necessary to access + * the many instance methods of this class. + * + * @return the current Runtime object + */ + public static Runtime getRuntime() + { + return current; + } + + /** + * Exit the Java runtime. This method will either throw a SecurityException + * or it will never return. The status code is returned to the system; often + * a non-zero status code indicates an abnormal exit. Of course, there is a + * security check, <code>checkExit(status)</code>. + * + * <p>First, all shutdown hooks are run, in unspecified order, and + * concurrently. Next, if finalization on exit has been enabled, all pending + * finalizers are run. Finally, the system calls <code>halt</code>.</p> + * + * <p>If this is run a second time after shutdown has already started, there + * are two actions. If shutdown hooks are still executing, it blocks + * indefinitely. Otherwise, if the status is nonzero it halts immediately; + * if it is zero, it blocks indefinitely. This is typically called by + * <code>System.exit</code>.</p> + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @see #addShutdownHook(Thread) + * @see #runFinalizersOnExit(boolean) + * @see #runFinalization() + * @see #halt(int) + */ + public void exit(int status) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(status); + + if (runShutdownHooks()) + halt(status); + + // Someone else already called runShutdownHooks(). + // Make sure we are not/no longer in the shutdownHooks set. + // And wait till the thread that is calling runShutdownHooks() finishes. + synchronized (libpath) + { + if (shutdownHooks != null) + { + shutdownHooks.remove(Thread.currentThread()); + // Interrupt the exit sequence thread, in case it was waiting + // inside a join on our thread. + exitSequence.interrupt(); + // Shutdown hooks are still running, so we clear status to + // make sure we don't halt. + status = 0; + } + } + + // If exit() is called again after the shutdown hooks have run, but + // while finalization for exit is going on and the status is non-zero + // we halt immediately. + if (status != 0) + halt(status); + + while (true) + try + { + exitSequence.join(); + } + catch (InterruptedException e) + { + // Ignore, we've suspended indefinitely to let all shutdown + // hooks complete, and to let any non-zero exits through, because + // this is a duplicate call to exit(0). + } + } + + /** + * On first invocation, run all the shutdown hooks and return true. + * Any subsequent invocations will simply return false. + * Note that it is package accessible so that VMRuntime can call it + * when VM exit is not triggered by a call to Runtime.exit(). + * + * @return was the current thread the first one to call this method? + */ + boolean runShutdownHooks() + { + boolean first = false; + synchronized (libpath) // Synch on libpath, not this, to avoid deadlock. + { + if (exitSequence == null) + { + first = true; + exitSequence = Thread.currentThread(); + if (shutdownHooks != null) + { + Iterator i = shutdownHooks.iterator(); + while (i.hasNext()) // Start all shutdown hooks. + try + { + ((Thread) i.next()).start(); + } + catch (IllegalThreadStateException e) + { + i.remove(); + } + } + } + } + if (first) + { + if (shutdownHooks != null) + { + // Check progress of all shutdown hooks. As a hook completes, + // remove it from the set. If a hook calls exit, it removes + // itself from the set, then waits indefinitely on the + // exitSequence thread. Once the set is empty, set it to null to + // signal all finalizer threads that halt may be called. + while (true) + { + Thread[] hooks; + synchronized (libpath) + { + hooks = new Thread[shutdownHooks.size()]; + shutdownHooks.toArray(hooks); + } + if (hooks.length == 0) + break; + for (int i = 0; i < hooks.length; i++) + { + try + { + synchronized (libpath) + { + if (!shutdownHooks.contains(hooks[i])) + continue; + } + hooks[i].join(); + synchronized (libpath) + { + shutdownHooks.remove(hooks[i]); + } + } + catch (InterruptedException x) + { + // continue waiting on the next thread + } + } + } + synchronized (libpath) + { + shutdownHooks = null; + } + } + // Run finalization on all finalizable objects (even if they are + // still reachable). + VMRuntime.runFinalizationForExit(); + } + return first; + } + + /** + * Register a new shutdown hook. This is invoked when the program exits + * normally (because all non-daemon threads ended, or because + * <code>System.exit</code> was invoked), or when the user terminates + * the virtual machine (such as by typing ^C, or logging off). There is + * a security check to add hooks, + * <code>RuntimePermission("shutdownHooks")</code>. + * + * <p>The hook must be an initialized, but unstarted Thread. The threads + * are run concurrently, and started in an arbitrary order; and user + * threads or daemons may still be running. Once shutdown hooks have + * started, they must all complete, or else you must use <code>halt</code>, + * to actually finish the shutdown sequence. Attempts to modify hooks + * after shutdown has started result in IllegalStateExceptions.</p> + * + * <p>It is imperative that you code shutdown hooks defensively, as you + * do not want to deadlock, and have no idea what other hooks will be + * running concurrently. It is also a good idea to finish quickly, as the + * virtual machine really wants to shut down!</p> + * + * <p>There are no guarantees that such hooks will run, as there are ways + * to forcibly kill a process. But in such a drastic case, shutdown hooks + * would do little for you in the first place.</p> + * + * @param hook an initialized, unstarted Thread + * @throws IllegalArgumentException if the hook is already registered or run + * @throws IllegalStateException if the virtual machine is already in + * the shutdown sequence + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #removeShutdownHook(Thread) + * @see #exit(int) + * @see #halt(int) + */ + public void addShutdownHook(Thread hook) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkPermission(new RuntimePermission("shutdownHooks")); + if (hook.isAlive() || hook.getThreadGroup() == null) + throw new IllegalArgumentException("The hook thread " + hook + " must not have been already run or started"); + synchronized (libpath) + { + if (exitSequence != null) + throw new IllegalStateException("The Virtual Machine is exiting. It is not possible anymore to add any hooks"); + if (shutdownHooks == null) + { + VMRuntime.enableShutdownHooks(); + shutdownHooks = new HashSet(); // Lazy initialization. + } + if (! shutdownHooks.add(hook)) + throw new IllegalArgumentException(hook.toString() + " had already been inserted"); + } + } + + /** + * De-register a shutdown hook. As when you registered it, there is a + * security check to remove hooks, + * <code>RuntimePermission("shutdownHooks")</code>. + * + * @param hook the hook to remove + * @return true if the hook was successfully removed, false if it was not + * registered in the first place + * @throws IllegalStateException if the virtual machine is already in + * the shutdown sequence + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #addShutdownHook(Thread) + * @see #exit(int) + * @see #halt(int) + */ + public boolean removeShutdownHook(Thread hook) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkPermission(new RuntimePermission("shutdownHooks")); + synchronized (libpath) + { + if (exitSequence != null) + throw new IllegalStateException(); + if (shutdownHooks != null) + return shutdownHooks.remove(hook); + } + return false; + } + + /** + * Forcibly terminate the virtual machine. This call never returns. It is + * much more severe than <code>exit</code>, as it bypasses all shutdown + * hooks and initializers. Use caution in calling this! Of course, there is + * a security check, <code>checkExit(status)</code>. + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #exit(int) + * @see #addShutdownHook(Thread) + */ + public void halt(int status) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(status); + VMRuntime.exit(status); + } + + /** + * Tell the VM to run the finalize() method on every single Object before + * it exits. Note that the JVM may still exit abnormally and not perform + * this, so you still don't have a guarantee. And besides that, this is + * inherently unsafe in multi-threaded code, as it may result in deadlock + * as multiple threads compete to manipulate objects. This value defaults to + * <code>false</code>. There is a security check, <code>checkExit(0)</code>. + * + * @param finalizeOnExit whether to finalize all Objects on exit + * @throws SecurityException if permission is denied + * @see #exit(int) + * @see #gc() + * @since 1.1 + * @deprecated never rely on finalizers to do a clean, thread-safe, + * mop-up from your code + */ + public static void runFinalizersOnExit(boolean finalizeOnExit) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(0); + VMRuntime.runFinalizersOnExit(finalizeOnExit); + } + + /** + * Create a new subprocess with the specified command line. Calls + * <code>exec(cmdline, null, null)</code>. A security check is performed, + * <code>checkExec</code>. + * + * @param cmdline the command to call + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null + * @throws IndexOutOfBoundsException if cmdline is "" + */ + public Process exec(String cmdline) throws IOException + { + return exec(cmdline, null, null); + } + + /** + * Create a new subprocess with the specified command line and environment. + * If the environment is null, the process inherits the environment of + * this process. Calls <code>exec(cmdline, env, null)</code>. A security + * check is performed, <code>checkExec</code>. + * + * @param cmdline the command to call + * @param env the environment to use, in the format name=value + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null, or env has null entries + * @throws IndexOutOfBoundsException if cmdline is "" + */ + public Process exec(String cmdline, String[] env) throws IOException + { + return exec(cmdline, env, null); + } + + /** + * Create a new subprocess with the specified command line, environment, and + * working directory. If the environment is null, the process inherits the + * environment of this process. If the directory is null, the process uses + * the current working directory. This splits cmdline into an array, using + * the default StringTokenizer, then calls + * <code>exec(cmdArray, env, dir)</code>. A security check is performed, + * <code>checkExec</code>. + * + * @param cmdline the command to call + * @param env the environment to use, in the format name=value + * @param dir the working directory to use + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null, or env has null entries + * @throws IndexOutOfBoundsException if cmdline is "" + * @since 1.3 + */ + public Process exec(String cmdline, String[] env, File dir) + throws IOException + { + StringTokenizer t = new StringTokenizer(cmdline); + String[] cmd = new String[t.countTokens()]; + for (int i = 0; i < cmd.length; i++) + cmd[i] = t.nextToken(); + return exec(cmd, env, dir); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized. Calls <code>exec(cmd, null, null)</code>. A security check + * is performed, <code>checkExec</code>. + * + * @param cmd the command to call + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or has null entries + * @throws IndexOutOfBoundsException if cmd is length 0 + */ + public Process exec(String[] cmd) throws IOException + { + return exec(cmd, null, null); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized, and specified environment. If the environment is null, the + * process inherits the environment of this process. Calls + * <code>exec(cmd, env, null)</code>. A security check is performed, + * <code>checkExec</code>. + * + * @param cmd the command to call + * @param env the environment to use, in the format name=value + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or cmd or env has null + * entries + * @throws IndexOutOfBoundsException if cmd is length 0 + */ + public Process exec(String[] cmd, String[] env) throws IOException + { + return exec(cmd, env, null); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized, and the specified environment and working directory. If the + * environment is null, the process inherits the environment of this + * process. If the directory is null, the process uses the current working + * directory. A security check is performed, <code>checkExec</code>. + * + * @param cmd the command to call + * @param env the environment to use, in the format name=value + * @param dir the working directory to use + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or cmd or env has null + * entries + * @throws IndexOutOfBoundsException if cmd is length 0 + * @since 1.3 + */ + public Process exec(String[] cmd, String[] env, File dir) + throws IOException + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExec(cmd[0]); + return VMRuntime.exec(cmd, env, dir); + } + + /** + * Returns the number of available processors currently available to the + * virtual machine. This number may change over time; so a multi-processor + * program want to poll this to determine maximal resource usage. + * + * @return the number of processors available, at least 1 + */ + public int availableProcessors() + { + return VMRuntime.availableProcessors(); + } + + /** + * Find out how much memory is still free for allocating Objects on the heap. + * + * @return the number of bytes of free memory for more Objects + */ + public long freeMemory() + { + return VMRuntime.freeMemory(); + } + + /** + * Find out how much memory total is available on the heap for allocating + * Objects. + * + * @return the total number of bytes of memory for Objects + */ + public long totalMemory() + { + return VMRuntime.totalMemory(); + } + + /** + * Returns the maximum amount of memory the virtual machine can attempt to + * use. This may be <code>Long.MAX_VALUE</code> if there is no inherent + * limit (or if you really do have a 8 exabyte memory!). + * + * @return the maximum number of bytes the virtual machine will attempt + * to allocate + */ + public long maxMemory() + { + return VMRuntime.maxMemory(); + } + + /** + * Run the garbage collector. This method is more of a suggestion than + * anything. All this method guarantees is that the garbage collector will + * have "done its best" by the time it returns. Notice that garbage + * collection takes place even without calling this method. + */ + public void gc() + { + VMRuntime.gc(); + } + + /** + * Run finalization on all Objects that are waiting to be finalized. Again, + * a suggestion, though a stronger one than {@link #gc()}. This calls the + * <code>finalize</code> method of all objects waiting to be collected. + * + * @see #finalize() + */ + public void runFinalization() + { + VMRuntime.runFinalization(); + } + + /** + * Tell the VM to trace every bytecode instruction that executes (print out + * a trace of it). No guarantees are made as to where it will be printed, + * and the VM is allowed to ignore this request. + * + * @param on whether to turn instruction tracing on + */ + public void traceInstructions(boolean on) + { + VMRuntime.traceInstructions(on); + } + + /** + * Tell the VM to trace every method call that executes (print out a trace + * of it). No guarantees are made as to where it will be printed, and the + * VM is allowed to ignore this request. + * + * @param on whether to turn method tracing on + */ + public void traceMethodCalls(boolean on) + { + VMRuntime.traceMethodCalls(on); + } + + /** + * Load a native library using the system-dependent filename. This is similar + * to loadLibrary, except the only name mangling done is inserting "_g" + * before the final ".so" if the VM was invoked by the name "java_g". There + * may be a security check, of <code>checkLink</code>. + * + * <p> + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param filename the file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + public void load(String filename) + { + load(filename, VMStackWalker.getCallingClassLoader()); + } + + /** + * Same as <code>load(String)</code> but using the given loader. + * + * @param filename the file to load + * @param loader class loader, or <code>null</code> for the boot loader + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + void load(String filename, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkLink(filename); + if (loadLib(filename, loader) == 0) + throw new UnsatisfiedLinkError("Could not load library " + filename); + } + + /** + * Do a security check on the filename and then load the native library. + * + * @param filename the file to load + * @param loader class loader, or <code>null</code> for the boot loader + * @return 0 on failure, nonzero on success + * @throws SecurityException if file read permission is denied + */ + private static int loadLib(String filename, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkRead(filename); + return VMRuntime.nativeLoad(filename, loader); + } + + /** + * Load a native library using a system-independent "short name" for the + * library. It will be transformed to a correct filename in a + * system-dependent manner (for example, in Windows, "mylib" will be turned + * into "mylib.dll"). This is done as follows: if the context that called + * load has a ClassLoader cl, then <code>cl.findLibrary(libpath)</code> is + * used to convert the name. If that result was null, or there was no class + * loader, this searches each directory of the system property + * <code>java.library.path</code> for a file named + * <code>System.mapLibraryName(libname)</code>. There may be a security + * check, of <code>checkLink</code>. + * + * <p>Note: Besides <code>java.library.path</code> a VM may chose to search + * for native libraries in a path that is specified by the + * <code>gnu.classpath.boot.library.path</code> system property. However + * this is for internal usage or development of GNU Classpath only. + * <b>A Java application must not load a non-system library by changing + * this property otherwise it will break compatibility.</b></p> + * + * <p> + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param libname the library to load + * + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + * + * @see System#mapLibraryName(String) + * @see ClassLoader#findLibrary(String) + */ + public void loadLibrary(String libname) + { + loadLibrary(libname, VMStackWalker.getCallingClassLoader()); + } + + /** + * Same as <code>loadLibrary(String)</code> but using the given loader. + * + * @param libname the library to load + * @param loader class loader, or <code>null</code> for the boot loader + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + void loadLibrary(String libname, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkLink(libname); + String filename; + if (loader != null && (filename = loader.findLibrary(libname)) != null) + { + if (loadLib(filename, loader) != 0) + return; + } + else + { + filename = VMRuntime.mapLibraryName(libname); + for (int i = 0; i < libpath.length; i++) + if (loadLib(libpath[i] + filename, loader) != 0) + return; + } + throw new UnsatisfiedLinkError("Native library `" + libname + + "' not found (as file `" + filename + "') in gnu.classpath.boot.library.path and java.library.path"); + } + + /** + * Return a localized version of this InputStream, meaning all characters + * are localized before they come out the other end. + * + * @param in the stream to localize + * @return the localized stream + * @deprecated <code>InputStreamReader</code> is the preferred way to read + * local encodings + * @XXX This implementation does not localize, yet. + */ + public InputStream getLocalizedInputStream(InputStream in) + { + return in; + } + + /** + * Return a localized version of this OutputStream, meaning all characters + * are localized before they are sent to the other end. + * + * @param out the stream to localize + * @return the localized stream + * @deprecated <code>OutputStreamWriter</code> is the preferred way to write + * local encodings + * @XXX This implementation does not localize, yet. + */ + public OutputStream getLocalizedOutputStream(OutputStream out) + { + return out; + } +} // class Runtime diff --git a/libjava/classpath/java/lang/RuntimeException.java b/libjava/classpath/java/lang/RuntimeException.java new file mode 100644 index 00000000000..72cf0872bc7 --- /dev/null +++ b/libjava/classpath/java/lang/RuntimeException.java @@ -0,0 +1,102 @@ +/* RuntimeException.java -- root of all unchecked exceptions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * All exceptions which are subclasses of <code>RuntimeException</code> + * can be thrown at any time during the execution of a Java virtual machine. + * Methods which throw these exceptions are not required to declare them + * in their throws clause. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class RuntimeException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7034897190745766939L; + + /** + * Create an exception without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public RuntimeException() + { + } + + /** + * Create an exception with a message. The cause remains uninitialized. + * + * @param s the message string + * @see #initCause(Throwable) + */ + public RuntimeException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message string + * @param cause the cause of this exception + * @since 1.4 + */ + public RuntimeException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an exception with the given cause, and a message of + * <code>cause == null ? null : cause.toString()</code>. + * + * @param cause the cause of this exception + * @since 1.4 + */ + public RuntimeException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/RuntimePermission.java b/libjava/classpath/java/lang/RuntimePermission.java new file mode 100644 index 00000000000..ca33307d12b --- /dev/null +++ b/libjava/classpath/java/lang/RuntimePermission.java @@ -0,0 +1,208 @@ +/* RuntimePermission.java -- permission for a secure runtime action + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.security.BasicPermission; + +/** + * A <code>RuntimePermission</code> contains a permission name, but no + * actions list. This means you either have the permission or you don't. + * + * Permission names have the follow the hierarchial property naming + * convention. In addition, an asterisk may appear at the end of a + * name if following a period or by itself. + * + * <table border=1> + * <tr><th>Valid names</th><th>Invalid names</th></tr> + * <tr><td>"accessClassInPackage.*","*"</td> + * <td>"**", "*x", "*.a"</td></tr> + * </table> + * <br> + * + * The following table provides a list of all the possible RuntimePermission + * permission names with a description of what that permission allows.<br> + * <table border=1> + * <tr><th>Permission Name</th><th>Permission Allows</th><th>Risks</th</tr> + * <tr> + * <td><code>createClassLoader</code></td> + * <td>creation of a class loader</td> + * <td>a class loader can load rogue classes which bypass all security + * permissions</td></tr> + * <tr> + * <td><code>getClassLoader</code></td> + * <td>retrieval of the class loader for the calling class</td> + * <td>rogue code could load classes not otherwise available</td></tr> + * <tr> + * <td><code>setContextClassLoader</code></td> + * <td>allows the setting of the context class loader used by a thread</td> + * <td>rogue code could change the context class loader needed by system + * threads</td></tr> + * <tr> + * <td><code>setSecurityManager</code></td> + * <td>allows the application to replace the security manager</td> + * <td>the new manager may be less restrictive, so that rogue code can + * bypass existing security checks</td></tr> + * <tr> + * <td><code>createSecurityManager</code></td> + * <td>allows the application to create a new security manager</td> + * <td>rogue code can use the new security manager to discover information + * about the execution stack</td></tr> + * <tr> + * <td><code>exitVM</code></td> + * <td>allows the application to halt the virtual machine</td> + * <td>rogue code can mount a denial-of-service attack by killing the + * virtual machine</td></tr> + * <tr> + * <td><code>shutdownHooks</code></td> + * <td>allows registration and modification of shutdown hooks</td> + * <td>rogue code can add a hook that interferes with clean + * virtual machine shutdown</td></tr> + * <tr> + * <td><code>setFactory</code></td> + * <td>allows the application to set the socket factory for socket, + * server socket, stream handler, or RMI socket factory.</td> + * <td>rogue code can create a rogue network object which mangles or + * intercepts data</td></tr> + * <tr> + * <td><code>setIO</code></td> + * <td>allows the application to set System.out, System.in, and + * System.err</td> + * <td>rogue code could sniff user input and intercept or mangle + * output</td></tr> + * <tr> + * <td><code>modifyThread</code></td> + * <td>allows the application to modify any thread in the virtual machine + * using any of the methods <code>stop</code>, <code>resume</code>, + * <code>suspend</code>, <code>setPriority</code>, and + * <code>setName</code> of classs <code>Thread</code></td> + * <td>rogue code could adversely modify system or user threads</td></tr> + * <tr> + * <td><code>stopThread</code></td> + * <td>allows the application to <code>stop</code> any thread it has + * access to in the system</td> + * <td>rogue code can stop arbitrary threads</td></tr> + * <tr> + * <td><code>modifyThreadGroup</code></td> + * <td>allows the application to modify thread groups using any of the + * methods <code>destroy</code>, <code>resume</code>, + * <code>setDaemon</code>, <code>setMaxPriority</code>, + * <code>stop</code>, and <code>suspend</code> of the class + * <code>ThreadGroup</code></td> + * <td>rogue code can mount a denial-of-service attack by changing run + * priorities</td></tr> + * <tr> + * <td><code>getProtectionDomain</code></td> + * <td>retrieve a class's ProtectionDomain</td> + * <td>rogue code can gain information about the security policy, to + * prepare a better attack</td></tr> + * <tr> + * <td><code>readFileDescriptor</code></td> + * <td>read a file descriptor</td> + * <td>rogue code can read sensitive information</td></tr> + * <tr> + * <td><code>writeFileDescriptor</code></td> + * <td>write a file descriptor</td> + * <td>rogue code can write files, including viruses, and can modify the + * virtual machine binary; if not just fill up the disk</td></tr> + * <tr> + * <td><code>loadLibrary.</code><em>library name</em></td> + * <td>dynamic linking of the named library</td> + * <td>native code can bypass many security checks of pure Java</td></tr> + * <tr> + * <td><code>accessClassInPackage.</code><em>package name</em></td> + * <td>access to a package via a ClassLoader</td> + * <td>rogue code can access classes not normally available</td></tr> + * <tr> + * <td><code>defineClassInPackage.</code><em>package name</em></td> + * <td>define a class inside a given package</td> + * <td>rogue code can install rogue classes, including in trusted packages + * like java.security or java.lang</td></tr> + * <tr> + * <td><code>accessDeclaredMembers</code></td> + * <td>access declared class members via reflection</td> + * <td>rogue code can discover information, invoke methods, or modify fields + * that are not otherwise available</td></tr> + * <tr> + * <td><code>queuePrintJob</code></td> + * <td>initiate a print job</td> + * <td>rogue code could make a hard copy of sensitive information, or + * simply waste paper</td></tr> + * </table> + * + * @author Brian Jones + * @author Eric Blake (ebb9@email.byu.edu) + * @see BasicPermission + * @see Permission + * @see SecurityManager + * @since 1.2 + * @status updated to 1.4 + */ +public final class RuntimePermission extends BasicPermission +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7399184964622342223L; + + /** + * Create a new permission with the specified name. + * + * @param permissionName the name of the granted permission + * @throws NullPointerException if name is null + * @throws IllegalArgumentException thrown if name is empty or invalid + */ + public RuntimePermission(String permissionName) + { + super(permissionName); + } + + /** + * Create a new permission with the specified name. The actions argument + * is ignored, as runtime permissions have no actions. + * + * @param permissionName the name of the granted permission + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException thrown if name is empty or invalid + */ + public RuntimePermission(String permissionName, String actions) + { + super(permissionName); + } +} diff --git a/libjava/classpath/java/lang/SecurityException.java b/libjava/classpath/java/lang/SecurityException.java new file mode 100644 index 00000000000..a95d797054f --- /dev/null +++ b/libjava/classpath/java/lang/SecurityException.java @@ -0,0 +1,74 @@ +/* SecurityException.java -- thrown to indicate a security violation + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The security manager will throw this exception to indicate a security + * violation. This can occur any time an operation is attempted which is + * deemed unsafe by the current security policies. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see SecurityManager + * @status updated to 1.4 + */ +public class SecurityException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6878364983674394167L; + + /** + * Create an exception without a message. + */ + public SecurityException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public SecurityException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/SecurityManager.java b/libjava/classpath/java/lang/SecurityManager.java new file mode 100644 index 00000000000..ef9e7597ddc --- /dev/null +++ b/libjava/classpath/java/lang/SecurityManager.java @@ -0,0 +1,1062 @@ +/* SecurityManager.java -- security checks for privileged actions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.VMStackWalker; + +import java.awt.AWTPermission; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FilePermission; +import java.lang.reflect.Member; +import java.net.InetAddress; +import java.net.SocketPermission; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.Permission; +import java.security.PrivilegedAction; +import java.security.Security; +import java.security.SecurityPermission; +import java.util.PropertyPermission; +import java.util.StringTokenizer; + +/** + * SecurityManager is a class you can extend to create your own Java + * security policy. By default, there is no SecurityManager installed in + * 1.1, which means that all things are permitted to all people. The security + * manager, if set, is consulted before doing anything with potentially + * dangerous results, and throws a <code>SecurityException</code> if the + * action is forbidden. + * + * <p>A typical check is as follows, just before the dangerous operation:<br> + * <pre> + * SecurityManager sm = System.getSecurityManager(); + * if (sm != null) + * sm.checkABC(<em>argument</em>, ...); + * </pre> + * Note that this is thread-safe, by caching the security manager in a local + * variable rather than risking a NullPointerException if the mangager is + * changed between the check for null and before the permission check. + * + * <p>The special method <code>checkPermission</code> is a catchall, and + * the default implementation calls + * <code>AccessController.checkPermission</code>. In fact, all the other + * methods default to calling checkPermission. + * + * <p>Sometimes, the security check needs to happen from a different context, + * such as when called from a worker thread. In such cases, use + * <code>getSecurityContext</code> to take a snapshot that can be passed + * to the worker thread:<br> + * <pre> + * Object context = null; + * SecurityManager sm = System.getSecurityManager(); + * if (sm != null) + * context = sm.getSecurityContext(); // defaults to an AccessControlContext + * // now, in worker thread + * if (sm != null) + * sm.checkPermission(permission, context); + * </pre> + * + * <p>Permissions fall into these categories: File, Socket, Net, Security, + * Runtime, Property, AWT, Reflect, and Serializable. Each of these + * permissions have a property naming convention, that follows a hierarchical + * naming convention, to make it easy to grant or deny several permissions + * at once. Some permissions also take a list of permitted actions, such + * as "read" or "write", to fine-tune control even more. The permission + * <code>java.security.AllPermission</code> grants all permissions. + * + * <p>The default methods in this class deny all things to all people. You + * must explicitly grant permission for anything you want to be legal when + * subclassing this class. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @see ClassLoader + * @see SecurityException + * @see #checkTopLevelWindow(Object) + * @see System#getSecurityManager() + * @see System#setSecurityManager(SecurityManager) + * @see AccessController + * @see AccessControlContext + * @see AccessControlException + * @see Permission + * @see BasicPermission + * @see java.io.FilePermission + * @see java.net.SocketPermission + * @see java.util.PropertyPermission + * @see RuntimePermission + * @see java.awt.AWTPermission + * @see Policy + * @see SecurityPermission + * @see ProtectionDomain + * @since 1.0 + * @status still missing 1.4 functionality + */ +public class SecurityManager +{ + /** + * The current security manager. This is located here instead of in + * System, to avoid security problems, as well as bootstrap issues. + * Make sure to access it in a thread-safe manner; it is package visible + * to avoid overhead in java.lang. + */ + static volatile SecurityManager current; + + /** + * Tells whether or not the SecurityManager is currently performing a + * security check. + * @deprecated Use {@link #checkPermission(Permission)} instead. + */ + protected boolean inCheck; + + /** + * Construct a new security manager. There may be a security check, of + * <code>RuntimePermission("createSecurityManager")</code>. + * + * @throws SecurityException if permission is denied + */ + public SecurityManager() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("createSecurityManager")); + } + + /** + * Tells whether or not the SecurityManager is currently performing a + * security check. + * + * @return true if the SecurityManager is in a security check + * @see #inCheck + * @deprecated use {@link #checkPermission(Permission)} instead + */ + public boolean getInCheck() + { + return inCheck; + } + + /** + * Get a list of all the classes currently executing methods on the Java + * stack. getClassContext()[0] is the currently executing method (ie. the + * class that CALLED getClassContext, not SecurityManager). + * + * @return an array of classes on the Java execution stack + */ + protected Class[] getClassContext() + { + Class[] stack1 = VMStackWalker.getClassContext(); + Class[] stack2 = new Class[stack1.length - 1]; + System.arraycopy(stack1, 1, stack2, 0, stack1.length - 1); + return stack2; + } + + /** + * Find the ClassLoader of the first non-system class on the execution + * stack. A non-system class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return null in three cases: + * + * <ul> + * <li>All methods on the stack are from system classes</li> + * <li>All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController.doPrivileged(PrivilegedAction)}, + * are from system classes</li> + * <li>A check of <code>java.security.AllPermission</code> succeeds.</li> + * </ul> + * + * @return the most recent non-system ClassLoader on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected ClassLoader currentClassLoader() + { + Class cl = currentLoadedClass(); + return cl != null ? cl.getClassLoader() : null; + } + + /** + * Find the first non-system class on the execution stack. A non-system + * class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return null in three cases: + * + * <ul> + * <li>All methods on the stack are from system classes</li> + * <li>All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController.doPrivileged(PrivilegedAction)}, + * are from system classes</li> + * <li>A check of <code>java.security.AllPermission</code> succeeds.</li> + * </ul> + * + * @return the most recent non-system Class on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected Class currentLoadedClass() + { + int i = classLoaderDepth(); + return i >= 0 ? getClassContext()[i] : null; + } + + /** + * Get the depth of a particular class on the execution stack. + * + * @param className the fully-qualified name to search for + * @return the index of the class on the stack, or -1 + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected int classDepth(String className) + { + Class[] c = getClassContext(); + for (int i = 0; i < c.length; i++) + if (className.equals(c[i].getName())) + return i; + return -1; + } + + /** + * Get the depth on the execution stack of the most recent non-system class. + * A non-system class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return -1 in three cases: + * + * <ul> + * <li>All methods on the stack are from system classes</li> + * <li>All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController.doPrivileged(PrivilegedAction)}, + * are from system classes</li> + * <li>A check of <code>java.security.AllPermission</code> succeeds.</li> + * </ul> + * + * @return the index of the most recent non-system Class on the stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected int classLoaderDepth() + { + try + { + checkPermission(new AllPermission()); + } + catch (SecurityException e) + { + Class[] c = getClassContext(); + for (int i = 0; i < c.length; i++) + if (c[i].getClassLoader() != null) + // XXX Check if c[i] is AccessController, or a system class. + return i; + } + return -1; + } + + /** + * Tell whether the specified class is on the execution stack. + * + * @param className the fully-qualified name of the class to find + * @return whether the specified class is on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected boolean inClass(String className) + { + return classDepth(className) != -1; + } + + /** + * Tell whether there is a class loaded with an explicit ClassLoader on + * the stack. + * + * @return whether a class with an explicit ClassLoader is on the stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected boolean inClassLoader() + { + return classLoaderDepth() != -1; + } + + /** + * Get an implementation-dependent Object that contains enough information + * about the current environment to be able to perform standard security + * checks later. This is used by trusted methods that need to verify that + * their callers have sufficient access to perform certain operations. + * + * <p>Currently the only methods that use this are checkRead() and + * checkConnect(). The default implementation returns an + * <code>AccessControlContext</code>. + * + * @return a security context + * @see #checkConnect(String, int, Object) + * @see #checkRead(String, Object) + * @see AccessControlContext + * @see AccessController#getContext() + */ + public Object getSecurityContext() + { + return AccessController.getContext(); + } + + /** + * Check if the current thread is allowed to perform an operation that + * requires the specified <code>Permission</code>. This defaults to + * <code>AccessController.checkPermission</code>. + * + * @param perm the <code>Permission</code> required + * @throws SecurityException if permission is denied + * @throws NullPointerException if perm is null + * @since 1.2 + */ + public void checkPermission(Permission perm) + { + AccessController.checkPermission(perm); + } + + /** + * Check if the current thread is allowed to perform an operation that + * requires the specified <code>Permission</code>. This is done in a + * context previously returned by <code>getSecurityContext()</code>. The + * default implementation expects context to be an AccessControlContext, + * and it calls <code>AccessControlContext.checkPermission(perm)</code>. + * + * @param perm the <code>Permission</code> required + * @param context a security context + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if perm is null + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + * @since 1.2 + */ + public void checkPermission(Permission perm, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + ((AccessControlContext) context).checkPermission(perm); + } + + /** + * Check if the current thread is allowed to create a ClassLoader. This + * method is called from ClassLoader.ClassLoader(), and checks + * <code>RuntimePermission("createClassLoader")</code>. If you override + * this, you should call <code>super.checkCreateClassLoader()</code> rather + * than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see ClassLoader#ClassLoader() + */ + public void checkCreateClassLoader() + { + checkPermission(new RuntimePermission("createClassLoader")); + } + + /** + * Check if the current thread is allowed to modify another Thread. This is + * called by Thread.stop(), suspend(), resume(), interrupt(), destroy(), + * setPriority(), setName(), and setDaemon(). The default implementation + * checks <code>RuntimePermission("modifyThread")</code> on system threads + * (ie. threads in ThreadGroup with a null parent), and returns silently on + * other threads. + * + * <p>If you override this, you must do two things. First, call + * <code>super.checkAccess(t)</code>, to make sure you are not relaxing + * requirements. Second, if the calling thread has + * <code>RuntimePermission("modifyThread")</code>, return silently, so that + * core classes (the Classpath library!) can modify any thread. + * + * @param thread the other Thread to check + * @throws SecurityException if permission is denied + * @throws NullPointerException if thread is null + * @see Thread#stop() + * @see Thread#suspend() + * @see Thread#resume() + * @see Thread#setPriority(int) + * @see Thread#setName(String) + * @see Thread#setDaemon(boolean) + */ + public void checkAccess(Thread thread) + { + if (thread.getThreadGroup() != null + && thread.getThreadGroup().getParent() != null) + checkPermission(new RuntimePermission("modifyThread")); + } + + /** + * Check if the current thread is allowed to modify a ThreadGroup. This is + * called by Thread.Thread() (to add a thread to the ThreadGroup), + * ThreadGroup.ThreadGroup() (to add this ThreadGroup to a parent), + * ThreadGroup.stop(), suspend(), resume(), interrupt(), destroy(), + * setDaemon(), and setMaxPriority(). The default implementation + * checks <code>RuntimePermission("modifyThread")</code> on the system group + * (ie. the one with a null parent), and returns silently on other groups. + * + * <p>If you override this, you must do two things. First, call + * <code>super.checkAccess(t)</code>, to make sure you are not relaxing + * requirements. Second, if the calling thread has + * <code>RuntimePermission("modifyThreadGroup")</code>, return silently, + * so that core classes (the Classpath library!) can modify any thread. + * + * @param g the ThreadGroup to check + * @throws SecurityException if permission is denied + * @throws NullPointerException if g is null + * @see Thread#Thread() + * @see ThreadGroup#ThreadGroup() + * @see ThreadGroup#stop() + * @see ThreadGroup#suspend() + * @see ThreadGroup#resume() + * @see ThreadGroup#interrupt() + * @see ThreadGroup#setDaemon(boolean) + * @see ThreadGroup#setMaxPriority(int) + */ + public void checkAccess(ThreadGroup g) + { + if (g.getParent() != null) + checkPermission(new RuntimePermission("modifyThreadGroup")); + } + + /** + * Check if the current thread is allowed to exit the JVM with the given + * status. This method is called from Runtime.exit() and Runtime.halt(). + * The default implementation checks + * <code>RuntimePermission("exitVM")</code>. If you override this, call + * <code>super.checkExit</code> rather than throwing an exception. + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @see Runtime#exit(int) + * @see Runtime#halt(int) + */ + public void checkExit(int status) + { + checkPermission(new RuntimePermission("exitVM")); + } + + /** + * Check if the current thread is allowed to execute the given program. This + * method is called from Runtime.exec(). If the name is an absolute path, + * the default implementation checks + * <code>FilePermission(program, "execute")</code>, otherwise it checks + * <code>FilePermission("<<ALL FILES>>", "execute")</code>. If + * you override this, call <code>super.checkExec</code> rather than + * throwing an exception. + * + * @param program the name of the program to exec + * @throws SecurityException if permission is denied + * @throws NullPointerException if program is null + * @see Runtime#exec(String[], String[], File) + */ + public void checkExec(String program) + { + if (! program.equals(new File(program).getAbsolutePath())) + program = "<<ALL FILES>>"; + checkPermission(new FilePermission(program, "execute")); + } + + /** + * Check if the current thread is allowed to link in the given native + * library. This method is called from Runtime.load() (and hence, by + * loadLibrary() as well). The default implementation checks + * <code>RuntimePermission("loadLibrary." + filename)</code>. If you + * override this, call <code>super.checkLink</code> rather than throwing + * an exception. + * + * @param filename the full name of the library to load + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see Runtime#load(String) + */ + public void checkLink(String filename) + { + // Use the toString() hack to do the null check. + checkPermission(new RuntimePermission("loadLibrary." + + filename.toString())); + } + + /** + * Check if the current thread is allowed to read the given file using the + * FileDescriptor. This method is called from + * FileInputStream.FileInputStream(). The default implementation checks + * <code>RuntimePermission("readFileDescriptor")</code>. If you override + * this, call <code>super.checkRead</code> rather than throwing an + * exception. + * + * @param desc the FileDescriptor representing the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if desc is null + * @see FileInputStream#FileInputStream(FileDescriptor) + */ + public void checkRead(FileDescriptor desc) + { + if (desc == null) + throw new NullPointerException(); + checkPermission(new RuntimePermission("readFileDescriptor")); + } + + /** + * Check if the current thread is allowed to read the given file. This + * method is called from FileInputStream.FileInputStream(), + * RandomAccessFile.RandomAccessFile(), File.exists(), canRead(), isFile(), + * isDirectory(), lastModified(), length() and list(). The default + * implementation checks <code>FilePermission(filename, "read")</code>. If + * you override this, call <code>super.checkRead</code> rather than + * throwing an exception. + * + * @param filename the full name of the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File + * @see FileInputStream#FileInputStream(String) + * @see RandomAccessFile#RandomAccessFile(String) + */ + public void checkRead(String filename) + { + checkPermission(new FilePermission(filename, "read")); + } + + /** + * Check if the current thread is allowed to read the given file. using the + * given security context. The context must be a result of a previous call + * to <code>getSecurityContext()</code>. The default implementation checks + * <code>AccessControlContext.checkPermission(new FilePermission(filename, + * "read"))</code>. If you override this, call <code>super.checkRead</code> + * rather than throwing an exception. + * + * @param filename the full name of the file to access + * @param context the context to determine access for + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if filename is null + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + */ + public void checkRead(String filename, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + AccessControlContext ac = (AccessControlContext) context; + ac.checkPermission(new FilePermission(filename, "read")); + } + + /** + * Check if the current thread is allowed to write the given file using the + * FileDescriptor. This method is called from + * FileOutputStream.FileOutputStream(). The default implementation checks + * <code>RuntimePermission("writeFileDescriptor")</code>. If you override + * this, call <code>super.checkWrite</code> rather than throwing an + * exception. + * + * @param desc the FileDescriptor representing the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if desc is null + * @see FileOutputStream#FileOutputStream(FileDescriptor) + */ + public void checkWrite(FileDescriptor desc) + { + if (desc == null) + throw new NullPointerException(); + checkPermission(new RuntimePermission("writeFileDescriptor")); + } + + /** + * Check if the current thread is allowed to write the given file. This + * method is called from FileOutputStream.FileOutputStream(), + * RandomAccessFile.RandomAccessFile(), File.canWrite(), mkdir(), and + * renameTo(). The default implementation checks + * <code>FilePermission(filename, "write")</code>. If you override this, + * call <code>super.checkWrite</code> rather than throwing an exception. + * + * @param filename the full name of the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File + * @see File#canWrite() + * @see File#mkdir() + * @see File#renameTo() + * @see FileOutputStream#FileOutputStream(String) + * @see RandomAccessFile#RandomAccessFile(String) + */ + public void checkWrite(String filename) + { + checkPermission(new FilePermission(filename, "write")); + } + + /** + * Check if the current thread is allowed to delete the given file. This + * method is called from File.delete(). The default implementation checks + * <code>FilePermission(filename, "delete")</code>. If you override this, + * call <code>super.checkDelete</code> rather than throwing an exception. + * + * @param filename the full name of the file to delete + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File#delete() + */ + public void checkDelete(String filename) + { + checkPermission(new FilePermission(filename, "delete")); + } + + /** + * Check if the current thread is allowed to connect to a given host on a + * given port. This method is called from Socket.Socket(). A port number + * of -1 indicates the caller is attempting to determine an IP address, so + * the default implementation checks + * <code>SocketPermission(host, "resolve")</code>. Otherwise, the default + * implementation checks + * <code>SocketPermission(host + ":" + port, "connect")</code>. If you + * override this, call <code>super.checkConnect</code> rather than throwing + * an exception. + * + * @param host the host to connect to + * @param port the port to connect on + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @see Socket#Socket() + */ + public void checkConnect(String host, int port) + { + if (port == -1) + checkPermission(new SocketPermission(host, "resolve")); + else + // Use the toString() hack to do the null check. + checkPermission(new SocketPermission(host.toString() + ":" + port, + "connect")); + } + + /** + * Check if the current thread is allowed to connect to a given host on a + * given port, using the given security context. The context must be a + * result of a previous call to <code>getSecurityContext</code>. A port + * number of -1 indicates the caller is attempting to determine an IP + * address, so the default implementation checks + * <code>AccessControlContext.checkPermission(new SocketPermission(host, + * "resolve"))</code>. Otherwise, the default implementation checks + * <code>AccessControlContext.checkPermission(new SocketPermission(host + * + ":" + port, "connect"))</code>. If you override this, call + * <code>super.checkConnect</code> rather than throwing an exception. + * + * @param host the host to connect to + * @param port the port to connect on + * @param context the context to determine access for + * + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if host is null + * + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + */ + public void checkConnect(String host, int port, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + AccessControlContext ac = (AccessControlContext) context; + if (port == -1) + ac.checkPermission(new SocketPermission(host, "resolve")); + else + // Use the toString() hack to do the null check. + ac.checkPermission(new SocketPermission(host.toString() + ":" + port, + "connect")); + } + + /** + * Check if the current thread is allowed to listen to a specific port for + * data. This method is called by ServerSocket.ServerSocket(). The default + * implementation checks + * <code>SocketPermission("localhost:" + (port == 0 ? "1024-" : "" + port), + * "listen")</code>. If you override this, call + * <code>super.checkListen</code> rather than throwing an exception. + * + * @param port the port to listen on + * @throws SecurityException if permission is denied + * @see ServerSocket#ServerSocket(int) + */ + public void checkListen(int port) + { + checkPermission(new SocketPermission("localhost:" + + (port == 0 ? "1024-" : "" +port), + "listen")); + } + + /** + * Check if the current thread is allowed to accept a connection from a + * particular host on a particular port. This method is called by + * ServerSocket.implAccept(). The default implementation checks + * <code>SocketPermission(host + ":" + port, "accept")</code>. If you + * override this, call <code>super.checkAccept</code> rather than throwing + * an exception. + * + * @param host the host which wishes to connect + * @param port the port the connection will be on + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @see ServerSocket#accept() + */ + public void checkAccept(String host, int port) + { + // Use the toString() hack to do the null check. + checkPermission(new SocketPermission(host.toString() + ":" + port, + "accept")); + } + + /** + * Check if the current thread is allowed to read and write multicast to + * a particular address. The default implementation checks + * <code>SocketPermission(addr.getHostAddress(), "accept,connect")</code>. + * If you override this, call <code>super.checkMulticast</code> rather than + * throwing an exception. + * + * @param addr the address to multicast to + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @since 1.1 + */ + public void checkMulticast(InetAddress addr) + { + checkPermission(new SocketPermission(addr.getHostAddress(), + "accept,connect")); + } + + /** + *Check if the current thread is allowed to read and write multicast to + * a particular address with a particular ttl (time-to-live) value. The + * default implementation ignores ttl, and checks + * <code>SocketPermission(addr.getHostAddress(), "accept,connect")</code>. + * If you override this, call <code>super.checkMulticast</code> rather than + * throwing an exception. + * + * @param addr the address to multicast to + * @param ttl value in use for multicast send + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @since 1.1 + * @deprecated use {@link #checkPermission(Permission)} instead + */ + public void checkMulticast(InetAddress addr, byte ttl) + { + checkPermission(new SocketPermission(addr.getHostAddress(), + "accept,connect")); + } + + /** + * Check if the current thread is allowed to read or write all the system + * properties at once. This method is called by System.getProperties() + * and setProperties(). The default implementation checks + * <code>PropertyPermission("*", "read,write")</code>. If you override + * this, call <code>super.checkPropertiesAccess</code> rather than + * throwing an exception. + * + * @throws SecurityException if permission is denied + * @see System#getProperties() + * @see System#setProperties(Properties) + */ + public void checkPropertiesAccess() + { + checkPermission(new PropertyPermission("*", "read,write")); + } + + /** + * Check if the current thread is allowed to read a particular system + * property (writes are checked directly via checkPermission). This method + * is called by System.getProperty() and setProperty(). The default + * implementation checks <code>PropertyPermission(key, "read")</code>. If + * you override this, call <code>super.checkPropertyAccess</code> rather + * than throwing an exception. + * + * @param key the key of the property to check + * + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + * + * @see System#getProperty(String) + */ + public void checkPropertyAccess(String key) + { + checkPermission(new PropertyPermission(key, "read")); + } + + /** + * Check if the current thread is allowed to create a top-level window. If + * it is not, the operation should still go through, but some sort of + * nonremovable warning should be placed on the window to show that it + * is untrusted. This method is called by Window.Window(). The default + * implementation checks + * <code>AWTPermission("showWindowWithoutWarningBanner")</code>, and returns + * true if no exception was thrown. If you override this, use + * <code>return super.checkTopLevelWindow</code> rather than returning + * false. + * + * @param window the window to create + * @return true if there is permission to show the window without warning + * @throws NullPointerException if window is null + * @see Window#Window(Frame) + */ + public boolean checkTopLevelWindow(Object window) + { + if (window == null) + throw new NullPointerException(); + try + { + checkPermission(new AWTPermission("showWindowWithoutWarningBanner")); + return true; + } + catch (SecurityException e) + { + return false; + } + } + + /** + * Check if the current thread is allowed to create a print job. This + * method is called by Toolkit.getPrintJob(). The default implementation + * checks <code>RuntimePermission("queuePrintJob")</code>. If you override + * this, call <code>super.checkPrintJobAccess</code> rather than throwing + * an exception. + * + * @throws SecurityException if permission is denied + * @see Toolkit#getPrintJob(Frame, String, Properties) + * @since 1.1 + */ + public void checkPrintJobAccess() + { + checkPermission(new RuntimePermission("queuePrintJob")); + } + + /** + * Check if the current thread is allowed to use the system clipboard. This + * method is called by Toolkit.getSystemClipboard(). The default + * implementation checks <code>AWTPermission("accessClipboard")</code>. If + * you override this, call <code>super.checkSystemClipboardAccess</code> + * rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see Toolkit#getSystemClipboard() + * @since 1.1 + */ + public void checkSystemClipboardAccess() + { + checkPermission(new AWTPermission("accessClipboard")); + } + + /** + * Check if the current thread is allowed to use the AWT event queue. This + * method is called by Toolkit.getSystemEventQueue(). The default + * implementation checks <code>AWTPermission("accessEventQueue")</code>. + * you override this, call <code>super.checkAwtEventQueueAccess</code> + * rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see Toolkit#getSystemEventQueue() + * @since 1.1 + */ + public void checkAwtEventQueueAccess() + { + checkPermission(new AWTPermission("accessEventQueue")); + } + + /** + * Check if the current thread is allowed to access the specified package + * at all. This method is called by ClassLoader.loadClass() in user-created + * ClassLoaders. The default implementation gets a list of all restricted + * packages, via <code>Security.getProperty("package.access")</code>. Then, + * if packageName starts with or equals any restricted package, it checks + * <code>RuntimePermission("accessClassInPackage." + packageName)</code>. + * If you override this, you should call + * <code>super.checkPackageAccess</code> before doing anything else. + * + * @param packageName the package name to check access to + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see ClassLoader#loadClass(String, boolean) + * @see Security#getProperty(String) + */ + public void checkPackageAccess(String packageName) + { + checkPackageList(packageName, "package.access", "accessClassInPackage."); + } + + /** + * Check if the current thread is allowed to define a class into the + * specified package. This method is called by ClassLoader.loadClass() in + * user-created ClassLoaders. The default implementation gets a list of all + * restricted packages, via + * <code>Security.getProperty("package.definition")</code>. Then, if + * packageName starts with or equals any restricted package, it checks + * <code>RuntimePermission("defineClassInPackage." + packageName)</code>. + * If you override this, you should call + * <code>super.checkPackageDefinition</code> before doing anything else. + * + * @param packageName the package name to check access to + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see ClassLoader#loadClass(String, boolean) + * @see Security#getProperty(String) + */ + public void checkPackageDefinition(String packageName) + { + checkPackageList(packageName, "package.definition", "defineClassInPackage."); + } + + /** + * Check if the current thread is allowed to set the current socket factory. + * This method is called by Socket.setSocketImplFactory(), + * ServerSocket.setSocketFactory(), and URL.setURLStreamHandlerFactory(). + * The default implementation checks + * <code>RuntimePermission("setFactory")</code>. If you override this, call + * <code>super.checkSetFactory</code> rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see Socket#setSocketImplFactory(SocketImplFactory) + * @see ServerSocket#setSocketFactory(SocketImplFactory) + * @see URL#setURLStreamHandlerFactory(URLStreamHandlerFactory) + */ + public void checkSetFactory() + { + checkPermission(new RuntimePermission("setFactory")); + } + + /** + * Check if the current thread is allowed to get certain types of Methods, + * Fields and Constructors from a Class object. This method is called by + * Class.getMethod[s](), Class.getField[s](), Class.getConstructor[s], + * Class.getDeclaredMethod[s](), Class.getDeclaredField[s](), and + * Class.getDeclaredConstructor[s](). The default implementation allows + * PUBLIC access, and access to classes defined by the same classloader as + * the code performing the reflection. Otherwise, it checks + * <code>RuntimePermission("accessDeclaredMembers")</code>. If you override + * this, do not call <code>super.checkMemberAccess</code>, as this would + * mess up the stack depth check that determines the ClassLoader requesting + * the access. + * + * @param c the Class to check + * @param memberType either DECLARED or PUBLIC + * @throws SecurityException if permission is denied, including when + * memberType is not DECLARED or PUBLIC + * @throws NullPointerException if c is null + * @see Class + * @see Member#DECLARED + * @see Member#PUBLIC + * @since 1.1 + */ + public void checkMemberAccess(Class c, int memberType) + { + if (c == null) + throw new NullPointerException(); + if (memberType == Member.PUBLIC) + return; + // XXX Allow access to classes created by same classloader before next + // check. + checkPermission(new RuntimePermission("accessDeclaredMembers")); + } + + /** + * Test whether a particular security action may be taken. The default + * implementation checks <code>SecurityPermission(action)</code>. If you + * override this, call <code>super.checkSecurityAccess</code> rather than + * throwing an exception. + * + * @param action the desired action to take + * @throws SecurityException if permission is denied + * @throws NullPointerException if action is null + * @throws IllegalArgumentException if action is "" + * @since 1.1 + */ + public void checkSecurityAccess(String action) + { + checkPermission(new SecurityPermission(action)); + } + + /** + * Get the ThreadGroup that a new Thread should belong to by default. Called + * by Thread.Thread(). The default implementation returns the current + * ThreadGroup of the current Thread. <STRONG>Spec Note:</STRONG> it is not + * clear whether the new Thread is guaranteed to pass the + * checkAccessThreadGroup() test when using this ThreadGroup, but I presume + * so. + * + * @return the ThreadGroup to put the new Thread into + * @since 1.1 + */ + public ThreadGroup getThreadGroup() + { + return Thread.currentThread().getThreadGroup(); + } + + /** + * Helper that checks a comma-separated list of restricted packages, from + * <code>Security.getProperty("package.definition")</code>, for the given + * package access permission. If packageName starts with or equals any + * restricted package, it checks + * <code>RuntimePermission(permission + packageName)</code>. + * + * @param packageName the package name to check access to + * @param restriction "package.access" or "package.definition" + * @param permission the base permission, including the '.' + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see #checkPackageAccess(String) + * @see #checkPackageDefinition(String) + */ + void checkPackageList(String packageName, final String restriction, + String permission) + { + if (packageName == null) + throw new NullPointerException(); + + String list = (String)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return Security.getProperty(restriction); + } + }); + + if (list == null || list.equals("")) + return; + + String packageNamePlusDot = packageName + "."; + + StringTokenizer st = new StringTokenizer(list, ","); + while (st.hasMoreTokens()) + { + if (packageNamePlusDot.startsWith(st.nextToken())) + { + Permission p = new RuntimePermission(permission + packageName); + checkPermission(p); + return; + } + } + } +} diff --git a/libjava/classpath/java/lang/Short.java b/libjava/classpath/java/lang/Short.java new file mode 100644 index 00000000000..fbeea915bd3 --- /dev/null +++ b/libjava/classpath/java/lang/Short.java @@ -0,0 +1,353 @@ +/* Short.java -- object wrapper for short + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class <code>Short</code> represent primitive + * <code>short</code> values. + * + * Additionally, this class provides various helper functions and variables + * related to shorts. + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public final class Short extends Number implements Comparable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7515723908773894738L; + + /** + * The minimum value a <code>short</code> can represent is -32768 (or + * -2<sup>15</sup>). + */ + public static final short MIN_VALUE = -32768; + + /** + * The minimum value a <code>short</code> can represent is 32767 (or + * 2<sup>15</sup>). + */ + public static final short MAX_VALUE = 32767; + + /** + * The primitive type <code>short</code> is represented by this + * <code>Class</code> object. + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('S'); + + /** + * The immutable value of this Short. + * + * @serial the wrapped short + */ + private final short value; + + /** + * Create a <code>Short</code> object representing the value of the + * <code>short</code> argument. + * + * @param value the value to use + */ + public Short(short value) + { + this.value = value; + } + + /** + * Create a <code>Short</code> object representing the value of the + * argument after conversion to a <code>short</code>. + * + * @param s the string to convert + * @throws NumberFormatException if the String cannot be parsed + */ + public Short(String s) + { + value = parseShort(s, 10); + } + + /** + * Converts the <code>short</code> to a <code>String</code> and assumes + * a radix of 10. + * + * @param s the <code>short</code> to convert to <code>String</code> + * @return the <code>String</code> representation of the argument + */ + public static String toString(short s) + { + return String.valueOf(s); + } + + /** + * Converts the specified <code>String</code> into a <code>short</code>. + * This function assumes a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the <code>short</code> value of <code>s</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>short</code> + */ + public static short parseShort(String s) + { + return parseShort(s, 10); + } + + /** + * Converts the specified <code>String</code> into a <code>short</code> + * using the specified radix (base). The string must not be <code>null</code> + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * <code>Character.digit(d, radix)</code>, and must be in the range + * <code>0</code> to <code>radix - 1</code>. Finally, the result must be + * within <code>MIN_VALUE</code> to <code>MAX_VALUE</code>, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to use in the conversion + * @return the <code>String</code> argument converted to <code>short</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>short</code> + */ + public static short parseShort(String s, int radix) + { + int i = Integer.parseInt(s, radix, false); + if ((short) i != i) + throw new NumberFormatException(); + return (short) i; + } + + /** + * Creates a new <code>Short</code> object using the <code>String</code> + * and specified radix (base). + * + * @param s the <code>String</code> to convert + * @param radix the radix (base) to convert with + * @return the new <code>Short</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>short</code> + * @see #parseShort(String, int) + */ + public static Short valueOf(String s, int radix) + { + return new Short(parseShort(s, radix)); + } + + /** + * Creates a new <code>Short</code> object using the <code>String</code>, + * assuming a radix of 10. + * + * @param s the <code>String</code> to convert + * @return the new <code>Short</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>short</code> + * @see #Short(String) + * @see #parseShort(String) + */ + public static Short valueOf(String s) + { + return new Short(parseShort(s, 10)); + } + + /** + * Convert the specified <code>String</code> into a <code>Short</code>. + * The <code>String</code> may represent decimal, hexadecimal, or + * octal numbers. + * + * <p>The extended BNF grammar is as follows:<br> + * <pre> + * <em>DecodableString</em>: + * ( [ <code>-</code> ] <em>DecimalNumber</em> ) + * | ( [ <code>-</code> ] ( <code>0x</code> | <code>0X</code> + * | <code>#</code> ) <em>HexDigit</em> { <em>HexDigit</em> } ) + * | ( [ <code>-</code> ] <code>0</code> { <em>OctalDigit</em> } ) + * <em>DecimalNumber</em>: + * <em>DecimalDigit except '0'</em> { <em>DecimalDigit</em> } + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 10) has value 0 to 9</em> + * <em>OctalDigit</em>: + * <em>Character.digit(d, 8) has value 0 to 7</em> + * <em>DecimalDigit</em>: + * <em>Character.digit(d, 16) has value 0 to 15</em> + * </pre> + * Finally, the value must be in the range <code>MIN_VALUE</code> to + * <code>MAX_VALUE</code>, or an exception is thrown. + * + * @param s the <code>String</code> to interpret + * @return the value of the String as a <code>Short</code> + * @throws NumberFormatException if <code>s</code> cannot be parsed as a + * <code>short</code> + * @throws NullPointerException if <code>s</code> is null + * @see Integer#decode(String) + */ + public static Short decode(String s) + { + int i = Integer.parseInt(s, 10, true); + if ((short) i != i) + throw new NumberFormatException(); + return new Short((short) i); + } + + /** + * Return the value of this <code>Short</code> as a <code>byte</code>. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this <code>Short</code>. + * + * @return the short value + */ + public short shortValue() + { + return value; + } + + /** + * Return the value of this <code>Short</code> as an <code>int</code>. + * + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this <code>Short</code> as a <code>long</code>. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this <code>Short</code> as a <code>float</code>. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this <code>Short</code> as a <code>double</code>. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the <code>Short</code> value to a <code>String</code> and + * assumes a radix of 10. + * + * @return the <code>String</code> representation of this <code>Short</code> + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. <code>Short</code>'s hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns <code>true</code> if <code>obj</code> is an instance of + * <code>Short</code> and represents the same short value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Short && value == ((Short) obj).value; + } + + /** + * Compare two Shorts numerically by comparing their <code>short</code> + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param s the Short to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Short s) + { + return value - s.value; + } + + /** + * Behaves like <code>compareTo(Short)</code> unless the Object + * is not a <code>Short</code>. + * + * @param o the object to compare + * @return the comparison + * @throws ClassCastException if the argument is not a <code>Short</code> + * @see #compareTo(Short) + * @see Comparable + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((Short)o); + } +} diff --git a/libjava/classpath/java/lang/StackOverflowError.java b/libjava/classpath/java/lang/StackOverflowError.java new file mode 100644 index 00000000000..5188ddda159 --- /dev/null +++ b/libjava/classpath/java/lang/StackOverflowError.java @@ -0,0 +1,72 @@ +/* StackOverflowError.java -- thrown when the stack depth is exceeded + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>StackOverflowError</code> is thrown when the execution stack + * overflow occurs. This often occurs when a method enters infinit recursion. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class StackOverflowError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8609175038441759607L; + + /** + * Create an error without a message. + */ + public StackOverflowError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public StackOverflowError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/StackTraceElement.java b/libjava/classpath/java/lang/StackTraceElement.java new file mode 100644 index 00000000000..6dd4d8532e8 --- /dev/null +++ b/libjava/classpath/java/lang/StackTraceElement.java @@ -0,0 +1,259 @@ +/* StackTraceElement.java -- One function call or call stack element + Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * One function call or stack trace element. Gives information about + * the execution point such as the source file name, the line number, + * the fully qualified class name, the method name and whether this method + * is native, if this information is known. + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public final class StackTraceElement implements Serializable +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6992337162326171013L; + + /** + * The name of the file, null if unknown. + * + * @serial the source code filename, if known + */ + private final String fileName; + + /** + * The line number in the file, negative if unknown. + * + * @serial the source code line number, if known + */ + private final int lineNumber; + + /** + * The fully qualified class name, null if unknown. + * + * @serial the enclosing class, if known + */ + private final String declaringClass; + + /** + * The method name in the class, null if unknown. + * + * @serial the enclosing method, if known + */ + private final String methodName; + + /** Whether the method is native. */ + private final transient boolean isNative; + + /** + * A package local constructor for the StackTraceElement class, to be + * called by the Virtual Machine as part of Throwable.fillInStackTrace. + * There are no public constructors defined for this class. Creation + * of new elements is implementation specific. + * + * @param fileName the name of the file, null if unknown + * @param lineNumber the line in the file, negative if unknown + * @param className the fully qualified name of the class, null if unknown + * @param methodName the name of the method, null if unknown + * @param isNative true if native, false otherwise + */ + StackTraceElement(String fileName, int lineNumber, String className, + String methodName, boolean isNative) + { + this.fileName = fileName; + this.lineNumber = lineNumber; + this.declaringClass = className; + this.methodName = methodName; + this.isNative = isNative; + } + + /** + * Returns the name of the file, or null if unknown. This is usually + * obtained from the <code>SourceFile</code> attribute of the class file + * format, if present. + * + * @return the file name + */ + public String getFileName() + { + return fileName; + } + + /** + * Returns the line number in the file, or a negative number if unknown. + * This is usually obtained from the <code>LineNumberTable</code> attribute + * of the method in the class file format, if present. + * + * @return the line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * Returns the fully qualified class name, or null if unknown. + * + * @return the class name + */ + public String getClassName() + { + return declaringClass; + } + + /** + * Returns the method name in the class, or null if unknown. If the + * execution point is in a constructor, the name is + * <code><init></code>; if the execution point is in the class + * initializer, the name is <code><clinit></code>. + * + * @return the method name + */ + public String getMethodName() + { + return methodName; + } + + /** + * Returns true if the method is native, or false if it is not or unknown. + * + * @return whether the method is native + */ + public boolean isNativeMethod() + { + return isNative; + } + + /** + * Returns a string representation of this stack trace element. The + * returned String is implementation specific. This implementation + * returns the following String: "[class][.][method]([file][:line])". + * If the fully qualified class name or the method is unknown it is + * omitted including the point seperator. If the source file name is + * unknown it is replaced by "Unknown Source" if the method is not native + * or by "Native Method" if the method is native. If the line number + * is unknown it and the colon are omitted. + * + * @return a string representation of this execution point + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + if (declaringClass != null) + { + sb.append(declaringClass); + if (methodName != null) + sb.append('.'); + } + if (methodName != null) + sb.append(methodName); + sb.append(" ("); + if (fileName != null) + sb.append(fileName); + else + sb.append(isNative ? "Native Method" : "Unknown Source"); + if (lineNumber >= 0) + sb.append(':').append(lineNumber); + sb.append(')'); + return sb.toString(); + } + + /** + * Returns true if the given object is also a StackTraceElement and all + * attributes, except the native flag, are equal (either the same attribute + * between the two elments are null, or both satisfy Object.equals). + * + * @param o the object to compare + * @return true if the two are equal + */ + public boolean equals(Object o) + { + if (! (o instanceof StackTraceElement)) + return false; + StackTraceElement e = (StackTraceElement) o; + return equals(fileName, e.fileName) + && lineNumber == e.lineNumber + && equals(declaringClass, e.declaringClass) + && equals(methodName, e.methodName); + } + + /** + * Returns the hashCode of this StackTraceElement. This implementation + * computes the hashcode by xor-ing the hashcode of all attributes except + * the native flag. + * + * @return the hashcode + */ + public int hashCode() + { + return hashCode(fileName) ^ lineNumber ^ hashCode(declaringClass) + ^ hashCode(methodName); + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == null ? o2 == null : o1.equals(o2) + */ + private static boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + private static int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } +} diff --git a/libjava/classpath/java/lang/StrictMath.java b/libjava/classpath/java/lang/StrictMath.java new file mode 100644 index 00000000000..32bd3540d80 --- /dev/null +++ b/libjava/classpath/java/lang/StrictMath.java @@ -0,0 +1,1844 @@ +/* java.lang.StrictMath -- common mathematical functions, strict Java + Copyright (C) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/* + * Some of the algorithms in this class are in the public domain, as part + * of fdlibm (freely-distributable math library), available at + * http://www.netlib.org/fdlibm/, and carry the following copyright: + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +package java.lang; + +import gnu.classpath.Configuration; + +import java.util.Random; + +/** + * Helper class containing useful mathematical functions and constants. + * This class mirrors {@link Math}, but is 100% portable, because it uses + * no native methods whatsoever. Also, these algorithms are all accurate + * to less than 1 ulp, and execute in <code>strictfp</code> mode, while + * Math is allowed to vary in its results for some functions. Unfortunately, + * this usually means StrictMath has less efficiency and speed, as Math can + * use native methods. + * + * <p>The source of the various algorithms used is the fdlibm library, at:<br> + * <a href="http://www.netlib.org/fdlibm/">http://www.netlib.org/fdlibm/</a> + * + * Note that angles are specified in radians. Conversion functions are + * provided for your convenience. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ +public final strictfp class StrictMath +{ + /** + * StrictMath is non-instantiable. + */ + private StrictMath() + { + } + + /** + * A random number generator, initialized on first use. + * + * @see #random() + */ + private static Random rand; + + /** + * The most accurate approximation to the mathematical constant <em>e</em>: + * <code>2.718281828459045</code>. Used in natural log and exp. + * + * @see #log(double) + * @see #exp(double) + */ + public static final double E + = 2.718281828459045; // Long bits 0x4005bf0z8b145769L. + + /** + * The most accurate approximation to the mathematical constant <em>pi</em>: + * <code>3.141592653589793</code>. This is the ratio of a circle's diameter + * to its circumference. + */ + public static final double PI + = 3.141592653589793; // Long bits 0x400921fb54442d18L. + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * <p>Note that the the largest negative value (Integer.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a <em>negative</em> value. You have been warned. + * + * @param i the number to take the absolute value of + * @return the absolute value + * @see Integer#MIN_VALUE + */ + public static int abs(int i) + { + return (i < 0) ? -i : i; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * <p>Note that the the largest negative value (Long.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a <em>negative</em> value. You have been warned. + * + * @param l the number to take the absolute value of + * @return the absolute value + * @see Long#MIN_VALUE + */ + public static long abs(long l) + { + return (l < 0) ? -l : l; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * @param f the number to take the absolute value of + * @return the absolute value + */ + public static float abs(float f) + { + return (f <= 0) ? 0 - f : f; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * @param d the number to take the absolute value of + * @return the absolute value + */ + public static double abs(double d) + { + return (d <= 0) ? 0 - d : d; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static int min(int a, int b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static long min(long a, long b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static float min(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static double min(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static int max(int a, int b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static long max(long a, long b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static float max(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static double max(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * The trigonometric function <em>sin</em>. The sine of NaN or infinity is + * NaN, and the sine of 0 retains its sign. + * + * @param a the angle (in radians) + * @return sin(a) + */ + public static double sin(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return sin(a, 0); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + switch (n & 3) + { + case 0: + return sin(y[0], y[1]); + case 1: + return cos(y[0], y[1]); + case 2: + return -sin(y[0], y[1]); + default: + return -cos(y[0], y[1]); + } + } + + /** + * The trigonometric function <em>cos</em>. The cosine of NaN or infinity is + * NaN. + * + * @param a the angle (in radians). + * @return cos(a). + */ + public static double cos(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return cos(a, 0); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + switch (n & 3) + { + case 0: + return cos(y[0], y[1]); + case 1: + return -sin(y[0], y[1]); + case 2: + return -cos(y[0], y[1]); + default: + return sin(y[0], y[1]); + } + } + + /** + * The trigonometric function <em>tan</em>. The tangent of NaN or infinity + * is NaN, and the tangent of 0 retains its sign. + * + * @param a the angle (in radians) + * @return tan(a) + */ + public static double tan(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return tan(a, 0, false); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + return tan(y[0], y[1], (n & 1) == 1); + } + + /** + * The trigonometric function <em>arcsin</em>. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN; and the arcsine of + * 0 retains its sign. + * + * @param x the sin to turn back into an angle + * @return arcsin(x) + */ + public static double asin(double x) + { + boolean negative = x < 0; + if (negative) + x = -x; + if (! (x <= 1)) + return Double.NaN; + if (x == 1) + return negative ? -PI / 2 : PI / 2; + if (x < 0.5) + { + if (x < 1 / TWO_27) + return negative ? -x : x; + double t = x * x; + double p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t + * (PS4 + t * PS5))))); + double q = 1 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4))); + return negative ? -x - x * (p / q) : x + x * (p / q); + } + double w = 1 - x; // 1>|x|>=0.5. + double t = w * 0.5; + double p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t + * (PS4 + t * PS5))))); + double q = 1 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4))); + double s = sqrt(t); + if (x >= 0.975) + { + w = p / q; + t = PI / 2 - (2 * (s + s * w) - PI_L / 2); + } + else + { + w = (float) s; + double c = (t - w * w) / (s + w); + p = 2 * s * (p / q) - (PI_L / 2 - 2 * c); + q = PI / 4 - 2 * w; + t = PI / 4 - (p - q); + } + return negative ? -t : t; + } + + /** + * The trigonometric function <em>arccos</em>. The range of angles returned + * is 0 to pi radians (0 to 180 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN. + * + * @param x the cos to turn back into an angle + * @return arccos(x) + */ + public static double acos(double x) + { + boolean negative = x < 0; + if (negative) + x = -x; + if (! (x <= 1)) + return Double.NaN; + if (x == 1) + return negative ? PI : 0; + if (x < 0.5) + { + if (x < 1 / TWO_57) + return PI / 2; + double z = x * x; + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double r = x - (PI_L / 2 - x * (p / q)); + return negative ? PI / 2 + r : PI / 2 - r; + } + if (negative) // x<=-0.5. + { + double z = (1 + x) * 0.5; + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double s = sqrt(z); + double w = p / q * s - PI_L / 2; + return PI - 2 * (s + w); + } + double z = (1 - x) * 0.5; // x>0.5. + double s = sqrt(z); + double df = (float) s; + double c = (z - df * df) / (s + df); + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double w = p / q * s + c; + return 2 * (df + w); + } + + /** + * The trigonometric function <em>arcsin</em>. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN, the + * result is NaN; and the arctangent of 0 retains its sign. + * + * @param x the tan to turn back into an angle + * @return arcsin(x) + * @see #atan2(double, double) + */ + public static double atan(double x) + { + double lo; + double hi; + boolean negative = x < 0; + if (negative) + x = -x; + if (x >= TWO_66) + return negative ? -PI / 2 : PI / 2; + if (! (x >= 0.4375)) // |x|<7/16, or NaN. + { + if (! (x >= 1 / TWO_29)) // Small, or NaN. + return negative ? -x : x; + lo = hi = 0; + } + else if (x < 1.1875) + { + if (x < 0.6875) // 7/16<=|x|<11/16. + { + x = (2 * x - 1) / (2 + x); + hi = ATAN_0_5H; + lo = ATAN_0_5L; + } + else // 11/16<=|x|<19/16. + { + x = (x - 1) / (x + 1); + hi = PI / 4; + lo = PI_L / 4; + } + } + else if (x < 2.4375) // 19/16<=|x|<39/16. + { + x = (x - 1.5) / (1 + 1.5 * x); + hi = ATAN_1_5H; + lo = ATAN_1_5L; + } + else // 39/16<=|x|<2**66. + { + x = -1 / x; + hi = PI / 2; + lo = PI_L / 2; + } + + // Break sum from i=0 to 10 ATi*z**(i+1) into odd and even poly. + double z = x * x; + double w = z * z; + double s1 = z * (AT0 + w * (AT2 + w * (AT4 + w * (AT6 + w + * (AT8 + w * AT10))))); + double s2 = w * (AT1 + w * (AT3 + w * (AT5 + w * (AT7 + w * AT9)))); + if (hi == 0) + return negative ? x * (s1 + s2) - x : x - x * (s1 + s2); + z = hi - ((x * (s1 + s2) - lo) - x); + return negative ? -z : z; + } + + /** + * A special version of the trigonometric function <em>arctan</em>, for + * converting rectangular coordinates <em>(x, y)</em> to polar + * <em>(r, theta)</em>. This computes the arctangent of x/y in the range + * of -pi to pi radians (-180 to 180 degrees). Special cases:<ul> + * <li>If either argument is NaN, the result is NaN.</li> + * <li>If the first argument is positive zero and the second argument is + * positive, or the first argument is positive and finite and the second + * argument is positive infinity, then the result is positive zero.</li> + * <li>If the first argument is negative zero and the second argument is + * positive, or the first argument is negative and finite and the second + * argument is positive infinity, then the result is negative zero.</li> + * <li>If the first argument is positive zero and the second argument is + * negative, or the first argument is positive and finite and the second + * argument is negative infinity, then the result is the double value + * closest to pi.</li> + * <li>If the first argument is negative zero and the second argument is + * negative, or the first argument is negative and finite and the second + * argument is negative infinity, then the result is the double value + * closest to -pi.</li> + * <li>If the first argument is positive and the second argument is + * positive zero or negative zero, or the first argument is positive + * infinity and the second argument is finite, then the result is the + * double value closest to pi/2.</li> + * <li>If the first argument is negative and the second argument is + * positive zero or negative zero, or the first argument is negative + * infinity and the second argument is finite, then the result is the + * double value closest to -pi/2.</li> + * <li>If both arguments are positive infinity, then the result is the + * double value closest to pi/4.</li> + * <li>If the first argument is positive infinity and the second argument + * is negative infinity, then the result is the double value closest to + * 3*pi/4.</li> + * <li>If the first argument is negative infinity and the second argument + * is positive infinity, then the result is the double value closest to + * -pi/4.</li> + * <li>If both arguments are negative infinity, then the result is the + * double value closest to -3*pi/4.</li> + * + * </ul><p>This returns theta, the angle of the point. To get r, albeit + * slightly inaccurately, use sqrt(x*x+y*y). + * + * @param y the y position + * @param x the x position + * @return <em>theta</em> in the conversion of (x, y) to (r, theta) + * @see #atan(double) + */ + public static double atan2(double y, double x) + { + if (x != x || y != y) + return Double.NaN; + if (x == 1) + return atan(y); + if (x == Double.POSITIVE_INFINITY) + { + if (y == Double.POSITIVE_INFINITY) + return PI / 4; + if (y == Double.NEGATIVE_INFINITY) + return -PI / 4; + return 0 * y; + } + if (x == Double.NEGATIVE_INFINITY) + { + if (y == Double.POSITIVE_INFINITY) + return 3 * PI / 4; + if (y == Double.NEGATIVE_INFINITY) + return -3 * PI / 4; + return (1 / (0 * y) == Double.POSITIVE_INFINITY) ? PI : -PI; + } + if (y == 0) + { + if (1 / (0 * x) == Double.POSITIVE_INFINITY) + return y; + return (1 / y == Double.POSITIVE_INFINITY) ? PI : -PI; + } + if (y == Double.POSITIVE_INFINITY || y == Double.NEGATIVE_INFINITY + || x == 0) + return y < 0 ? -PI / 2 : PI / 2; + + double z = abs(y / x); // Safe to do y/x. + if (z > TWO_60) + z = PI / 2 + 0.5 * PI_L; + else if (x < 0 && z < 1 / TWO_60) + z = 0; + else + z = atan(z); + if (x > 0) + return y > 0 ? z : -z; + return y > 0 ? PI - (z - PI_L) : z - PI_L - PI; + } + + /** + * Take <em>e</em><sup>a</sup>. The opposite of <code>log()</code>. If the + * argument is NaN, the result is NaN; if the argument is positive infinity, + * the result is positive infinity; and if the argument is negative + * infinity, the result is positive zero. + * + * @param x the number to raise to the power + * @return the number raised to the power of <em>e</em> + * @see #log(double) + * @see #pow(double, double) + */ + public static double exp(double x) + { + if (x != x) + return x; + if (x > EXP_LIMIT_H) + return Double.POSITIVE_INFINITY; + if (x < EXP_LIMIT_L) + return 0; + + // Argument reduction. + double hi; + double lo; + int k; + double t = abs(x); + if (t > 0.5 * LN2) + { + if (t < 1.5 * LN2) + { + hi = t - LN2_H; + lo = LN2_L; + k = 1; + } + else + { + k = (int) (INV_LN2 * t + 0.5); + hi = t - k * LN2_H; + lo = k * LN2_L; + } + if (x < 0) + { + hi = -hi; + lo = -lo; + k = -k; + } + x = hi - lo; + } + else if (t < 1 / TWO_28) + return 1; + else + lo = hi = k = 0; + + // Now x is in primary range. + t = x * x; + double c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + if (k == 0) + return 1 - (x * c / (c - 2) - x); + double y = 1 - (lo - x * c / (2 - c) - hi); + return scale(y, k); + } + + /** + * Take ln(a) (the natural log). The opposite of <code>exp()</code>. If the + * argument is NaN or negative, the result is NaN; if the argument is + * positive infinity, the result is positive infinity; and if the argument + * is either zero, the result is negative infinity. + * + * <p>Note that the way to get log<sub>b</sub>(a) is to do this: + * <code>ln(a) / ln(b)</code>. + * + * @param x the number to take the natural log of + * @return the natural log of <code>a</code> + * @see #exp(double) + */ + public static double log(double x) + { + if (x == 0) + return Double.NEGATIVE_INFINITY; + if (x < 0) + return Double.NaN; + if (! (x < Double.POSITIVE_INFINITY)) + return x; + + // Normalize x. + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + bits = Double.doubleToLongBits(x); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + bits = (bits & 0x000fffffffffffffL) | 0x3ff0000000000000L; + x = Double.longBitsToDouble(bits); + if (x >= SQRT_2) + { + x *= 0.5; + exp++; + } + x--; + if (abs(x) < 1 / TWO_20) + { + if (x == 0) + return exp * LN2_H + exp * LN2_L; + double r = x * x * (0.5 - 1 / 3.0 * x); + if (exp == 0) + return x - r; + return exp * LN2_H - ((r - exp * LN2_L) - x); + } + double s = x / (2 + x); + double z = s * s; + double w = z * z; + double t1 = w * (LG2 + w * (LG4 + w * LG6)); + double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); + double r = t2 + t1; + if (bits >= 0x3ff6174a00000000L && bits < 0x3ff6b85200000000L) + { + double h = 0.5 * x * x; // Need more accuracy for x near sqrt(2). + if (exp == 0) + return x - (h - s * (h + r)); + return exp * LN2_H - ((h - (s * (h + r) + exp * LN2_L)) - x); + } + if (exp == 0) + return x - s * (x - r); + return exp * LN2_H - ((s * (x - r) - exp * LN2_L) - x); + } + + /** + * Take a square root. If the argument is NaN or negative, the result is + * NaN; if the argument is positive infinity, the result is positive + * infinity; and if the result is either zero, the result is the same. + * + * <p>For other roots, use pow(x, 1/rootNumber). + * + * @param x the numeric argument + * @return the square root of the argument + * @see #pow(double, double) + */ + public static double sqrt(double x) + { + if (x < 0) + return Double.NaN; + if (x == 0 || ! (x < Double.POSITIVE_INFINITY)) + return x; + + // Normalize x. + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + bits = Double.doubleToLongBits(x); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + bits = (bits & 0x000fffffffffffffL) | 0x0010000000000000L; + if ((exp & 1) == 1) // Odd exp, double x to make it even. + bits <<= 1; + exp >>= 1; + + // Generate sqrt(x) bit by bit. + bits <<= 1; + long q = 0; + long s = 0; + long r = 0x0020000000000000L; // Move r right to left. + while (r != 0) + { + long t = s + r; + if (t <= bits) + { + s = t + r; + bits -= t; + q += r; + } + bits <<= 1; + r >>= 1; + } + + // Use floating add to round correctly. + if (bits != 0) + q += q & 1; + return Double.longBitsToDouble((q >> 1) + ((exp + 1022L) << 52)); + } + + /** + * Raise a number to a power. Special cases:<ul> + * <li>If the second argument is positive or negative zero, then the result + * is 1.0.</li> + * <li>If the second argument is 1.0, then the result is the same as the + * first argument.</li> + * <li>If the second argument is NaN, then the result is NaN.</li> + * <li>If the first argument is NaN and the second argument is nonzero, + * then the result is NaN.</li> + * <li>If the absolute value of the first argument is greater than 1 and + * the second argument is positive infinity, or the absolute value of the + * first argument is less than 1 and the second argument is negative + * infinity, then the result is positive infinity.</li> + * <li>If the absolute value of the first argument is greater than 1 and + * the second argument is negative infinity, or the absolute value of the + * first argument is less than 1 and the second argument is positive + * infinity, then the result is positive zero.</li> + * <li>If the absolute value of the first argument equals 1 and the second + * argument is infinite, then the result is NaN.</li> + * <li>If the first argument is positive zero and the second argument is + * greater than zero, or the first argument is positive infinity and the + * second argument is less than zero, then the result is positive zero.</li> + * <li>If the first argument is positive zero and the second argument is + * less than zero, or the first argument is positive infinity and the + * second argument is greater than zero, then the result is positive + * infinity.</li> + * <li>If the first argument is negative zero and the second argument is + * greater than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is less than zero but not a + * finite odd integer, then the result is positive zero.</li> + * <li>If the first argument is negative zero and the second argument is a + * positive finite odd integer, or the first argument is negative infinity + * and the second argument is a negative finite odd integer, then the result + * is negative zero.</li> + * <li>If the first argument is negative zero and the second argument is + * less than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is greater than zero but not a + * finite odd integer, then the result is positive infinity.</li> + * <li>If the first argument is negative zero and the second argument is a + * negative finite odd integer, or the first argument is negative infinity + * and the second argument is a positive finite odd integer, then the result + * is negative infinity.</li> + * <li>If the first argument is less than zero and the second argument is a + * finite even integer, then the result is equal to the result of raising + * the absolute value of the first argument to the power of the second + * argument.</li> + * <li>If the first argument is less than zero and the second argument is a + * finite odd integer, then the result is equal to the negative of the + * result of raising the absolute value of the first argument to the power + * of the second argument.</li> + * <li>If the first argument is finite and less than zero and the second + * argument is finite and not an integer, then the result is NaN.</li> + * <li>If both arguments are integers, then the result is exactly equal to + * the mathematical result of raising the first argument to the power of + * the second argument if that result can in fact be represented exactly as + * a double value.</li> + * + * </ul><p>(In the foregoing descriptions, a floating-point value is + * considered to be an integer if and only if it is a fixed point of the + * method {@link #ceil(double)} or, equivalently, a fixed point of the + * method {@link #floor(double)}. A value is a fixed point of a one-argument + * method if and only if the result of applying the method to the value is + * equal to the value.) + * + * @param x the number to raise + * @param y the power to raise it to + * @return x<sup>y</sup> + */ + public static double pow(double x, double y) + { + // Special cases first. + if (y == 0) + return 1; + if (y == 1) + return x; + if (y == -1) + return 1 / x; + if (x != x || y != y) + return Double.NaN; + + // When x < 0, yisint tells if y is not an integer (0), even(1), + // or odd (2). + int yisint = 0; + if (x < 0 && floor(y) == y) + yisint = (y % 2 == 0) ? 2 : 1; + double ax = abs(x); + double ay = abs(y); + + // More special cases, of y. + if (ay == Double.POSITIVE_INFINITY) + { + if (ax == 1) + return Double.NaN; + if (ax > 1) + return y > 0 ? y : 0; + return y < 0 ? -y : 0; + } + if (y == 2) + return x * x; + if (y == 0.5) + return sqrt(x); + + // More special cases, of x. + if (x == 0 || ax == Double.POSITIVE_INFINITY || ax == 1) + { + if (y < 0) + ax = 1 / ax; + if (x < 0) + { + if (x == -1 && yisint == 0) + ax = Double.NaN; + else if (yisint == 1) + ax = -ax; + } + return ax; + } + if (x < 0 && yisint == 0) + return Double.NaN; + + // Now we can start! + double t; + double t1; + double t2; + double u; + double v; + double w; + if (ay > TWO_31) + { + if (ay > TWO_64) // Automatic over/underflow. + return ((ax < 1) ? y < 0 : y > 0) ? Double.POSITIVE_INFINITY : 0; + // Over/underflow if x is not close to one. + if (ax < 0.9999995231628418) + return y < 0 ? Double.POSITIVE_INFINITY : 0; + if (ax >= 1.0000009536743164) + return y > 0 ? Double.POSITIVE_INFINITY : 0; + // Now |1-x| is <= 2**-20, sufficient to compute + // log(x) by x-x^2/2+x^3/3-x^4/4. + t = x - 1; + w = t * t * (0.5 - t * (1 / 3.0 - t * 0.25)); + u = INV_LN2_H * t; + v = t * INV_LN2_L - w * INV_LN2; + t1 = (float) (u + v); + t2 = v - (t1 - u); + } + else + { + long bits = Double.doubleToLongBits(ax); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + ax *= TWO_54; + bits = Double.doubleToLongBits(ax); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + ax = Double.longBitsToDouble((bits & 0x000fffffffffffffL) + | 0x3ff0000000000000L); + boolean k; + if (ax < SQRT_1_5) // |x|<sqrt(3/2). + k = false; + else if (ax < SQRT_3) // |x|<sqrt(3). + k = true; + else + { + k = false; + ax *= 0.5; + exp++; + } + + // Compute s = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5). + u = ax - (k ? 1.5 : 1); + v = 1 / (ax + (k ? 1.5 : 1)); + double s = u * v; + double s_h = (float) s; + double t_h = (float) (ax + (k ? 1.5 : 1)); + double t_l = ax - (t_h - (k ? 1.5 : 1)); + double s_l = v * ((u - s_h * t_h) - s_h * t_l); + // Compute log(ax). + double s2 = s * s; + double r = s_l * (s_h + s) + s2 * s2 + * (L1 + s2 * (L2 + s2 * (L3 + s2 * (L4 + s2 * (L5 + s2 * L6))))); + s2 = s_h * s_h; + t_h = (float) (3.0 + s2 + r); + t_l = r - (t_h - 3.0 - s2); + // u+v = s*(1+...). + u = s_h * t_h; + v = s_l * t_h + t_l * s; + // 2/(3log2)*(s+...). + double p_h = (float) (u + v); + double p_l = v - (p_h - u); + double z_h = CP_H * p_h; + double z_l = CP_L * p_h + p_l * CP + (k ? DP_L : 0); + // log2(ax) = (s+..)*2/(3*log2) = exp + dp_h + z_h + z_l. + t = exp; + t1 = (float) (z_h + z_l + (k ? DP_H : 0) + t); + t2 = z_l - (t1 - t - (k ? DP_H : 0) - z_h); + } + + // Split up y into y1+y2 and compute (y1+y2)*(t1+t2). + boolean negative = x < 0 && yisint == 1; + double y1 = (float) y; + double p_l = (y - y1) * t1 + y * t2; + double p_h = y1 * t1; + double z = p_l + p_h; + if (z >= 1024) // Detect overflow. + { + if (z > 1024 || p_l + OVT > z - p_h) + return negative ? Double.NEGATIVE_INFINITY + : Double.POSITIVE_INFINITY; + } + else if (z <= -1075) // Detect underflow. + { + if (z < -1075 || p_l <= z - p_h) + return negative ? -0.0 : 0; + } + + // Compute 2**(p_h+p_l). + int n = round((float) z); + p_h -= n; + t = (float) (p_l + p_h); + u = t * LN2_H; + v = (p_l - (t - p_h)) * LN2 + t * LN2_L; + z = u + v; + w = v - (z - u); + t = z * z; + t1 = z - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + double r = (z * t1) / (t1 - 2) - (w + z * w); + z = scale(1 - (r - z), n); + return negative ? -z : z; + } + + /** + * Get the IEEE 754 floating point remainder on two numbers. This is the + * value of <code>x - y * <em>n</em></code>, where <em>n</em> is the closest + * double to <code>x / y</code> (ties go to the even n); for a zero + * remainder, the sign is that of <code>x</code>. If either argument is NaN, + * the first argument is infinite, or the second argument is zero, the result + * is NaN; if x is finite but y is infinite, the result is x. + * + * @param x the dividend (the top half) + * @param y the divisor (the bottom half) + * @return the IEEE 754-defined floating point remainder of x/y + * @see #rint(double) + */ + public static double IEEEremainder(double x, double y) + { + // Purge off exception values. + if (x == Double.NEGATIVE_INFINITY || ! (x < Double.POSITIVE_INFINITY) + || y == 0 || y != y) + return Double.NaN; + + boolean negative = x < 0; + x = abs(x); + y = abs(y); + if (x == y || x == 0) + return 0 * x; // Get correct sign. + + // Achieve x < 2y, then take first shot at remainder. + if (y < TWO_1023) + x %= y + y; + + // Now adjust x to get correct precision. + if (y < 4 / TWO_1023) + { + if (x + x > y) + { + x -= y; + if (x + x >= y) + x -= y; + } + } + else + { + y *= 0.5; + if (x > y) + { + x -= y; + if (x >= y) + x -= y; + } + } + return negative ? -x : x; + } + + /** + * Take the nearest integer that is that is greater than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same; if the argument is between -1 and 0, the result is negative zero. + * Note that <code>Math.ceil(x) == -Math.floor(-x)</code>. + * + * @param a the value to act upon + * @return the nearest integer >= <code>a</code> + */ + public static double ceil(double a) + { + return -floor(-a); + } + + /** + * Take the nearest integer that is that is less than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same. Note that <code>Math.ceil(x) == -Math.floor(-x)</code>. + * + * @param a the value to act upon + * @return the nearest integer <= <code>a</code> + */ + public static double floor(double a) + { + double x = abs(a); + if (! (x < TWO_52) || (long) a == a) + return a; // No fraction bits; includes NaN and infinity. + if (x < 1) + return a >= 0 ? 0 * a : -1; // Worry about signed zero. + return a < 0 ? (long) a - 1.0 : (long) a; // Cast to long truncates. + } + + /** + * Take the nearest integer to the argument. If it is exactly between + * two integers, the even integer is taken. If the argument is NaN, + * infinite, or zero, the result is the same. + * + * @param a the value to act upon + * @return the nearest integer to <code>a</code> + */ + public static double rint(double a) + { + double x = abs(a); + if (! (x < TWO_52)) + return a; // No fraction bits; includes NaN and infinity. + if (x <= 0.5) + return 0 * a; // Worry about signed zero. + if (x % 2 <= 0.5) + return (long) a; // Catch round down to even. + return (long) (a + (a < 0 ? -0.5 : 0.5)); // Cast to long truncates. + } + + /** + * Take the nearest integer to the argument. This is equivalent to + * <code>(int) Math.floor(f + 0.5f)</code>. If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of int, the + * result will be Integer.MIN_VALUE or Integer.MAX_VALUE, as appropriate. + * + * @param f the argument to round + * @return the nearest integer to the argument + * @see Integer#MIN_VALUE + * @see Integer#MAX_VALUE + */ + public static int round(float f) + { + return (int) floor(f + 0.5f); + } + + /** + * Take the nearest long to the argument. This is equivalent to + * <code>(long) Math.floor(d + 0.5)</code>. If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of long, the + * result will be Long.MIN_VALUE or Long.MAX_VALUE, as appropriate. + * + * @param d the argument to round + * @return the nearest long to the argument + * @see Long#MIN_VALUE + * @see Long#MAX_VALUE + */ + public static long round(double d) + { + return (long) floor(d + 0.5); + } + + /** + * Get a random number. This behaves like Random.nextDouble(), seeded by + * System.currentTimeMillis() when first called. In other words, the number + * is from a pseudorandom sequence, and lies in the range [+0.0, 1.0). + * This random sequence is only used by this method, and is threadsafe, + * although you may want your own random number generator if it is shared + * among threads. + * + * @return a random number + * @see Random#nextDouble() + * @see System#currentTimeMillis() + */ + public static synchronized double random() + { + if (rand == null) + rand = new Random(); + return rand.nextDouble(); + } + + /** + * Convert from degrees to radians. The formula for this is + * radians = degrees * (pi/180); however it is not always exact given the + * limitations of floating point numbers. + * + * @param degrees an angle in degrees + * @return the angle in radians + */ + public static double toRadians(double degrees) + { + return (degrees * PI) / 180; + } + + /** + * Convert from radians to degrees. The formula for this is + * degrees = radians * (180/pi); however it is not always exact given the + * limitations of floating point numbers. + * + * @param rads an angle in radians + * @return the angle in degrees + */ + public static double toDegrees(double rads) + { + return (rads * 180) / PI; + } + + /** + * Constants for scaling and comparing doubles by powers of 2. The compiler + * must automatically inline constructs like (1/TWO_54), so we don't list + * negative powers of two here. + */ + private static final double + TWO_16 = 0x10000, // Long bits 0x40f0000000000000L. + TWO_20 = 0x100000, // Long bits 0x4130000000000000L. + TWO_24 = 0x1000000, // Long bits 0x4170000000000000L. + TWO_27 = 0x8000000, // Long bits 0x41a0000000000000L. + TWO_28 = 0x10000000, // Long bits 0x41b0000000000000L. + TWO_29 = 0x20000000, // Long bits 0x41c0000000000000L. + TWO_31 = 0x80000000L, // Long bits 0x41e0000000000000L. + TWO_49 = 0x2000000000000L, // Long bits 0x4300000000000000L. + TWO_52 = 0x10000000000000L, // Long bits 0x4330000000000000L. + TWO_54 = 0x40000000000000L, // Long bits 0x4350000000000000L. + TWO_57 = 0x200000000000000L, // Long bits 0x4380000000000000L. + TWO_60 = 0x1000000000000000L, // Long bits 0x43b0000000000000L. + TWO_64 = 1.8446744073709552e19, // Long bits 0x43f0000000000000L. + TWO_66 = 7.378697629483821e19, // Long bits 0x4410000000000000L. + TWO_1023 = 8.98846567431158e307; // Long bits 0x7fe0000000000000L. + + /** + * Super precision for 2/pi in 24-bit chunks, for use in + * {@link #remPiOver2()}. + */ + private static final int TWO_OVER_PI[] = { + 0xa2f983, 0x6e4e44, 0x1529fc, 0x2757d1, 0xf534dd, 0xc0db62, + 0x95993c, 0x439041, 0xfe5163, 0xabdebb, 0xc561b7, 0x246e3a, + 0x424dd2, 0xe00649, 0x2eea09, 0xd1921c, 0xfe1deb, 0x1cb129, + 0xa73ee8, 0x8235f5, 0x2ebb44, 0x84e99c, 0x7026b4, 0x5f7e41, + 0x3991d6, 0x398353, 0x39f49c, 0x845f8b, 0xbdf928, 0x3b1ff8, + 0x97ffde, 0x05980f, 0xef2f11, 0x8b5a0a, 0x6d1f6d, 0x367ecf, + 0x27cb09, 0xb74f46, 0x3f669e, 0x5fea2d, 0x7527ba, 0xc7ebe5, + 0xf17b3d, 0x0739f7, 0x8a5292, 0xea6bfb, 0x5fb11f, 0x8d5d08, + 0x560330, 0x46fc7b, 0x6babf0, 0xcfbc20, 0x9af436, 0x1da9e3, + 0x91615e, 0xe61b08, 0x659985, 0x5f14a0, 0x68408d, 0xffd880, + 0x4d7327, 0x310606, 0x1556ca, 0x73a8c9, 0x60e27b, 0xc08c6b, + }; + + /** + * Super precision for pi/2 in 24-bit chunks, for use in + * {@link #remPiOver2()}. + */ + private static final double PI_OVER_TWO[] = { + 1.570796251296997, // Long bits 0x3ff921fb40000000L. + 7.549789415861596e-8, // Long bits 0x3e74442d00000000L. + 5.390302529957765e-15, // Long bits 0x3cf8469880000000L. + 3.282003415807913e-22, // Long bits 0x3b78cc5160000000L. + 1.270655753080676e-29, // Long bits 0x39f01b8380000000L. + 1.2293330898111133e-36, // Long bits 0x387a252040000000L. + 2.7337005381646456e-44, // Long bits 0x36e3822280000000L. + 2.1674168387780482e-51, // Long bits 0x3569f31d00000000L. + }; + + /** + * More constants related to pi, used in {@link #remPiOver2()} and + * elsewhere. + */ + private static final double + PI_L = 1.2246467991473532e-16, // Long bits 0x3ca1a62633145c07L. + PIO2_1 = 1.5707963267341256, // Long bits 0x3ff921fb54400000L. + PIO2_1L = 6.077100506506192e-11, // Long bits 0x3dd0b4611a626331L. + PIO2_2 = 6.077100506303966e-11, // Long bits 0x3dd0b4611a600000L. + PIO2_2L = 2.0222662487959506e-21, // Long bits 0x3ba3198a2e037073L. + PIO2_3 = 2.0222662487111665e-21, // Long bits 0x3ba3198a2e000000L. + PIO2_3L = 8.4784276603689e-32; // Long bits 0x397b839a252049c1L. + + /** + * Natural log and square root constants, for calculation of + * {@link #exp(double)}, {@link #log(double)} and + * {@link #power(double, double)}. CP is 2/(3*ln(2)). + */ + private static final double + SQRT_1_5 = 1.224744871391589, // Long bits 0x3ff3988e1409212eL. + SQRT_2 = 1.4142135623730951, // Long bits 0x3ff6a09e667f3bcdL. + SQRT_3 = 1.7320508075688772, // Long bits 0x3ffbb67ae8584caaL. + EXP_LIMIT_H = 709.782712893384, // Long bits 0x40862e42fefa39efL. + EXP_LIMIT_L = -745.1332191019411, // Long bits 0xc0874910d52d3051L. + CP = 0.9617966939259756, // Long bits 0x3feec709dc3a03fdL. + CP_H = 0.9617967009544373, // Long bits 0x3feec709e0000000L. + CP_L = -7.028461650952758e-9, // Long bits 0xbe3e2fe0145b01f5L. + LN2 = 0.6931471805599453, // Long bits 0x3fe62e42fefa39efL. + LN2_H = 0.6931471803691238, // Long bits 0x3fe62e42fee00000L. + LN2_L = 1.9082149292705877e-10, // Long bits 0x3dea39ef35793c76L. + INV_LN2 = 1.4426950408889634, // Long bits 0x3ff71547652b82feL. + INV_LN2_H = 1.4426950216293335, // Long bits 0x3ff7154760000000L. + INV_LN2_L = 1.9259629911266175e-8; // Long bits 0x3e54ae0bf85ddf44L. + + /** + * Constants for computing {@link #log(double)}. + */ + private static final double + LG1 = 0.6666666666666735, // Long bits 0x3fe5555555555593L. + LG2 = 0.3999999999940942, // Long bits 0x3fd999999997fa04L. + LG3 = 0.2857142874366239, // Long bits 0x3fd2492494229359L. + LG4 = 0.22222198432149784, // Long bits 0x3fcc71c51d8e78afL. + LG5 = 0.1818357216161805, // Long bits 0x3fc7466496cb03deL. + LG6 = 0.15313837699209373, // Long bits 0x3fc39a09d078c69fL. + LG7 = 0.14798198605116586; // Long bits 0x3fc2f112df3e5244L. + + /** + * Constants for computing {@link #pow(double, double)}. L and P are + * coefficients for series; OVT is -(1024-log2(ovfl+.5ulp)); and DP is ???. + * The P coefficients also calculate {@link #exp(double)}. + */ + private static final double + L1 = 0.5999999999999946, // Long bits 0x3fe3333333333303L. + L2 = 0.4285714285785502, // Long bits 0x3fdb6db6db6fabffL. + L3 = 0.33333332981837743, // Long bits 0x3fd55555518f264dL. + L4 = 0.272728123808534, // Long bits 0x3fd17460a91d4101L. + L5 = 0.23066074577556175, // Long bits 0x3fcd864a93c9db65L. + L6 = 0.20697501780033842, // Long bits 0x3fca7e284a454eefL. + P1 = 0.16666666666666602, // Long bits 0x3fc555555555553eL. + P2 = -2.7777777777015593e-3, // Long bits 0xbf66c16c16bebd93L. + P3 = 6.613756321437934e-5, // Long bits 0x3f11566aaf25de2cL. + P4 = -1.6533902205465252e-6, // Long bits 0xbebbbd41c5d26bf1L. + P5 = 4.1381367970572385e-8, // Long bits 0x3e66376972bea4d0L. + DP_H = 0.5849624872207642, // Long bits 0x3fe2b80340000000L. + DP_L = 1.350039202129749e-8, // Long bits 0x3e4cfdeb43cfd006L. + OVT = 8.008566259537294e-17; // Long bits 0x3c971547652b82feL. + + /** + * Coefficients for computing {@link #sin(double)}. + */ + private static final double + S1 = -0.16666666666666632, // Long bits 0xbfc5555555555549L. + S2 = 8.33333333332249e-3, // Long bits 0x3f8111111110f8a6L. + S3 = -1.984126982985795e-4, // Long bits 0xbf2a01a019c161d5L. + S4 = 2.7557313707070068e-6, // Long bits 0x3ec71de357b1fe7dL. + S5 = -2.5050760253406863e-8, // Long bits 0xbe5ae5e68a2b9cebL. + S6 = 1.58969099521155e-10; // Long bits 0x3de5d93a5acfd57cL. + + /** + * Coefficients for computing {@link #cos(double)}. + */ + private static final double + C1 = 0.0416666666666666, // Long bits 0x3fa555555555554cL. + C2 = -1.388888888887411e-3, // Long bits 0xbf56c16c16c15177L. + C3 = 2.480158728947673e-5, // Long bits 0x3efa01a019cb1590L. + C4 = -2.7557314351390663e-7, // Long bits 0xbe927e4f809c52adL. + C5 = 2.087572321298175e-9, // Long bits 0x3e21ee9ebdb4b1c4L. + C6 = -1.1359647557788195e-11; // Long bits 0xbda8fae9be8838d4L. + + /** + * Coefficients for computing {@link #tan(double)}. + */ + private static final double + T0 = 0.3333333333333341, // Long bits 0x3fd5555555555563L. + T1 = 0.13333333333320124, // Long bits 0x3fc111111110fe7aL. + T2 = 0.05396825397622605, // Long bits 0x3faba1ba1bb341feL. + T3 = 0.021869488294859542, // Long bits 0x3f9664f48406d637L. + T4 = 8.8632398235993e-3, // Long bits 0x3f8226e3e96e8493L. + T5 = 3.5920791075913124e-3, // Long bits 0x3f6d6d22c9560328L. + T6 = 1.4562094543252903e-3, // Long bits 0x3f57dbc8fee08315L. + T7 = 5.880412408202641e-4, // Long bits 0x3f4344d8f2f26501L. + T8 = 2.464631348184699e-4, // Long bits 0x3f3026f71a8d1068L. + T9 = 7.817944429395571e-5, // Long bits 0x3f147e88a03792a6L. + T10 = 7.140724913826082e-5, // Long bits 0x3f12b80f32f0a7e9L. + T11 = -1.8558637485527546e-5, // Long bits 0xbef375cbdb605373L. + T12 = 2.590730518636337e-5; // Long bits 0x3efb2a7074bf7ad4L. + + /** + * Coefficients for computing {@link #asin(double)} and + * {@link #acos(double)}. + */ + private static final double + PS0 = 0.16666666666666666, // Long bits 0x3fc5555555555555L. + PS1 = -0.3255658186224009, // Long bits 0xbfd4d61203eb6f7dL. + PS2 = 0.20121253213486293, // Long bits 0x3fc9c1550e884455L. + PS3 = -0.04005553450067941, // Long bits 0xbfa48228b5688f3bL. + PS4 = 7.915349942898145e-4, // Long bits 0x3f49efe07501b288L. + PS5 = 3.479331075960212e-5, // Long bits 0x3f023de10dfdf709L. + QS1 = -2.403394911734414, // Long bits 0xc0033a271c8a2d4bL. + QS2 = 2.0209457602335057, // Long bits 0x40002ae59c598ac8L. + QS3 = -0.6882839716054533, // Long bits 0xbfe6066c1b8d0159L. + QS4 = 0.07703815055590194; // Long bits 0x3fb3b8c5b12e9282L. + + /** + * Coefficients for computing {@link #atan(double)}. + */ + private static final double + ATAN_0_5H = 0.4636476090008061, // Long bits 0x3fddac670561bb4fL. + ATAN_0_5L = 2.2698777452961687e-17, // Long bits 0x3c7a2b7f222f65e2L. + ATAN_1_5H = 0.982793723247329, // Long bits 0x3fef730bd281f69bL. + ATAN_1_5L = 1.3903311031230998e-17, // Long bits 0x3c7007887af0cbbdL. + AT0 = 0.3333333333333293, // Long bits 0x3fd555555555550dL. + AT1 = -0.19999999999876483, // Long bits 0xbfc999999998ebc4L. + AT2 = 0.14285714272503466, // Long bits 0x3fc24924920083ffL. + AT3 = -0.11111110405462356, // Long bits 0xbfbc71c6fe231671L. + AT4 = 0.09090887133436507, // Long bits 0x3fb745cdc54c206eL. + AT5 = -0.0769187620504483, // Long bits 0xbfb3b0f2af749a6dL. + AT6 = 0.06661073137387531, // Long bits 0x3fb10d66a0d03d51L. + AT7 = -0.058335701337905735, // Long bits 0xbfadde2d52defd9aL. + AT8 = 0.049768779946159324, // Long bits 0x3fa97b4b24760debL. + AT9 = -0.036531572744216916, // Long bits 0xbfa2b4442c6a6c2fL. + AT10 = 0.016285820115365782; // Long bits 0x3f90ad3ae322da11L. + + /** + * Helper function for reducing an angle to a multiple of pi/2 within + * [-pi/4, pi/4]. + * + * @param x the angle; not infinity or NaN, and outside pi/4 + * @param y an array of 2 doubles modified to hold the remander x % pi/2 + * @return the quadrant of the result, mod 4: 0: [-pi/4, pi/4], + * 1: [pi/4, 3*pi/4], 2: [3*pi/4, 5*pi/4], 3: [-3*pi/4, -pi/4] + */ + private static int remPiOver2(double x, double[] y) + { + boolean negative = x < 0; + x = abs(x); + double z; + int n; + if (Configuration.DEBUG && (x <= PI / 4 || x != x + || x == Double.POSITIVE_INFINITY)) + throw new InternalError("Assertion failure"); + if (x < 3 * PI / 4) // If |x| is small. + { + z = x - PIO2_1; + if ((float) x != (float) (PI / 2)) // 33+53 bit pi is good enough. + { + y[0] = z - PIO2_1L; + y[1] = z - y[0] - PIO2_1L; + } + else // Near pi/2, use 33+33+53 bit pi. + { + z -= PIO2_2; + y[0] = z - PIO2_2L; + y[1] = z - y[0] - PIO2_2L; + } + n = 1; + } + else if (x <= TWO_20 * PI / 2) // Medium size. + { + n = (int) (2 / PI * x + 0.5); + z = x - n * PIO2_1; + double w = n * PIO2_1L; // First round good to 85 bits. + y[0] = z - w; + if (n >= 32 || (float) x == (float) (w)) + { + if (x / y[0] >= TWO_16) // Second iteration, good to 118 bits. + { + double t = z; + w = n * PIO2_2; + z = t - w; + w = n * PIO2_2L - (t - z - w); + y[0] = z - w; + if (x / y[0] >= TWO_49) // Third iteration, 151 bits accuracy. + { + t = z; + w = n * PIO2_3; + z = t - w; + w = n * PIO2_3L - (t - z - w); + y[0] = z - w; + } + } + } + y[1] = z - y[0] - w; + } + else + { + // All other (large) arguments. + int e0 = (int) (Double.doubleToLongBits(x) >> 52) - 1046; + z = scale(x, -e0); // e0 = ilogb(z) - 23. + double[] tx = new double[3]; + for (int i = 0; i < 2; i++) + { + tx[i] = (int) z; + z = (z - tx[i]) * TWO_24; + } + tx[2] = z; + int nx = 2; + while (tx[nx] == 0) + nx--; + n = remPiOver2(tx, y, e0, nx); + } + if (negative) + { + y[0] = -y[0]; + y[1] = -y[1]; + return -n; + } + return n; + } + + /** + * Helper function for reducing an angle to a multiple of pi/2 within + * [-pi/4, pi/4]. + * + * @param x the positive angle, broken into 24-bit chunks + * @param y an array of 2 doubles modified to hold the remander x % pi/2 + * @param e0 the exponent of x[0] + * @param nx the last index used in x + * @return the quadrant of the result, mod 4: 0: [-pi/4, pi/4], + * 1: [pi/4, 3*pi/4], 2: [3*pi/4, 5*pi/4], 3: [-3*pi/4, -pi/4] + */ + private static int remPiOver2(double[] x, double[] y, int e0, int nx) + { + int i; + int ih; + int n; + double fw; + double z; + int[] iq = new int[20]; + double[] f = new double[20]; + double[] q = new double[20]; + boolean recompute = false; + + // Initialize jk, jz, jv, q0; note that 3>q0. + int jk = 4; + int jz = jk; + int jv = max((e0 - 3) / 24, 0); + int q0 = e0 - 24 * (jv + 1); + + // Set up f[0] to f[nx+jk] where f[nx+jk] = TWO_OVER_PI[jv+jk]. + int j = jv - nx; + int m = nx + jk; + for (i = 0; i <= m; i++, j++) + f[i] = (j < 0) ? 0 : TWO_OVER_PI[j]; + + // Compute q[0],q[1],...q[jk]. + for (i = 0; i <= jk; i++) + { + for (j = 0, fw = 0; j <= nx; j++) + fw += x[j] * f[nx + i - j]; + q[i] = fw; + } + + do + { + // Distill q[] into iq[] reversingly. + for (i = 0, j = jz, z = q[jz]; j > 0; i++, j--) + { + fw = (int) (1 / TWO_24 * z); + iq[i] = (int) (z - TWO_24 * fw); + z = q[j - 1] + fw; + } + + // Compute n. + z = scale(z, q0); + z -= 8 * floor(z * 0.125); // Trim off integer >= 8. + n = (int) z; + z -= n; + ih = 0; + if (q0 > 0) // Need iq[jz-1] to determine n. + { + i = iq[jz - 1] >> (24 - q0); + n += i; + iq[jz - 1] -= i << (24 - q0); + ih = iq[jz - 1] >> (23 - q0); + } + else if (q0 == 0) + ih = iq[jz - 1] >> 23; + else if (z >= 0.5) + ih = 2; + + if (ih > 0) // If q > 0.5. + { + n += 1; + int carry = 0; + for (i = 0; i < jz; i++) // Compute 1-q. + { + j = iq[i]; + if (carry == 0) + { + if (j != 0) + { + carry = 1; + iq[i] = 0x1000000 - j; + } + } + else + iq[i] = 0xffffff - j; + } + switch (q0) + { + case 1: // Rare case: chance is 1 in 12 for non-default. + iq[jz - 1] &= 0x7fffff; + break; + case 2: + iq[jz - 1] &= 0x3fffff; + } + if (ih == 2) + { + z = 1 - z; + if (carry != 0) + z -= scale(1, q0); + } + } + + // Check if recomputation is needed. + if (z == 0) + { + j = 0; + for (i = jz - 1; i >= jk; i--) + j |= iq[i]; + if (j == 0) // Need recomputation. + { + int k; + for (k = 1; iq[jk - k] == 0; k++); // k = no. of terms needed. + + for (i = jz + 1; i <= jz + k; i++) // Add q[jz+1] to q[jz+k]. + { + f[nx + i] = TWO_OVER_PI[jv + i]; + for (j = 0, fw = 0; j <= nx; j++) + fw += x[j] * f[nx + i - j]; + q[i] = fw; + } + jz += k; + recompute = true; + } + } + } + while (recompute); + + // Chop off zero terms. + if (z == 0) + { + jz--; + q0 -= 24; + while (iq[jz] == 0) + { + jz--; + q0 -= 24; + } + } + else // Break z into 24-bit if necessary. + { + z = scale(z, -q0); + if (z >= TWO_24) + { + fw = (int) (1 / TWO_24 * z); + iq[jz] = (int) (z - TWO_24 * fw); + jz++; + q0 += 24; + iq[jz] = (int) fw; + } + else + iq[jz] = (int) z; + } + + // Convert integer "bit" chunk to floating-point value. + fw = scale(1, q0); + for (i = jz; i >= 0; i--) + { + q[i] = fw * iq[i]; + fw *= 1 / TWO_24; + } + + // Compute PI_OVER_TWO[0,...,jk]*q[jz,...,0]. + double[] fq = new double[20]; + for (i = jz; i >= 0; i--) + { + fw = 0; + for (int k = 0; k <= jk && k <= jz - i; k++) + fw += PI_OVER_TWO[k] * q[i + k]; + fq[jz - i] = fw; + } + + // Compress fq[] into y[]. + fw = 0; + for (i = jz; i >= 0; i--) + fw += fq[i]; + y[0] = (ih == 0) ? fw : -fw; + fw = fq[0] - fw; + for (i = 1; i <= jz; i++) + fw += fq[i]; + y[1] = (ih == 0) ? fw : -fw; + return n; + } + + /** + * Helper method for scaling a double by a power of 2. + * + * @param x the double + * @param n the scale; |n| < 2048 + * @return x * 2**n + */ + private static double scale(double x, int n) + { + if (Configuration.DEBUG && abs(n) >= 2048) + throw new InternalError("Assertion failure"); + if (x == 0 || x == Double.NEGATIVE_INFINITY + || ! (x < Double.POSITIVE_INFINITY) || n == 0) + return x; + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52) & 0x7ff; + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + exp = ((int) (Double.doubleToLongBits(x) >> 52) & 0x7ff) - 54; + } + exp += n; + if (exp > 0x7fe) // Overflow. + return Double.POSITIVE_INFINITY * x; + if (exp > 0) // Normal. + return Double.longBitsToDouble((bits & 0x800fffffffffffffL) + | ((long) exp << 52)); + if (exp <= -54) + return 0 * x; // Underflow. + exp += 54; // Subnormal result. + x = Double.longBitsToDouble((bits & 0x800fffffffffffffL) + | ((long) exp << 52)); + return x * (1 / TWO_54); + } + + /** + * Helper trig function; computes sin in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @return sin(x+y) + */ + private static double sin(double x, double y) + { + if (Configuration.DEBUG && abs(x + y) > 0.7854) + throw new InternalError("Assertion failure"); + if (abs(x) < 1 / TWO_27) + return x; // If |x| ~< 2**-27, already know answer. + + double z = x * x; + double v = z * x; + double r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6))); + if (y == 0) + return x + v * (S1 + z * r); + return x - ((z * (0.5 * y - v * r) - y) - v * S1); + } + + /** + * Helper trig function; computes cos in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @return cos(x+y) + */ + private static double cos(double x, double y) + { + if (Configuration.DEBUG && abs(x + y) > 0.7854) + throw new InternalError("Assertion failure"); + x = abs(x); + if (x < 1 / TWO_27) + return 1; // If |x| ~< 2**-27, already know answer. + + double z = x * x; + double r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6))))); + + if (x < 0.3) + return 1 - (0.5 * z - (z * r - x * y)); + + double qx = (x > 0.78125) ? 0.28125 : (x * 0.25); + return 1 - qx - ((0.5 * z - qx) - (z * r - x * y)); + } + + /** + * Helper trig function; computes tan in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @param invert true iff -1/tan should be returned instead + * @return tan(x+y) + */ + private static double tan(double x, double y, boolean invert) + { + // PI/2 is irrational, so no double is a perfect multiple of it. + if (Configuration.DEBUG && (abs(x + y) > 0.7854 || (x == 0 && invert))) + throw new InternalError("Assertion failure"); + boolean negative = x < 0; + if (negative) + { + x = -x; + y = -y; + } + if (x < 1 / TWO_28) // If |x| ~< 2**-28, already know answer. + return (negative ? -1 : 1) * (invert ? -1 / x : x); + + double z; + double w; + boolean large = x >= 0.6744; + if (large) + { + z = PI / 4 - x; + w = PI_L / 4 - y; + x = z + w; + y = 0; + } + z = x * x; + w = z * z; + // Break x**5*(T1+x**2*T2+...) into + // x**5(T1+x**4*T3+...+x**20*T11) + // + x**5(x**2*(T2+x**4*T4+...+x**22*T12)). + double r = T1 + w * (T3 + w * (T5 + w * (T7 + w * (T9 + w * T11)))); + double v = z * (T2 + w * (T4 + w * (T6 + w * (T8 + w * (T10 + w * T12))))); + double s = z * x; + r = y + z * (s * (r + v) + y); + r += T0 * s; + w = x + r; + if (large) + { + v = invert ? -1 : 1; + return (negative ? -1 : 1) * (v - 2 * (x - (w * w / (w + v) - r))); + } + if (! invert) + return w; + + // Compute -1.0/(x+r) accurately. + z = (float) w; + v = r - (z - x); + double a = -1 / w; + double t = (float) a; + return t + a * (1 + t * z + t * v); + } +} diff --git a/libjava/classpath/java/lang/String.java b/libjava/classpath/java/lang/String.java new file mode 100644 index 00000000000..b4db8505051 --- /dev/null +++ b/libjava/classpath/java/lang/String.java @@ -0,0 +1,1750 @@ +/* String.java -- immutable character sequences; the object of string literals + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.java.lang.CharData; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.text.Collator; +import java.util.Comparator; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Strings represent an immutable set of characters. All String literals + * are instances of this class, and two string literals with the same contents + * refer to the same String object. + * + * <p>This class also includes a number of methods for manipulating the + * contents of strings (of course, creating a new object if there are any + * changes, as String is immutable). Case mapping relies on Unicode 3.0.0 + * standards, where some character sequences have a different number of + * characters in the uppercase version than the lower case. + * + * <p>Strings are special, in that they are the only object with an overloaded + * operator. When you use '+' with at least one String argument, both + * arguments have String conversion performed on them, and another String (not + * guaranteed to be unique) results. + * + * <p>String is special-cased when doing data serialization - rather than + * listing the fields of this class, a String object is converted to a string + * literal in the object stream. + * + * @author Paul N. Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Per Bothner (bothner@cygnus.com) + * @since 1.0 + * @status updated to 1.4; but could use better data sharing via offset field + */ +public final class String implements Serializable, Comparable, CharSequence +{ + // WARNING: String is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * This is probably not necessary because this class is special cased already + * but it will avoid showing up as a discrepancy when comparing SUIDs. + */ + private static final long serialVersionUID = -6849794470754667710L; + + /** + * Stores unicode multi-character uppercase expansion table. + * @see #toUpperCase(char) + * @see CharData#UPPER_EXPAND + */ + private static final char[] upperExpand + = zeroBasedStringValue(CharData.UPPER_EXPAND); + + /** + * Stores unicode multi-character uppercase special casing table. + * @see #upperCaseExpansion(char) + * @see CharData#UPPER_SPECIAL + */ + private static final char[] upperSpecial + = zeroBasedStringValue(CharData.UPPER_SPECIAL); + + /** + * Characters which make up the String. + * Package access is granted for use by StringBuffer. + */ + final char[] value; + + /** + * Holds the number of characters in value. This number is generally + * the same as value.length, but can be smaller because substrings and + * StringBuffers can share arrays. Package visible for use by trusted code. + */ + final int count; + + /** + * Caches the result of hashCode(). If this value is zero, the hashcode + * is considered uncached (even if 0 is the correct hash value). + */ + private int cachedHashCode; + + /** + * Holds the starting position for characters in value[]. Since + * substring()'s are common, the use of offset allows the operation + * to perform in O(1). Package access is granted for use by StringBuffer. + */ + final int offset; + + /** + * An implementation for {@link CASE_INSENSITIVE_ORDER}. + * This must be {@link Serializable}. The class name is dictated by + * compatibility with Sun's JDK. + */ + private static final class CaseInsensitiveComparator + implements Comparator, Serializable + { + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 8575799808933029326L; + + /** + * The default private constructor generates unnecessary overhead. + */ + CaseInsensitiveComparator() {} + + /** + * Compares to Strings, using + * <code>String.compareToIgnoreCase(String)</code>. + * + * @param o1 the first string + * @param o2 the second string + * @return < 0, 0, or > 0 depending on the case-insensitive + * comparison of the two strings. + * @throws NullPointerException if either argument is null + * @throws ClassCastException if either argument is not a String + * @see #compareToIgnoreCase(String) + */ + public int compare(Object o1, Object o2) + { + return ((String) o1).compareToIgnoreCase((String) o2); + } + } // class CaseInsensitiveComparator + + /** + * A Comparator that uses <code>String.compareToIgnoreCase(String)</code>. + * This comparator is {@link Serializable}. Note that it ignores Locale, + * for that, you want a Collator. + * + * @see Collator#compare(String, String) + * @since 1.2 + */ + public static final Comparator CASE_INSENSITIVE_ORDER + = new CaseInsensitiveComparator(); + + /** + * Creates an empty String (length 0). Unless you really need a new object, + * consider using <code>""</code> instead. + */ + public String() + { + value = "".value; + offset = 0; + count = 0; + } + + /** + * Copies the contents of a String to a new String. Since Strings are + * immutable, only a shallow copy is performed. + * + * @param str String to copy + * @throws NullPointerException if value is null + */ + public String(String str) + { + value = str.value; + offset = str.offset; + count = str.count; + cachedHashCode = str.cachedHashCode; + } + + /** + * Creates a new String using the character sequence of the char array. + * Subsequent changes to data do not affect the String. + * + * @param data char array to copy + * @throws NullPointerException if data is null + */ + public String(char[] data) + { + this(data, 0, data.length, false); + } + + /** + * Creates a new String using the character sequence of a subarray of + * characters. The string starts at offset, and copies count chars. + * Subsequent changes to data do not affect the String. + * + * @param data char array to copy + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public String(char[] data, int offset, int count) + { + this(data, offset, count, false); + } + + /** + * Creates a new String using an 8-bit array of integer values, starting at + * an offset, and copying up to the count. Each character c, using + * corresponding byte b, is created in the new String as if by performing: + * + * <pre> + * c = (char) (((hibyte & 0xff) << 8) | (b & 0xff)) + * </pre> + * + * @param ascii array of integer values + * @param hibyte top byte of each Unicode character + * @param offset position (base 0) to start copying out of ascii + * @param count the number of characters from ascii to copy + * @throws NullPointerException if ascii is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count > ascii.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(byte[]) + * @see #String(byte[], String) + * @see #String(byte[], int, int) + * @see #String(byte[], int, int, String) + * @deprecated use {@link #String(byte[], int, int, String)} to perform + * correct encoding + */ + public String(byte[] ascii, int hibyte, int offset, int count) + { + if (offset < 0 || count < 0 || offset + count > ascii.length) + throw new StringIndexOutOfBoundsException(); + value = new char[count]; + this.offset = 0; + this.count = count; + hibyte <<= 8; + offset += count; + while (--count >= 0) + value[count] = (char) (hibyte | (ascii[--offset] & 0xff)); + } + + /** + * Creates a new String using an 8-bit array of integer values. Each + * character c, using corresponding byte b, is created in the new String + * as if by performing: + * + * <pre> + * c = (char) (((hibyte & 0xff) << 8) | (b & 0xff)) + * </pre> + * + * @param ascii array of integer values + * @param hibyte top byte of each Unicode character + * @throws NullPointerException if ascii is null + * @see #String(byte[]) + * @see #String(byte[], String) + * @see #String(byte[], int, int) + * @see #String(byte[], int, int, String) + * @see #String(byte[], int, int, int) + * @deprecated use {@link #String(byte[], String)} to perform + * correct encoding + */ + public String(byte[] ascii, int hibyte) + { + this(ascii, hibyte, 0, ascii.length); + } + + /** + * Creates a new String using the portion of the byte array starting at the + * offset and ending at offset + count. Uses the specified encoding type + * to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. The behavior is not specified if + * the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param offset the offset to start at + * @param count the number of bytes in the array to use + * @param encoding the name of the encoding to use + * @throws NullPointerException if data or encoding is null + * @throws IndexOutOfBoundsException if offset or count is incorrect + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @throws UnsupportedEncodingException if encoding is not found + * @throws Error if the decoding fails + * @since 1.1 + */ + public String(byte[] data, int offset, int count, String encoding) + throws UnsupportedEncodingException + { + if (offset < 0 || count < 0 || offset + count > data.length) + throw new StringIndexOutOfBoundsException(); + try + { + CharsetDecoder csd = Charset.forName(encoding).newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + value = cbuf.array(); + this.offset = cbuf.position(); + this.count = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + value = new char[cbuf.remaining()]; + cbuf.get(value); + this.offset = 0; + this.count = value.length; + } + } catch(CharacterCodingException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } catch(IllegalCharsetNameException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } catch(UnsupportedCharsetException e){ + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } + } + + /** + * Creates a new String using the byte array. Uses the specified encoding + * type to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. The behavior is not specified if + * the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param encoding the name of the encoding to use + * @throws NullPointerException if data or encoding is null + * @throws UnsupportedEncodingException if encoding is not found + * @throws Error if the decoding fails + * @see #String(byte[], int, int, String) + * @since 1.1 + */ + public String(byte[] data, String encoding) + throws UnsupportedEncodingException + { + this(data, 0, data.length, encoding); + } + + /** + * Creates a new String using the portion of the byte array starting at the + * offset and ending at offset + count. Uses the encoding of the platform's + * default charset, so the resulting string may be longer or shorter than + * the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}. The behavior is not specified + * if the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param offset the offset to start at + * @param count the number of bytes in the array to use + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if offset or count is incorrect + * @throws Error if the decoding fails + * @see #String(byte[], int, int, String) + * @since 1.1 + */ + public String(byte[] data, int offset, int count) + { + if (offset < 0 || count < 0 || offset + count > data.length) + throw new StringIndexOutOfBoundsException(); + int o, c; + char[] v; + String encoding; + try + { + encoding = System.getProperty("file.encoding"); + CharsetDecoder csd = Charset.forName(encoding).newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + v = cbuf.array(); + o = cbuf.position(); + c = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + v = new char[cbuf.remaining()]; + cbuf.get(v); + o = 0; + c = v.length; + } + } catch(Exception ex){ + // If anything goes wrong (System property not set, + // NIO provider not available, etc) + // Default to the 'safe' encoding ISO8859_1 + v = new char[count]; + o = 0; + c = count; + for (int i=0;i<count;i++) + v[i] = (char)data[offset+i]; + } + this.value = v; + this.offset = o; + this.count = c; + } + + /** + * Creates a new String using the byte array. Uses the encoding of the + * platform's default charset, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}. The behavior is not specified + * if the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @throws NullPointerException if data is null + * @throws Error if the decoding fails + * @see #String(byte[], int, int) + * @see #String(byte[], int, int, String) + * @since 1.1 + */ + public String(byte[] data) + { + this(data, 0, data.length); + } + + /** + * Creates a new String using the character sequence represented by + * the StringBuffer. Subsequent changes to buf do not affect the String. + * + * @param buffer StringBuffer to copy + * @throws NullPointerException if buffer is null + */ + public String(StringBuffer buffer) + { + synchronized (buffer) + { + offset = 0; + count = buffer.count; + // Share unless buffer is 3/4 empty. + if ((count << 2) < buffer.value.length) + { + value = new char[count]; + VMSystem.arraycopy(buffer.value, 0, value, 0, count); + } + else + { + buffer.shared = true; + value = buffer.value; + } + } + } + + /** + * Creates a new String using the character sequence represented by + * the StringBuilder. Subsequent changes to buf do not affect the String. + * + * @param buffer StringBuilder to copy + * @throws NullPointerException if buffer is null + */ + public String(StringBuilder buffer) + { + this(buffer.value, 0, buffer.count); + } + + /** + * Special constructor which can share an array when safe to do so. + * + * @param data the characters to copy + * @param offset the location to start from + * @param count the number of characters to use + * @param dont_copy true if the array is trusted, and need not be copied + * @throws NullPointerException if chars is null + * @throws StringIndexOutOfBoundsException if bounds check fails + */ + String(char[] data, int offset, int count, boolean dont_copy) + { + if (offset < 0 || count < 0 || offset + count > data.length) + throw new StringIndexOutOfBoundsException(); + if (dont_copy) + { + value = data; + this.offset = offset; + } + else + { + value = new char[count]; + VMSystem.arraycopy(data, offset, value, 0, count); + this.offset = 0; + } + this.count = count; + } + + /** + * Returns the number of characters contained in this String. + * + * @return the length of this String + */ + public int length() + { + return count; + } + + /** + * Returns the character located at the specified index within this String. + * + * @param index position of character to return (base 0) + * @return character located at position index + * @throws IndexOutOfBoundsException if index < 0 || index >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public char charAt(int index) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + return value[offset + index]; + } + + /** + * Copies characters from this String starting at a specified start index, + * ending at a specified stop index, to a character array starting at + * a specified destination begin index. + * + * @param srcBegin index to begin copying characters from this String + * @param srcEnd index after the last character to be copied from this String + * @param dst character array which this String is copied into + * @param dstBegin index to start writing characters into dst + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any indices are out of bounds + * (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dst problems cause an + * ArrayIndexOutOfBoundsException) + */ + public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) + { + if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) + throw new StringIndexOutOfBoundsException(); + VMSystem.arraycopy(value, srcBegin + offset, + dst, dstBegin, srcEnd - srcBegin); + } + + /** + * Copies the low byte of each character from this String starting at a + * specified start index, ending at a specified stop index, to a byte array + * starting at a specified destination begin index. + * + * @param srcBegin index to being copying characters from this String + * @param srcEnd index after the last character to be copied from this String + * @param dst byte array which each low byte of this String is copied into + * @param dstBegin index to start writing characters into dst + * @throws NullPointerException if dst is null and copy length is non-zero + * @throws IndexOutOfBoundsException if any indices are out of bounds + * (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dst problems cause an + * ArrayIndexOutOfBoundsException) + * @see #getBytes() + * @see #getBytes(String) + * @deprecated use {@link #getBytes()}, which uses a char to byte encoder + */ + public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) + { + if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) + throw new StringIndexOutOfBoundsException(); + int i = srcEnd - srcBegin; + srcBegin += offset; + while (--i >= 0) + dst[dstBegin++] = (byte) value[srcBegin++]; + } + + /** + * Converts the Unicode characters in this String to a byte array. Uses the + * specified encoding method, so the result may be longer or shorter than + * the String. For more encoding control, use + * {@link java.nio.charset.CharsetEncoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. Unsupported characters get + * replaced by an encoding specific byte. + * + * @param enc encoding name + * @return the resulting byte array + * @throws NullPointerException if enc is null + * @throws UnsupportedEncodingException if encoding is not supported + * @since 1.1 + */ + public byte[] getBytes(String enc) throws UnsupportedEncodingException + { + try + { + CharsetEncoder cse = Charset.forName(enc).newEncoder(); + cse.onMalformedInput(CodingErrorAction.REPLACE); + cse.onUnmappableCharacter(CodingErrorAction.REPLACE); + ByteBuffer bbuf = cse.encode(CharBuffer.wrap(value, offset, count)); + if(bbuf.hasArray()) + return bbuf.array(); + + // Doubt this will happen. But just in case. + byte[] bytes = new byte[bbuf.remaining()]; + bbuf.get(bytes); + return bytes; + + } catch(IllegalCharsetNameException e){ + throw new UnsupportedEncodingException("Encoding: "+enc+ + " not found."); + } catch(UnsupportedCharsetException e){ + throw new UnsupportedEncodingException("Encoding: "+enc+ + " not found."); + } catch(CharacterCodingException e){ + // XXX - Ignore coding exceptions? They shouldn't really happen. + return null; + } + } + + /** + * Converts the Unicode characters in this String to a byte array. Uses the + * encoding of the platform's default charset, so the result may be longer + * or shorter than the String. For more encoding control, use + * {@link java.nio.charset.CharsetEncoder}. Unsupported characters get + * replaced by an encoding specific byte. + * + * @return the resulting byte array, or null on a problem + * @since 1.1 + */ + public byte[] getBytes() + { + try + { + return getBytes(System.getProperty("file.encoding")); + } catch(Exception e) { + // XXX - Throw an error here? + // For now, default to the 'safe' encoding. + byte[] bytes = new byte[count]; + for(int i=0;i<count;i++) + bytes[i] = (byte)((value[offset+i] <= 0xFF)? + value[offset+i]:'?'); + return bytes; + } + } + + /** + * Predicate which compares anObject to this. This is true only for Strings + * with the same character sequence. + * + * @param anObject the object to compare + * @return true if anObject is semantically equal to this + * @see #compareTo(String) + * @see #equalsIgnoreCase(String) + */ + public boolean equals(Object anObject) + { + if (! (anObject instanceof String)) + return false; + String str2 = (String) anObject; + if (count != str2.count) + return false; + if (value == str2.value && offset == str2.offset) + return true; + int i = count; + int x = offset; + int y = str2.offset; + while (--i >= 0) + if (value[x++] != str2.value[y++]) + return false; + return true; + } + + /** + * Compares the given StringBuffer to this String. This is true if the + * StringBuffer has the same content as this String at this moment. + * + * @param buffer the StringBuffer to compare to + * @return true if StringBuffer has the same character sequence + * @throws NullPointerException if the given StringBuffer is null + * @since 1.4 + */ + public boolean contentEquals(StringBuffer buffer) + { + synchronized (buffer) + { + if (count != buffer.count) + return false; + if (value == buffer.value) + return true; // Possible if shared. + int i = count; + int x = offset + count; + while (--i >= 0) + if (value[--x] != buffer.value[i]) + return false; + return true; + } + } + + /** + * Compares a String to this String, ignoring case. This does not handle + * multi-character capitalization exceptions; instead the comparison is + * made on a character-by-character basis, and is true if:<br><ul> + * <li><code>c1 == c2</code></li> + * <li><code>Character.toUpperCase(c1) + * == Character.toUpperCase(c2)</code></li> + * <li><code>Character.toLowerCase(c1) + * == Character.toLowerCase(c2)</code></li> + * </ul> + * + * @param anotherString String to compare to this String + * @return true if anotherString is equal, ignoring case + * @see #equals(Object) + * @see Character#toUpperCase(char) + * @see Character#toLowerCase(char) + */ + public boolean equalsIgnoreCase(String anotherString) + { + if (anotherString == null || count != anotherString.count) + return false; + int i = count; + int x = offset; + int y = anotherString.offset; + while (--i >= 0) + { + char c1 = value[x++]; + char c2 = anotherString.value[y++]; + // Note that checking c1 != c2 is redundant, but avoids method calls. + if (c1 != c2 + && Character.toUpperCase(c1) != Character.toUpperCase(c2) + && Character.toLowerCase(c1) != Character.toLowerCase(c2)) + return false; + } + return true; + } + + /** + * Compares this String and another String (case sensitive, + * lexicographically). The result is less than 0 if this string sorts + * before the other, 0 if they are equal, and greater than 0 otherwise. + * After any common starting sequence is skipped, the result is + * <code>this.charAt(k) - anotherString.charAt(k)</code> if both strings + * have characters remaining, or + * <code>this.length() - anotherString.length()</code> if one string is + * a subsequence of the other. + * + * @param anotherString the String to compare against + * @return the comparison + * @throws NullPointerException if anotherString is null + */ + public int compareTo(String anotherString) + { + int i = Math.min(count, anotherString.count); + int x = offset; + int y = anotherString.offset; + while (--i >= 0) + { + int result = value[x++] - anotherString.value[y++]; + if (result != 0) + return result; + } + return count - anotherString.count; + } + + /** + * Behaves like <code>compareTo(java.lang.String)</code> unless the Object + * is not a <code>String</code>. Then it throws a + * <code>ClassCastException</code>. + * + * @param o the object to compare against + * @return the comparison + * @throws NullPointerException if o is null + * @throws ClassCastException if o is not a <code>String</code> + * @since 1.2 + */ + public int compareTo(Object o) + { + return compareTo((String) o); + } + + /** + * Compares this String and another String (case insensitive). This + * comparison is <em>similar</em> to equalsIgnoreCase, in that it ignores + * locale and multi-characater capitalization, and compares characters + * after performing + * <code>Character.toLowerCase(Character.toUpperCase(c))</code> on each + * character of the string. This is unsatisfactory for locale-based + * comparison, in which case you should use {@link java.text.Collator}. + * + * @param str the string to compare against + * @return the comparison + * @see Collator#compare(String, String) + * @since 1.2 + */ + public int compareToIgnoreCase(String str) + { + int i = Math.min(count, str.count); + int x = offset; + int y = str.offset; + while (--i >= 0) + { + int result = Character.toLowerCase(Character.toUpperCase(value[x++])) + - Character.toLowerCase(Character.toUpperCase(str.value[y++])); + if (result != 0) + return result; + } + return count - str.count; + } + + /** + * Predicate which determines if this String matches another String + * starting at a specified offset for each String and continuing + * for a specified length. Indices out of bounds are harmless, and give + * a false result. + * + * @param toffset index to start comparison at for this String + * @param other String to compare region to this String + * @param ooffset index to start comparison at for other + * @param len number of characters to compare + * @return true if regions match (case sensitive) + * @throws NullPointerException if other is null + */ + public boolean regionMatches(int toffset, String other, int ooffset, int len) + { + return regionMatches(false, toffset, other, ooffset, len); + } + + /** + * Predicate which determines if this String matches another String + * starting at a specified offset for each String and continuing + * for a specified length, optionally ignoring case. Indices out of bounds + * are harmless, and give a false result. Case comparisons are based on + * <code>Character.toLowerCase()</code> and + * <code>Character.toUpperCase()</code>, not on multi-character + * capitalization expansions. + * + * @param ignoreCase true if case should be ignored in comparision + * @param toffset index to start comparison at for this String + * @param other String to compare region to this String + * @param ooffset index to start comparison at for other + * @param len number of characters to compare + * @return true if regions match, false otherwise + * @throws NullPointerException if other is null + */ + public boolean regionMatches(boolean ignoreCase, int toffset, + String other, int ooffset, int len) + { + if (toffset < 0 || ooffset < 0 || toffset + len > count + || ooffset + len > other.count) + return false; + toffset += offset; + ooffset += other.offset; + while (--len >= 0) + { + char c1 = value[toffset++]; + char c2 = other.value[ooffset++]; + // Note that checking c1 != c2 is redundant when ignoreCase is true, + // but it avoids method calls. + if (c1 != c2 + && (! ignoreCase + || (Character.toLowerCase(c1) != Character.toLowerCase(c2) + && (Character.toUpperCase(c1) + != Character.toUpperCase(c2))))) + return false; + } + return true; + } + + /** + * Predicate which determines if this String contains the given prefix, + * beginning comparison at toffset. The result is false if toffset is + * negative or greater than this.length(), otherwise it is the same as + * <code>this.substring(toffset).startsWith(prefix)</code>. + * + * @param prefix String to compare + * @param toffset offset for this String where comparison starts + * @return true if this String starts with prefix + * @throws NullPointerException if prefix is null + * @see #regionMatches(boolean, int, String, int, int) + */ + public boolean startsWith(String prefix, int toffset) + { + return regionMatches(false, toffset, prefix, 0, prefix.count); + } + + /** + * Predicate which determines if this String starts with a given prefix. + * If the prefix is an empty String, true is returned. + * + * @param prefix String to compare + * @return true if this String starts with the prefix + * @throws NullPointerException if prefix is null + * @see #startsWith(String, int) + */ + public boolean startsWith(String prefix) + { + return regionMatches(false, 0, prefix, 0, prefix.count); + } + + /** + * Predicate which determines if this String ends with a given suffix. + * If the suffix is an empty String, true is returned. + * + * @param suffix String to compare + * @return true if this String ends with the suffix + * @throws NullPointerException if suffix is null + * @see #regionMatches(boolean, int, String, int, int) + */ + public boolean endsWith(String suffix) + { + return regionMatches(false, count - suffix.count, suffix, 0, suffix.count); + } + + /** + * Computes the hashcode for this String. This is done with int arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[0]*31**(n-1) + s[1]*31**(n-2) + ... + s[n-1]</code>. + * + * @return hashcode value of this String + */ + public int hashCode() + { + if (cachedHashCode != 0) + return cachedHashCode; + + // Compute the hash code using a local variable to be reentrant. + int hashCode = 0; + int limit = count + offset; + for (int i = offset; i < limit; i++) + hashCode = hashCode * 31 + value[i]; + return cachedHashCode = hashCode; + } + + /** + * Finds the first instance of a character in this String. + * + * @param ch character to find + * @return location (base 0) of the character, or -1 if not found + */ + public int indexOf(int ch) + { + return indexOf(ch, 0); + } + + /** + * Finds the first instance of a character in this String, starting at + * a given index. If starting index is less than 0, the search + * starts at the beginning of this String. If the starting index + * is greater than the length of this String, -1 is returned. + * + * @param ch character to find + * @param fromIndex index to start the search + * @return location (base 0) of the character, or -1 if not found + */ + public int indexOf(int ch, int fromIndex) + { + if ((char) ch != ch) + return -1; + if (fromIndex < 0) + fromIndex = 0; + int i = fromIndex + offset; + for ( ; fromIndex < count; fromIndex++) + if (value[i++] == ch) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a character in this String. + * + * @param ch character to find + * @return location (base 0) of the character, or -1 if not found + */ + public int lastIndexOf(int ch) + { + return lastIndexOf(ch, count - 1); + } + + /** + * Finds the last instance of a character in this String, starting at + * a given index. If starting index is greater than the maximum valid + * index, then the search begins at the end of this String. If the + * starting index is less than zero, -1 is returned. + * + * @param ch character to find + * @param fromIndex index to start the search + * @return location (base 0) of the character, or -1 if not found + */ + public int lastIndexOf(int ch, int fromIndex) + { + if ((char) ch != ch) + return -1; + if (fromIndex >= count) + fromIndex = count - 1; + int i = fromIndex + offset; + for ( ; fromIndex >= 0; fromIndex--) + if (value[i--] == ch) + return fromIndex; + return -1; + } + + /** + * Finds the first instance of a String in this String. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int indexOf(String str) + { + return indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this String, starting at + * a given index. If starting index is less than 0, the search + * starts at the beginning of this String. If the starting index + * is greater than the length of this String, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int indexOf(String str, int fromIndex) + { + if (fromIndex < 0) + fromIndex = 0; + int limit = count - str.count; + for ( ; fromIndex <= limit; fromIndex++) + if (regionMatches(fromIndex, str, 0, str.count)) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a String in this String. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int lastIndexOf(String str) + { + return lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this String, starting at + * a given index. If starting index is greater than the maximum valid + * index, then the search begins at the end of this String. If the + * starting index is less than zero, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int lastIndexOf(String str, int fromIndex) + { + fromIndex = Math.min(fromIndex, count - str.count); + for ( ; fromIndex >= 0; fromIndex--) + if (regionMatches(fromIndex, str, 0, str.count)) + return fromIndex; + return -1; + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at the end of this String. + * + * @param begin index to start substring (base 0) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || begin > length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public String substring(int begin) + { + return substring(begin, count); + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start substring (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || end > length() + * || begin > end (while unspecified, this is a + * StringIndexOutOfBoundsException) + */ + public String substring(int beginIndex, int endIndex) + { + if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) + throw new StringIndexOutOfBoundsException(); + if (beginIndex == 0 && endIndex == count) + return this; + int len = endIndex - beginIndex; + // Package constructor avoids an array copy. + return new String(value, beginIndex + offset, len, + (len << 2) >= value.length); + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at one character before a specified index. This behaves like + * <code>substring(begin, end)</code>. + * + * @param begin index to start substring (inclusive, base 0) + * @param end index to end at (exclusive) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || end > length() + * || begin > end + * @since 1.4 + */ + public CharSequence subSequence(int begin, int end) + { + return substring(begin, end); + } + + /** + * Concatenates a String to this String. This results in a new string unless + * one of the two originals is "". + * + * @param str String to append to this String + * @return newly concatenated String + * @throws NullPointerException if str is null + */ + public String concat(String str) + { + if (str.count == 0) + return this; + if (count == 0) + return str; + char[] newStr = new char[count + str.count]; + VMSystem.arraycopy(value, offset, newStr, 0, count); + VMSystem.arraycopy(str.value, str.offset, newStr, count, str.count); + // Package constructor avoids an array copy. + return new String(newStr, 0, newStr.length, true); + } + + /** + * Replaces every instance of a character in this String with a new + * character. If no replacements occur, this is returned. + * + * @param oldChar the old character to replace + * @param newChar the new character + * @return new String with all instances of oldChar replaced with newChar + */ + public String replace(char oldChar, char newChar) + { + if (oldChar == newChar) + return this; + int i = count; + int x = offset - 1; + while (--i >= 0) + if (value[++x] == oldChar) + break; + if (i < 0) + return this; + char[] newStr = (char[]) value.clone(); + newStr[x] = newChar; + while (--i >= 0) + if (value[++x] == oldChar) + newStr[x] = newChar; + // Package constructor avoids an array copy. + return new String(newStr, offset, count, true); + } + + /** + * Test if this String matches a regular expression. This is shorthand for + * <code>{@link Pattern}.matches(regex, this)</code>. + * + * @param regex the pattern to match + * @return true if the pattern matches + * @throws NullPointerException if regex is null + * @throws PatternSyntaxException if regex is invalid + * @see Pattern#matches(String, CharSequence) + * @since 1.4 + */ + public boolean matches(String regex) + { + return Pattern.matches(regex, this); + } + + /** + * Replaces the first substring match of the regular expression with a + * given replacement. This is shorthand for <code>{@link Pattern} + * .compile(regex).matcher(this).replaceFirst(replacement)</code>. + * + * @param regex the pattern to match + * @param replacement the replacement string + * @return the modified string + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #replaceAll(String, String) + * @see Pattern#compile(String) + * @see Pattern#matcher(CharSequence) + * @see Matcher#replaceFirst(String) + * @since 1.4 + */ + public String replaceFirst(String regex, String replacement) + { + return Pattern.compile(regex).matcher(this).replaceFirst(replacement); + } + + /** + * Replaces all matching substrings of the regular expression with a + * given replacement. This is shorthand for <code>{@link Pattern} + * .compile(regex).matcher(this).replaceAll(replacement)</code>. + * + * @param regex the pattern to match + * @param replacement the replacement string + * @return the modified string + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #replaceFirst(String, String) + * @see Pattern#compile(String) + * @see Pattern#matcher(CharSequence) + * @see Matcher#replaceAll(String) + * @since 1.4 + */ + public String replaceAll(String regex, String replacement) + { + return Pattern.compile(regex).matcher(this).replaceAll(replacement); + } + + /** + * Split this string around the matches of a regular expression. Each + * element of the returned array is the largest block of characters not + * terminated by the regular expression, in the order the matches are found. + * + * <p>The limit affects the length of the array. If it is positive, the + * array will contain at most n elements (n - 1 pattern matches). If + * negative, the array length is unlimited, but there can be trailing empty + * entries. if 0, the array length is unlimited, and trailing empty entries + * are discarded. + * + * <p>For example, splitting "boo:and:foo" yields:<br> + * <table border=0> + * <th><td>Regex</td> <td>Limit</td> <td>Result</td></th> + * <tr><td>":"</td> <td>2</td> <td>{ "boo", "and:foo" }</td></tr> + * <tr><td>":"</td> <td>t</td> <td>{ "boo", "and", "foo" }</td></tr> + * <tr><td>":"</td> <td>-2</td> <td>{ "boo", "and", "foo" }</td></tr> + * <tr><td>"o"</td> <td>5</td> <td>{ "b", "", ":and:f", "", "" }</td></tr> + * <tr><td>"o"</td> <td>-2</td> <td>{ "b", "", ":and:f", "", "" }</td></tr> + * <tr><td>"o"</td> <td>0</td> <td>{ "b", "", ":and:f" }</td></tr> + * </table> + * + * <p>This is shorthand for + * <code>{@link Pattern}.compile(regex).split(this, limit)</code>. + * + * @param regex the pattern to match + * @param limit the limit threshold + * @return the array of split strings + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see Pattern#compile(String) + * @see Pattern#split(CharSequence, int) + * @since 1.4 + */ + public String[] split(String regex, int limit) + { + return Pattern.compile(regex).split(this, limit); + } + + /** + * Split this string around the matches of a regular expression. Each + * element of the returned array is the largest block of characters not + * terminated by the regular expression, in the order the matches are found. + * The array length is unlimited, and trailing empty entries are discarded, + * as though calling <code>split(regex, 0)</code>. + * + * @param regex the pattern to match + * @return the array of split strings + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #split(String, int) + * @see Pattern#compile(String) + * @see Pattern#split(CharSequence, int) + * @since 1.4 + */ + public String[] split(String regex) + { + return Pattern.compile(regex).split(this, 0); + } + + /** + * Lowercases this String according to a particular locale. This uses + * Unicode's special case mappings, as applied to the given Locale, so the + * resulting string may be a different length. + * + * @param loc locale to use + * @return new lowercased String, or this if no characters were lowercased + * @throws NullPointerException if loc is null + * @see #toUpperCase(Locale) + * @since 1.1 + */ + public String toLowerCase(Locale loc) + { + // First, see if the current string is already lower case. + boolean turkish = "tr".equals(loc.getLanguage()); + int i = count; + int x = offset - 1; + while (--i >= 0) + { + char ch = value[++x]; + if ((turkish && ch == '\u0049') + || ch != Character.toLowerCase(ch)) + break; + } + if (i < 0) + return this; + + // Now we perform the conversion. Fortunately, there are no multi-character + // lowercase expansions in Unicode 3.0.0. + char[] newStr = (char[]) value.clone(); + do + { + char ch = value[x]; + // Hardcoded special case. + newStr[x++] = (turkish && ch == '\u0049') ? '\u0131' + : Character.toLowerCase(ch); + } + while (--i >= 0); + // Package constructor avoids an array copy. + return new String(newStr, offset, count, true); + } + + /** + * Lowercases this String. This uses Unicode's special case mappings, as + * applied to the platform's default Locale, so the resulting string may + * be a different length. + * + * @return new lowercased String, or this if no characters were lowercased + * @see #toLowerCase(Locale) + * @see #toUpperCase() + */ + public String toLowerCase() + { + return toLowerCase(Locale.getDefault()); + } + + /** + * Uppercases this String according to a particular locale. This uses + * Unicode's special case mappings, as applied to the given Locale, so the + * resulting string may be a different length. + * + * @param loc locale to use + * @return new uppercased String, or this if no characters were uppercased + * @throws NullPointerException if loc is null + * @see #toLowerCase(Locale) + * @since 1.1 + */ + public String toUpperCase(Locale loc) + { + // First, see how many characters we have to grow by, as well as if the + // current string is already upper case. + boolean turkish = "tr".equals(loc.getLanguage()); + int expand = 0; + boolean unchanged = true; + int i = count; + int x = i + offset; + while (--i >= 0) + { + char ch = value[--x]; + expand += upperCaseExpansion(ch); + unchanged = (unchanged && expand == 0 + && ! (turkish && ch == '\u0069') + && ch == Character.toUpperCase(ch)); + } + if (unchanged) + return this; + + // Now we perform the conversion. + i = count; + if (expand == 0) + { + char[] newStr = (char[]) value.clone(); + while (--i >= 0) + { + char ch = value[x]; + // Hardcoded special case. + newStr[x++] = (turkish && ch == '\u0069') ? '\u0130' + : Character.toUpperCase(ch); + } + // Package constructor avoids an array copy. + return new String(newStr, offset, count, true); + } + + // Expansion is necessary. + char[] newStr = new char[count + expand]; + int j = 0; + while (--i >= 0) + { + char ch = value[x++]; + // Hardcoded special case. + if (turkish && ch == '\u0069') + { + newStr[j++] = '\u0130'; + continue; + } + expand = upperCaseExpansion(ch); + if (expand > 0) + { + int index = upperCaseIndex(ch); + while (expand-- >= 0) + newStr[j++] = upperExpand[index++]; + } + else + newStr[j++] = Character.toUpperCase(ch); + } + // Package constructor avoids an array copy. + return new String(newStr, 0, newStr.length, true); + } + + /** + * Uppercases this String. This uses Unicode's special case mappings, as + * applied to the platform's default Locale, so the resulting string may + * be a different length. + * + * @return new uppercased String, or this if no characters were uppercased + * @see #toUpperCase(Locale) + * @see #toLowerCase() + */ + public String toUpperCase() + { + return toUpperCase(Locale.getDefault()); + } + + /** + * Trims all characters less than or equal to <code>'\u0020'</code> + * (<code>' '</code>) from the beginning and end of this String. This + * includes many, but not all, ASCII control characters, and all + * {@link Character#isWhitespace(char)}. + * + * @return new trimmed String, or this if nothing trimmed + */ + public String trim() + { + int limit = count + offset; + if (count == 0 || (value[offset] > '\u0020' + && value[limit - 1] > '\u0020')) + return this; + int begin = offset; + do + if (begin == limit) + return ""; + while (value[begin++] <= '\u0020'); + int end = limit; + while (value[--end] <= '\u0020'); + return substring(begin - offset - 1, end - offset + 1); + } + + /** + * Returns this, as it is already a String! + * + * @return this + */ + public String toString() + { + return this; + } + + /** + * Copies the contents of this String into a character array. Subsequent + * changes to the array do not affect the String. + * + * @return character array copying the String + */ + public char[] toCharArray() + { + if (count == value.length) + return (char[]) value.clone(); + + char[] copy = new char[count]; + VMSystem.arraycopy(value, offset, copy, 0, count); + return copy; + } + + /** + * Returns a String representation of an Object. This is "null" if the + * object is null, otherwise it is <code>obj.toString()</code> (which + * can be null). + * + * @param obj the Object + * @return the string conversion of obj + */ + public static String valueOf(Object obj) + { + return obj == null ? "null" : obj.toString(); + } + + /** + * Returns a String representation of a character array. Subsequent + * changes to the array do not affect the String. + * + * @param data the character array + * @return a String containing the same character sequence as data + * @throws NullPointerException if data is null + * @see #valueOf(char[], int, int) + * @see #String(char[]) + */ + public static String valueOf(char[] data) + { + return valueOf (data, 0, data.length); + } + + /** + * Returns a String representing the character sequence of the char array, + * starting at the specified offset, and copying chars up to the specified + * count. Subsequent changes to the array do not affect the String. + * + * @param data character array + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @return String containing the chars from data[offset..offset+count] + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(char[], int, int) + */ + public static String valueOf(char[] data, int offset, int count) + { + return new String(data, offset, count, false); + } + + /** + * Returns a String representing the character sequence of the char array, + * starting at the specified offset, and copying chars up to the specified + * count. Subsequent changes to the array do not affect the String. + * + * @param data character array + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @return String containing the chars from data[offset..offset+count] + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(char[], int, int) + */ + public static String copyValueOf(char[] data, int offset, int count) + { + return new String(data, offset, count, false); + } + + /** + * Returns a String representation of a character array. Subsequent + * changes to the array do not affect the String. + * + * @param data the character array + * @return a String containing the same character sequence as data + * @throws NullPointerException if data is null + * @see #copyValueOf(char[], int, int) + * @see #String(char[]) + */ + public static String copyValueOf(char[] data) + { + return copyValueOf (data, 0, data.length); + } + + /** + * Returns a String representing a boolean. + * + * @param b the boolean + * @return "true" if b is true, else "false" + */ + public static String valueOf(boolean b) + { + return b ? "true" : "false"; + } + + /** + * Returns a String representing a character. + * + * @param c the character + * @return String containing the single character c + */ + public static String valueOf(char c) + { + // Package constructor avoids an array copy. + return new String(new char[] { c }, 0, 1, true); + } + + /** + * Returns a String representing an integer. + * + * @param i the integer + * @return String containing the integer in base 10 + * @see Integer#toString(int) + */ + public static String valueOf(int i) + { + // See Integer to understand why we call the two-arg variant. + return Integer.toString(i, 10); + } + + /** + * Returns a String representing a long. + * + * @param l the long + * @return String containing the long in base 10 + * @see Long#toString(long) + */ + public static String valueOf(long l) + { + return Long.toString(l); + } + + /** + * Returns a String representing a float. + * + * @param f the float + * @return String containing the float + * @see Float#toString(float) + */ + public static String valueOf(float f) + { + return Float.toString(f); + } + + /** + * Returns a String representing a double. + * + * @param d the double + * @return String containing the double + * @see Double#toString(double) + */ + public static String valueOf(double d) + { + return Double.toString(d); + } + + /** + * If two Strings are considered equal, by the equals() method, + * then intern() will return the same String instance. ie. + * if (s1.equals(s2)) then (s1.intern() == s2.intern()). + * All string literals and string-valued constant expressions + * are already interned. + * + * @return the interned String + */ + public String intern() + { + return VMString.intern(this); + } + + /** + * Helper function used to detect which characters have a multi-character + * uppercase expansion. Note that this is only used in locations which + * track one-to-many capitalization (java.lang.Character does not do this). + * As of Unicode 3.0.0, the result is limited in the range 0 to 2, as the + * longest uppercase expansion is three characters (a growth of 2 from the + * lowercase character). + * + * @param ch the char to check + * @return the number of characters to add when converting to uppercase + * @see CharData#DIRECTION + * @see CharData#UPPER_SPECIAL + * @see #toUpperCase(Locale) + */ + private static int upperCaseExpansion(char ch) + { + return Character.direction[Character.readChar(ch) >> 7] & 3; + } + + /** + * Helper function used to locate the offset in upperExpand given a + * character with a multi-character expansion. The binary search is + * optimized under the assumption that this method will only be called on + * characters which exist in upperSpecial. + * + * @param ch the char to check + * @return the index where its expansion begins + * @see CharData#UPPER_SPECIAL + * @see CharData#UPPER_EXPAND + * @see #toUpperCase(Locale) + */ + private static int upperCaseIndex(char ch) + { + // Simple binary search for the correct character. + int low = 0; + int hi = upperSpecial.length - 2; + int mid = ((low + hi) >> 2) << 1; + char c = upperSpecial[mid]; + while (ch != c) + { + if (ch < c) + hi = mid - 2; + else + low = mid + 2; + mid = ((low + hi) >> 2) << 1; + c = upperSpecial[mid]; + } + return upperSpecial[mid + 1]; + } + + /** + * Returns the value array of the given string if it is zero based or a + * copy of it that is zero based (stripping offset and making length equal + * to count). Used for accessing the char[]s of gnu.java.lang.CharData. + * Package private for use in Character. + */ + static char[] zeroBasedStringValue(String s) + { + char[] value; + + if (s.offset == 0 && s.count == s.value.length) + value = s.value; + else + { + int count = s.count; + value = new char[count]; + VMSystem.arraycopy(s.value, s.offset, value, 0, count); + } + + return value; + } +} diff --git a/libjava/classpath/java/lang/StringBuffer.java b/libjava/classpath/java/lang/StringBuffer.java new file mode 100644 index 00000000000..94dec4878cb --- /dev/null +++ b/libjava/classpath/java/lang/StringBuffer.java @@ -0,0 +1,931 @@ +/* StringBuffer.java -- Growable strings + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; + +/** + * <code>StringBuffer</code> represents a changeable <code>String</code>. + * It provides the operations required to modify the + * <code>StringBuffer</code>, including insert, replace, delete, append, + * and reverse. It is thread-safe; meaning that all modifications to a buffer + * are in synchronized methods. + * + * <p><code>StringBuffer</code>s are variable-length in nature, so even if + * you initialize them to a certain size, they can still grow larger than + * that. <em>Capacity</em> indicates the number of characters the + * <code>StringBuffer</code> can have in it before it has to grow (growing + * the char array is an expensive operation involving <code>new</code>). + * + * <p>Incidentally, compilers often implement the String operator "+" + * by using a <code>StringBuffer</code> operation:<br> + * <code>a + b</code><br> + * is the same as<br> + * <code>new StringBuffer().append(a).append(b).toString()</code>. + * + * <p>Classpath's StringBuffer is capable of sharing memory with Strings for + * efficiency. This will help when a StringBuffer is converted to a String + * and the StringBuffer is not changed after that (quite common when performing + * string concatenation). + * + * @author Paul Fisher + * @author John Keiser + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @see String + * @since 1.0 + * @status updated to 1.4 + */ +public final class StringBuffer implements Serializable, CharSequence +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3388685877147921107L; + + /** + * Index of next available character (and thus the size of the current + * string contents). Note that this has permissions set this way so that + * String can get the value. + * + * @serial the number of characters in the buffer + */ + int count; + + /** + * The buffer. Note that this has permissions set this way so that String + * can get the value. + * + * @serial the buffer + */ + char[] value; + + /** + * True if the buffer is shared with another object (StringBuffer or + * String); this means the buffer must be copied before writing to it again. + * Note that this has permissions set this way so that String can get the + * value. + * + * @serial whether the buffer is shared + */ + boolean shared; + + /** + * The default capacity of a buffer. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * Create a new StringBuffer with default capacity 16. + */ + public StringBuffer() + { + this(DEFAULT_CAPACITY); + } + + /** + * Create an empty <code>StringBuffer</code> with the specified initial + * capacity. + * + * @param capacity the initial capacity + * @throws NegativeArraySizeException if capacity is negative + */ + public StringBuffer(int capacity) + { + value = new char[capacity]; + } + + /** + * Create a new <code>StringBuffer</code> with the characters in the + * specified <code>String</code>. Initial capacity will be the size of the + * String plus 16. + * + * @param str the <code>String</code> to convert + * @throws NullPointerException if str is null + */ + public StringBuffer(String str) + { + // Unfortunately, because the size is 16 larger, we cannot share. + count = str.count; + value = new char[count + DEFAULT_CAPACITY]; + str.getChars(0, count, value, 0); + } + + /** + * Get the length of the <code>String</code> this <code>StringBuffer</code> + * would create. Not to be confused with the <em>capacity</em> of the + * <code>StringBuffer</code>. + * + * @return the length of this <code>StringBuffer</code> + * @see #capacity() + * @see #setLength(int) + */ + public synchronized int length() + { + return count; + } + + /** + * Get the total number of characters this <code>StringBuffer</code> can + * support before it must be grown. Not to be confused with <em>length</em>. + * + * @return the capacity of this <code>StringBuffer</code> + * @see #length() + * @see #ensureCapacity(int) + */ + public synchronized int capacity() + { + return value.length; + } + + /** + * Increase the capacity of this <code>StringBuffer</code>. This will + * ensure that an expensive growing operation will not occur until + * <code>minimumCapacity</code> is reached. The buffer is grown to the + * larger of <code>minimumCapacity</code> and + * <code>capacity() * 2 + 2</code>, if it is not already large enough. + * + * @param minimumCapacity the new capacity + * @see #capacity() + */ + public synchronized void ensureCapacity(int minimumCapacity) + { + ensureCapacity_unsynchronized(minimumCapacity); + } + + /** + * Set the length of this StringBuffer. If the new length is greater than + * the current length, all the new characters are set to '\0'. If the new + * length is less than the current length, the first <code>newLength</code> + * characters of the old array will be preserved, and the remaining + * characters are truncated. + * + * @param newLength the new length + * @throws IndexOutOfBoundsException if the new length is negative + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #length() + */ + public synchronized void setLength(int newLength) + { + if (newLength < 0) + throw new StringIndexOutOfBoundsException(newLength); + + int valueLength = value.length; + + /* Always call ensureCapacity_unsynchronized in order to preserve + copy-on-write semantics. */ + ensureCapacity_unsynchronized(newLength); + + if (newLength < valueLength) + { + /* If the StringBuffer's value just grew, then we know that + value is newly allocated and the region between count and + newLength is filled with '\0'. */ + count = newLength; + } + else + { + /* The StringBuffer's value doesn't need to grow. However, + we should clear out any cruft that may exist. */ + while (count < newLength) + value[count++] = '\0'; + } + } + + /** + * Get the character at the specified index. + * + * @param index the index of the character to get, starting at 0 + * @return the character at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized char charAt(int index) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + return value[index]; + } + + /** + * Get the specified array of characters. <code>srcOffset - srcEnd</code> + * characters will be copied into the array you pass in. + * + * @param srcOffset the index to start copying from (inclusive) + * @param srcEnd the index to stop copying from (exclusive) + * @param dst the array to copy into + * @param dstOffset the index to start copying into + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any source or target indices are + * out of range (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dest problems cause an + * ArrayIndexOutOfBoundsException) + * @see System#arraycopy(Object, int, Object, int, int) + */ + public synchronized void getChars(int srcOffset, int srcEnd, + char[] dst, int dstOffset) + { + if (srcOffset < 0 || srcEnd > count || srcEnd < srcOffset) + throw new StringIndexOutOfBoundsException(); + VMSystem.arraycopy(value, srcOffset, dst, dstOffset, srcEnd - srcOffset); + } + + /** + * Set the character at the specified index. + * + * @param index the index of the character to set starting at 0 + * @param ch the value to set that character to + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized void setCharAt(int index, char ch) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity_unsynchronized(count); + value[index] = ch; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param obj the <code>Object</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(Object) + * @see #append(String) + */ + public StringBuffer append(Object obj) + { + return append(obj == null ? "null" : obj.toString()); + } + + /** + * Append the <code>String</code> to this <code>StringBuffer</code>. If + * str is null, the String "null" is appended. + * + * @param str the <code>String</code> to append + * @return this <code>StringBuffer</code> + */ + public synchronized StringBuffer append(String str) + { + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity_unsynchronized(count + len); + str.getChars(0, len, value, count); + count += len; + return this; + } + + /** + * Append the <code>StringBuffer</code> value of the argument to this + * <code>StringBuffer</code>. This behaves the same as + * <code>append((Object) stringBuffer)</code>, except it is more efficient. + * + * @param stringBuffer the <code>StringBuffer</code> to convert and append + * @return this <code>StringBuffer</code> + * @see #append(Object) + * @since 1.4 + */ + public synchronized StringBuffer append(StringBuffer stringBuffer) + { + if (stringBuffer == null) + return append("null"); + synchronized (stringBuffer) + { + int len = stringBuffer.count; + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(stringBuffer.value, 0, value, count, len); + count += len; + } + return this; + } + + /** + * Append the <code>char</code> array to this <code>StringBuffer</code>. + * This is similar (but more efficient) than + * <code>append(new String(data))</code>, except in the case of null. + * + * @param data the <code>char[]</code> to append + * @return this <code>StringBuffer</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @see #append(char[], int, int) + */ + public StringBuffer append(char[] data) + { + return append(data, 0, data.length); + } + + /** + * Append part of the <code>char</code> array to this + * <code>StringBuffer</code>. This is similar (but more efficient) than + * <code>append(new String(data, offset, count))</code>, except in the case + * of null. + * + * @param data the <code>char[]</code> to append + * @param offset the start location in <code>str</code> + * @param count the number of characters to get from <code>str</code> + * @return this <code>StringBuffer</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @throws IndexOutOfBoundsException if offset or count is out of range + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized StringBuffer append(char[] data, int offset, int count) + { + if (offset < 0 || count < 0 || offset > data.length - count) + throw new StringIndexOutOfBoundsException(); + ensureCapacity_unsynchronized(this.count + count); + VMSystem.arraycopy(data, offset, value, this.count, count); + this.count += count; + return this; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param bool the <code>boolean</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(boolean) + */ + public StringBuffer append(boolean bool) + { + return append(bool ? "true" : "false"); + } + + /** + * Append the <code>char</code> to this <code>StringBuffer</code>. + * + * @param ch the <code>char</code> to append + * @return this <code>StringBuffer</code> + */ + public synchronized StringBuffer append(char ch) + { + ensureCapacity_unsynchronized(count + 1); + value[count++] = ch; + return this; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param inum the <code>int</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(int) + */ + // This is native in libgcj, for efficiency. + public StringBuffer append(int inum) + { + return append(String.valueOf(inum)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param lnum the <code>long</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(long) + */ + public StringBuffer append(long lnum) + { + return append(Long.toString(lnum, 10)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param fnum the <code>float</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(float) + */ + public StringBuffer append(float fnum) + { + return append(Float.toString(fnum)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param dnum the <code>double</code> to convert and append + * @return this <code>StringBuffer</code> + * @see String#valueOf(double) + */ + public StringBuffer append(double dnum) + { + return append(Double.toString(dnum)); + } + + /** + * Delete characters from this <code>StringBuffer</code>. + * <code>delete(10, 12)</code> will delete 10 and 11, but not 12. It is + * harmless for end to be larger than length(). + * + * @param start the first character to delete + * @param end the index after the last character to delete + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @since 1.2 + */ + public synchronized StringBuffer delete(int start, int end) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + if (end > count) + end = count; + // This will unshare if required. + ensureCapacity_unsynchronized(count); + if (count - end != 0) + VMSystem.arraycopy(value, end, value, start, count - end); + count -= end - start; + return this; + } + + /** + * Delete a character from this <code>StringBuffer</code>. + * + * @param index the index of the character to delete + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if index is out of bounds + * @since 1.2 + */ + public StringBuffer deleteCharAt(int index) + { + return delete(index, index + 1); + } + + /** + * Replace characters between index <code>start</code> (inclusive) and + * <code>end</code> (exclusive) with <code>str</code>. If <code>end</code> + * is larger than the size of this StringBuffer, all characters after + * <code>start</code> are replaced. + * + * @param start the beginning index of characters to delete (inclusive) + * @param end the ending index of characters to delete (exclusive) + * @param str the new <code>String</code> to insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @throws NullPointerException if str is null + * @since 1.2 + */ + public synchronized StringBuffer replace(int start, int end, String str) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + + int len = str.count; + // Calculate the difference in 'count' after the replace. + int delta = len - (end > count ? count : end) + start; + ensureCapacity_unsynchronized(count + delta); + + if (delta != 0 && end < count) + VMSystem.arraycopy(value, end, value, end + delta, count - end); + + str.getChars(0, len, value, start); + count += delta; + return this; + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at the end of this StringBuffer. + * + * @param beginIndex index to start substring (base 0) + * @return new String which is a substring of this StringBuffer + * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds + * @see #substring(int, int) + * @since 1.2 + */ + public String substring(int beginIndex) + { + return substring(beginIndex, count); + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at one character before a specified index. This is implemented + * the same as <code>substring(beginIndex, endIndex)</code>, to satisfy + * the CharSequence interface. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuffer + * @throws IndexOutOfBoundsException if beginIndex or endIndex is out of + * bounds + * @see #substring(int, int) + * @since 1.4 + */ + public CharSequence subSequence(int beginIndex, int endIndex) + { + return substring(beginIndex, endIndex); + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuffer + * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out + * of bounds + * @since 1.2 + */ + public synchronized String substring(int beginIndex, int endIndex) + { + int len = endIndex - beginIndex; + if (beginIndex < 0 || endIndex > count || endIndex < beginIndex) + throw new StringIndexOutOfBoundsException(); + if (len == 0) + return ""; + // Don't copy unless substring is smaller than 1/4 of the buffer. + boolean share_buffer = ((len << 2) >= value.length); + if (share_buffer) + this.shared = true; + // Package constructor avoids an array copy. + return new String(value, beginIndex, len, share_buffer); + } + + /** + * Insert a subarray of the <code>char[]</code> argument into this + * <code>StringBuffer</code>. + * + * @param offset the place to insert in this buffer + * @param str the <code>char[]</code> to insert + * @param str_offset the index in <code>str</code> to start inserting from + * @param len the number of characters to insert + * @return this <code>StringBuffer</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @throws StringIndexOutOfBoundsException if any index is out of bounds + * @since 1.2 + */ + public synchronized StringBuffer insert(int offset, + char[] str, int str_offset, int len) + { + if (offset < 0 || offset > count || len < 0 + || str_offset < 0 || str_offset > str.length - len) + throw new StringIndexOutOfBoundsException(); + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(value, offset, value, offset + len, count - offset); + VMSystem.arraycopy(str, str_offset, value, offset, len); + count += len; + return this; + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param obj the <code>Object</code> to convert and insert + * @return this <code>StringBuffer</code> + * @exception StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(Object) + */ + public StringBuffer insert(int offset, Object obj) + { + return insert(offset, obj == null ? "null" : obj.toString()); + } + + /** + * Insert the <code>String</code> argument into this + * <code>StringBuffer</code>. If str is null, the String "null" is used + * instead. + * + * @param offset the place to insert in this buffer + * @param str the <code>String</code> to insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public synchronized StringBuffer insert(int offset, String str) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(value, offset, value, offset + len, count - offset); + str.getChars(0, len, value, offset); + count += len; + return this; + } + + /** + * Insert the <code>char[]</code> argument into this + * <code>StringBuffer</code>. + * + * @param offset the place to insert in this buffer + * @param data the <code>char[]</code> to insert + * @return this <code>StringBuffer</code> + * @throws NullPointerException if <code>data</code> is <code>null</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see #insert(int, char[], int, int) + */ + public StringBuffer insert(int offset, char[] data) + { + return insert(offset, data, 0, data.length); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param bool the <code>boolean</code> to convert and insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(boolean) + */ + public StringBuffer insert(int offset, boolean bool) + { + return insert(offset, bool ? "true" : "false"); + } + + /** + * Insert the <code>char</code> argument into this <code>StringBuffer</code>. + * + * @param offset the place to insert in this buffer + * @param ch the <code>char</code> to insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public synchronized StringBuffer insert(int offset, char ch) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + ensureCapacity_unsynchronized(count + 1); + VMSystem.arraycopy(value, offset, value, offset + 1, count - offset); + value[offset] = ch; + count++; + return this; + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param inum the <code>int</code> to convert and insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(int) + */ + public StringBuffer insert(int offset, int inum) + { + return insert(offset, String.valueOf(inum)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param lnum the <code>long</code> to convert and insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(long) + */ + public StringBuffer insert(int offset, long lnum) + { + return insert(offset, Long.toString(lnum, 10)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param fnum the <code>float</code> to convert and insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(float) + */ + public StringBuffer insert(int offset, float fnum) + { + return insert(offset, Float.toString(fnum)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param dnum the <code>double</code> to convert and insert + * @return this <code>StringBuffer</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(double) + */ + public StringBuffer insert(int offset, double dnum) + { + return insert(offset, Double.toString(dnum)); + } + + /** + * Finds the first instance of a substring in this StringBuffer. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #indexOf(String, int) + * @since 1.4 + */ + public int indexOf(String str) + { + return indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this StringBuffer, starting at + * a given index. If starting index is less than 0, the search starts at + * the beginning of this String. If the starting index is greater than the + * length of this String, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public synchronized int indexOf(String str, int fromIndex) + { + if (fromIndex < 0) + fromIndex = 0; + int limit = count - str.count; + for ( ; fromIndex <= limit; fromIndex++) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a substring in this StringBuffer. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #lastIndexOf(String, int) + * @since 1.4 + */ + public int lastIndexOf(String str) + { + return lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this StringBuffer, starting at a + * given index. If starting index is greater than the maximum valid index, + * then the search begins at the end of this String. If the starting index + * is less than zero, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public synchronized int lastIndexOf(String str, int fromIndex) + { + fromIndex = Math.min(fromIndex, count - str.count); + for ( ; fromIndex >= 0; fromIndex--) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Reverse the characters in this StringBuffer. The same sequence of + * characters exists, but in the reverse index ordering. + * + * @return this <code>StringBuffer</code> + */ + public synchronized StringBuffer reverse() + { + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity_unsynchronized(count); + for (int i = count >> 1, j = count - i; --i >= 0; ++j) + { + char c = value[i]; + value[i] = value[j]; + value[j] = c; + } + return this; + } + + /** + * Convert this <code>StringBuffer</code> to a <code>String</code>. The + * String is composed of the characters currently in this StringBuffer. Note + * that the result is a copy, and that future modifications to this buffer + * do not affect the String. + * + * @return the characters in this StringBuffer + */ + public String toString() + { + // The string will set this.shared = true. + return new String(this); + } + + /** + * An unsynchronized version of ensureCapacity, used internally to avoid + * the cost of a second lock on the same object. This also has the side + * effect of duplicating the array, if it was shared (to form copy-on-write + * semantics). + * + * @param minimumCapacity the minimum capacity + * @see #ensureCapacity(int) + */ + private void ensureCapacity_unsynchronized(int minimumCapacity) + { + if (shared || minimumCapacity > value.length) + { + // We don't want to make a larger vector when `shared' is + // set. If we do, then setLength becomes very inefficient + // when repeatedly reusing a StringBuffer in a loop. + int max = (minimumCapacity > value.length + ? value.length * 2 + 2 + : value.length); + minimumCapacity = (minimumCapacity < max ? max : minimumCapacity); + char[] nb = new char[minimumCapacity]; + VMSystem.arraycopy(value, 0, nb, 0, count); + value = nb; + shared = false; + } + } + + /** + * Predicate which determines if a substring of this matches another String + * starting at a specified offset for each String and continuing for a + * specified length. This is more efficient than creating a String to call + * indexOf on. + * + * @param toffset index to start comparison at for this String + * @param other non-null String to compare to region of this + * @return true if regions match, false otherwise + * @see #indexOf(String, int) + * @see #lastIndexOf(String, int) + * @see String#regionMatches(boolean, int, String, int, int) + */ + private boolean regionMatches(int toffset, String other) + { + int len = other.count; + int index = other.offset; + while (--len >= 0) + if (value[toffset++] != other.value[index++]) + return false; + return true; + } +} diff --git a/libjava/classpath/java/lang/StringBuilder.java b/libjava/classpath/java/lang/StringBuilder.java new file mode 100644 index 00000000000..b54c8ef7eb4 --- /dev/null +++ b/libjava/classpath/java/lang/StringBuilder.java @@ -0,0 +1,944 @@ +/* StringBuilder.java -- Unsynchronized growable strings + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; + +/** + * <code>StringBuilder</code> represents a changeable <code>String</code>. + * It provides the operations required to modify the + * <code>StringBuilder</code>, including insert, replace, delete, append, + * and reverse. It like <code>StringBuffer</code>, but is not + * synchronized. It is ideal for use when it is known that the + * object will only be used from a single thread. + * + * <p><code>StringBuilder</code>s are variable-length in nature, so even if + * you initialize them to a certain size, they can still grow larger than + * that. <em>Capacity</em> indicates the number of characters the + * <code>StringBuilder</code> can have in it before it has to grow (growing + * the char array is an expensive operation involving <code>new</code>). + * + * <p>Incidentally, compilers often implement the String operator "+" + * by using a <code>StringBuilder</code> operation:<br> + * <code>a + b</code><br> + * is the same as<br> + * <code>new StringBuilder().append(a).append(b).toString()</code>. + * + * <p>Classpath's StringBuilder is capable of sharing memory with Strings for + * efficiency. This will help when a StringBuilder is converted to a String + * and the StringBuilder is not changed after that (quite common when + * performing string concatenation). + * + * @author Paul Fisher + * @author John Keiser + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @see String + * @see StringBuffer + * + * @since 1.5 + */ +// FIX15: Implement Appendable when co-variant methods are available +public final class StringBuilder + implements Serializable, CharSequence +{ + // Implementation note: if you change this class, you usually will + // want to change StringBuffer as well. + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 4383685877147921099L; + + /** + * Index of next available character (and thus the size of the current + * string contents). Note that this has permissions set this way so that + * String can get the value. + * + * @serial the number of characters in the buffer + */ + int count; + + /** + * The buffer. Note that this has permissions set this way so that String + * can get the value. + * + * @serial the buffer + */ + char[] value; + + /** + * The default capacity of a buffer. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * Create a new StringBuilder with default capacity 16. + */ + public StringBuilder() + { + this(DEFAULT_CAPACITY); + } + + /** + * Create an empty <code>StringBuilder</code> with the specified initial + * capacity. + * + * @param capacity the initial capacity + * @throws NegativeArraySizeException if capacity is negative + */ + public StringBuilder(int capacity) + { + value = new char[capacity]; + } + + /** + * Create a new <code>StringBuilder</code> with the characters in the + * specified <code>String</code>. Initial capacity will be the size of the + * String plus 16. + * + * @param str the <code>String</code> to convert + * @throws NullPointerException if str is null + */ + public StringBuilder(String str) + { + // Unfortunately, because the size is 16 larger, we cannot share. + count = str.count; + value = new char[count + DEFAULT_CAPACITY]; + str.getChars(0, count, value, 0); + } + + /** + * Create a new <code>StringBuilder</code> with the characters in the + * specified <code>CharSequence</code>. Initial capacity will be the + * length of the sequence plus 16; if the sequence reports a length + * less than or equal to 0, then the initial capacity will be 16. + * + * @param seq the initializing <code>CharSequence</code> + * @throws NullPointerException if str is null + */ + public StringBuilder(CharSequence seq) + { + int len = seq.length(); + count = len <= 0 ? 0 : len; + value = new char[count + DEFAULT_CAPACITY]; + for (int i = 0; i < len; ++i) + value[i] = seq.charAt(i); + } + + /** + * Get the length of the <code>String</code> this <code>StringBuilder</code> + * would create. Not to be confused with the <em>capacity</em> of the + * <code>StringBuilder</code>. + * + * @return the length of this <code>StringBuilder</code> + * @see #capacity() + * @see #setLength(int) + */ + public int length() + { + return count; + } + + /** + * Get the total number of characters this <code>StringBuilder</code> can + * support before it must be grown. Not to be confused with <em>length</em>. + * + * @return the capacity of this <code>StringBuilder</code> + * @see #length() + * @see #ensureCapacity(int) + */ + public int capacity() + { + return value.length; + } + + /** + * Increase the capacity of this <code>StringBuilder</code>. This will + * ensure that an expensive growing operation will not occur until + * <code>minimumCapacity</code> is reached. The buffer is grown to the + * larger of <code>minimumCapacity</code> and + * <code>capacity() * 2 + 2</code>, if it is not already large enough. + * + * @param minimumCapacity the new capacity + * @see #capacity() + */ + public void ensureCapacity(int minimumCapacity) + { + if (minimumCapacity > value.length) + { + int max = value.length * 2 + 2; + minimumCapacity = (minimumCapacity < max ? max : minimumCapacity); + char[] nb = new char[minimumCapacity]; + System.arraycopy(value, 0, nb, 0, count); + value = nb; + } + } + + /** + * Set the length of this StringBuilder. If the new length is greater than + * the current length, all the new characters are set to '\0'. If the new + * length is less than the current length, the first <code>newLength</code> + * characters of the old array will be preserved, and the remaining + * characters are truncated. + * + * @param newLength the new length + * @throws IndexOutOfBoundsException if the new length is negative + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #length() + */ + public void setLength(int newLength) + { + if (newLength < 0) + throw new StringIndexOutOfBoundsException(newLength); + + int valueLength = value.length; + + /* Always call ensureCapacity in order to preserve copy-on-write + semantics. */ + ensureCapacity(newLength); + + if (newLength < valueLength) + { + /* If the StringBuilder's value just grew, then we know that + value is newly allocated and the region between count and + newLength is filled with '\0'. */ + count = newLength; + } + else + { + /* The StringBuilder's value doesn't need to grow. However, + we should clear out any cruft that may exist. */ + while (count < newLength) + value[count++] = '\0'; + } + } + + /** + * Get the character at the specified index. + * + * @param index the index of the character to get, starting at 0 + * @return the character at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public char charAt(int index) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + return value[index]; + } + + /** + * Get the specified array of characters. <code>srcOffset - srcEnd</code> + * characters will be copied into the array you pass in. + * + * @param srcOffset the index to start copying from (inclusive) + * @param srcEnd the index to stop copying from (exclusive) + * @param dst the array to copy into + * @param dstOffset the index to start copying into + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any source or target indices are + * out of range (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dest problems cause an + * ArrayIndexOutOfBoundsException) + * @see System#arraycopy(Object, int, Object, int, int) + */ + public void getChars(int srcOffset, int srcEnd, + char[] dst, int dstOffset) + { + if (srcOffset < 0 || srcEnd > count || srcEnd < srcOffset) + throw new StringIndexOutOfBoundsException(); + System.arraycopy(value, srcOffset, dst, dstOffset, srcEnd - srcOffset); + } + + /** + * Set the character at the specified index. + * + * @param index the index of the character to set starting at 0 + * @param ch the value to set that character to + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public void setCharAt(int index, char ch) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity(count); + value[index] = ch; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param obj the <code>Object</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(Object) + * @see #append(String) + */ + public StringBuilder append(Object obj) + { + return append(obj == null ? "null" : obj.toString()); + } + + /** + * Append the <code>String</code> to this <code>StringBuilder</code>. If + * str is null, the String "null" is appended. + * + * @param str the <code>String</code> to append + * @return this <code>StringBuilder</code> + */ + public StringBuilder append(String str) + { + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity(count + len); + str.getChars(0, len, value, count); + count += len; + return this; + } + + /** + * Append the <code>StringBuilder</code> value of the argument to this + * <code>StringBuilder</code>. This behaves the same as + * <code>append((Object) stringBuffer)</code>, except it is more efficient. + * + * @param stringBuffer the <code>StringBuilder</code> to convert and append + * @return this <code>StringBuilder</code> + * @see #append(Object) + */ + public StringBuilder append(StringBuffer stringBuffer) + { + if (stringBuffer == null) + return append("null"); + synchronized (stringBuffer) + { + int len = stringBuffer.count; + ensureCapacity(count + len); + System.arraycopy(stringBuffer.value, 0, value, count, len); + count += len; + } + return this; + } + + /** + * Append the <code>char</code> array to this <code>StringBuilder</code>. + * This is similar (but more efficient) than + * <code>append(new String(data))</code>, except in the case of null. + * + * @param data the <code>char[]</code> to append + * @return this <code>StringBuilder</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @see #append(char[], int, int) + */ + public StringBuilder append(char[] data) + { + return append(data, 0, data.length); + } + + /** + * Append part of the <code>char</code> array to this + * <code>StringBuilder</code>. This is similar (but more efficient) than + * <code>append(new String(data, offset, count))</code>, except in the case + * of null. + * + * @param data the <code>char[]</code> to append + * @param offset the start location in <code>str</code> + * @param count the number of characters to get from <code>str</code> + * @return this <code>StringBuilder</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @throws IndexOutOfBoundsException if offset or count is out of range + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public StringBuilder append(char[] data, int offset, int count) + { + if (offset < 0 || count < 0 || offset > data.length - count) + throw new StringIndexOutOfBoundsException(); + ensureCapacity(this.count + count); + System.arraycopy(data, offset, value, this.count, count); + this.count += count; + return this; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param bool the <code>boolean</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(boolean) + */ + public StringBuilder append(boolean bool) + { + return append(bool ? "true" : "false"); + } + + /** + * Append the <code>char</code> to this <code>StringBuilder</code>. + * + * @param ch the <code>char</code> to append + * @return this <code>StringBuilder</code> + */ + public StringBuilder append(char ch) + { + ensureCapacity(count + 1); + value[count++] = ch; + return this; + } + + /** + * Append the characters in the <code>CharSequence</code> to this + * buffer. + * + * @param seq the <code>CharSequence</code> providing the characters + * @return this <code>StringBuilder</code> + */ + public StringBuilder append(CharSequence seq) + { + return append(seq, 0, seq.length()); + } + + /** + * Append some characters from the <code>CharSequence</code> to this + * buffer. If the argument is null, the four characters "null" are + * appended. + * + * @param seq the <code>CharSequence</code> providing the characters + * @param start the starting index + * @param end one past the final index + * @return this <code>StringBuilder</code> + */ + public StringBuilder append(CharSequence seq, int start, + int end) + { + if (seq == null) + return append("null"); + if (end - start > 0) + { + ensureCapacity(count + end - start); + for (; start < end; ++start) + value[count++] = seq.charAt(start); + } + return this; + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param inum the <code>int</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(int) + */ + // This is native in libgcj, for efficiency. + public StringBuilder append(int inum) + { + return append(String.valueOf(inum)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param lnum the <code>long</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(long) + */ + public StringBuilder append(long lnum) + { + return append(Long.toString(lnum, 10)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param fnum the <code>float</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(float) + */ + public StringBuilder append(float fnum) + { + return append(Float.toString(fnum)); + } + + /** + * Append the <code>String</code> value of the argument to this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param dnum the <code>double</code> to convert and append + * @return this <code>StringBuilder</code> + * @see String#valueOf(double) + */ + public StringBuilder append(double dnum) + { + return append(Double.toString(dnum)); + } + + /** + * Delete characters from this <code>StringBuilder</code>. + * <code>delete(10, 12)</code> will delete 10 and 11, but not 12. It is + * harmless for end to be larger than length(). + * + * @param start the first character to delete + * @param end the index after the last character to delete + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + */ + public StringBuilder delete(int start, int end) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + if (end > count) + end = count; + // This will unshare if required. + ensureCapacity(count); + if (count - end != 0) + System.arraycopy(value, end, value, start, count - end); + count -= end - start; + return this; + } + + /** + * Delete a character from this <code>StringBuilder</code>. + * + * @param index the index of the character to delete + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if index is out of bounds + */ + public StringBuilder deleteCharAt(int index) + { + return delete(index, index + 1); + } + + /** + * Replace characters between index <code>start</code> (inclusive) and + * <code>end</code> (exclusive) with <code>str</code>. If <code>end</code> + * is larger than the size of this StringBuilder, all characters after + * <code>start</code> are replaced. + * + * @param start the beginning index of characters to delete (inclusive) + * @param end the ending index of characters to delete (exclusive) + * @param str the new <code>String</code> to insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @throws NullPointerException if str is null + */ + public StringBuilder replace(int start, int end, String str) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + + int len = str.count; + // Calculate the difference in 'count' after the replace. + int delta = len - (end > count ? count : end) + start; + ensureCapacity(count + delta); + + if (delta != 0 && end < count) + System.arraycopy(value, end, value, end + delta, count - end); + + str.getChars(0, len, value, start); + count += delta; + return this; + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at the end of this StringBuilder. + * + * @param beginIndex index to start substring (base 0) + * @return new String which is a substring of this StringBuilder + * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds + * @see #substring(int, int) + */ + public String substring(int beginIndex) + { + return substring(beginIndex, count); + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at one character before a specified index. This is implemented + * the same as <code>substring(beginIndex, endIndex)</code>, to satisfy + * the CharSequence interface. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuilder + * @throws IndexOutOfBoundsException if beginIndex or endIndex is out of + * bounds + * @see #substring(int, int) + */ + public CharSequence subSequence(int beginIndex, int endIndex) + { + return substring(beginIndex, endIndex); + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuilder + * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out + * of bounds + */ + public String substring(int beginIndex, int endIndex) + { + int len = endIndex - beginIndex; + if (beginIndex < 0 || endIndex > count || endIndex < beginIndex) + throw new StringIndexOutOfBoundsException(); + if (len == 0) + return ""; + return new String(value, beginIndex, len); + } + + /** + * Insert a subarray of the <code>char[]</code> argument into this + * <code>StringBuilder</code>. + * + * @param offset the place to insert in this buffer + * @param str the <code>char[]</code> to insert + * @param str_offset the index in <code>str</code> to start inserting from + * @param len the number of characters to insert + * @return this <code>StringBuilder</code> + * @throws NullPointerException if <code>str</code> is <code>null</code> + * @throws StringIndexOutOfBoundsException if any index is out of bounds + */ + public StringBuilder insert(int offset, + char[] str, int str_offset, int len) + { + if (offset < 0 || offset > count || len < 0 + || str_offset < 0 || str_offset > str.length - len) + throw new StringIndexOutOfBoundsException(); + ensureCapacity(count + len); + System.arraycopy(value, offset, value, offset + len, count - offset); + System.arraycopy(str, str_offset, value, offset, len); + count += len; + return this; + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param obj the <code>Object</code> to convert and insert + * @return this <code>StringBuilder</code> + * @exception StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(Object) + */ + public StringBuilder insert(int offset, Object obj) + { + return insert(offset, obj == null ? "null" : obj.toString()); + } + + /** + * Insert the <code>String</code> argument into this + * <code>StringBuilder</code>. If str is null, the String "null" is used + * instead. + * + * @param offset the place to insert in this buffer + * @param str the <code>String</code> to insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public StringBuilder insert(int offset, String str) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity(count + len); + System.arraycopy(value, offset, value, offset + len, count - offset); + str.getChars(0, len, value, offset); + count += len; + return this; + } + + /** + * Insert the <code>char[]</code> argument into this + * <code>StringBuilder</code>. + * + * @param offset the place to insert in this buffer + * @param data the <code>char[]</code> to insert + * @return this <code>StringBuilder</code> + * @throws NullPointerException if <code>data</code> is <code>null</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see #insert(int, char[], int, int) + */ + public StringBuilder insert(int offset, char[] data) + { + return insert(offset, data, 0, data.length); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param bool the <code>boolean</code> to convert and insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(boolean) + */ + public StringBuilder insert(int offset, boolean bool) + { + return insert(offset, bool ? "true" : "false"); + } + + /** + * Insert the <code>char</code> argument into this <code>StringBuilder</code>. + * + * @param offset the place to insert in this buffer + * @param ch the <code>char</code> to insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public StringBuilder insert(int offset, char ch) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + ensureCapacity(count + 1); + System.arraycopy(value, offset, value, offset + 1, count - offset); + value[offset] = ch; + count++; + return this; + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param inum the <code>int</code> to convert and insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(int) + */ + public StringBuilder insert(int offset, int inum) + { + return insert(offset, String.valueOf(inum)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param lnum the <code>long</code> to convert and insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(long) + */ + public StringBuilder insert(int offset, long lnum) + { + return insert(offset, Long.toString(lnum, 10)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param fnum the <code>float</code> to convert and insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(float) + */ + public StringBuilder insert(int offset, float fnum) + { + return insert(offset, Float.toString(fnum)); + } + + /** + * Insert the <code>String</code> value of the argument into this + * <code>StringBuilder</code>. Uses <code>String.valueOf()</code> to convert + * to <code>String</code>. + * + * @param offset the place to insert in this buffer + * @param dnum the <code>double</code> to convert and insert + * @return this <code>StringBuilder</code> + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(double) + */ + public StringBuilder insert(int offset, double dnum) + { + return insert(offset, Double.toString(dnum)); + } + + /** + * Finds the first instance of a substring in this StringBuilder. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #indexOf(String, int) + */ + public int indexOf(String str) + { + return indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this StringBuilder, starting at + * a given index. If starting index is less than 0, the search starts at + * the beginning of this String. If the starting index is greater than the + * length of this String, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int indexOf(String str, int fromIndex) + { + if (fromIndex < 0) + fromIndex = 0; + int limit = count - str.count; + for ( ; fromIndex <= limit; fromIndex++) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a substring in this StringBuilder. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #lastIndexOf(String, int) + */ + public int lastIndexOf(String str) + { + return lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this StringBuilder, starting at a + * given index. If starting index is greater than the maximum valid index, + * then the search begins at the end of this String. If the starting index + * is less than zero, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int lastIndexOf(String str, int fromIndex) + { + fromIndex = Math.min(fromIndex, count - str.count); + for ( ; fromIndex >= 0; fromIndex--) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Reverse the characters in this StringBuilder. The same sequence of + * characters exists, but in the reverse index ordering. + * + * @return this <code>StringBuilder</code> + */ + public StringBuilder reverse() + { + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity(count); + for (int i = count >> 1, j = count - i; --i >= 0; ++j) + { + char c = value[i]; + value[i] = value[j]; + value[j] = c; + } + return this; + } + + /** + * Convert this <code>StringBuilder</code> to a <code>String</code>. The + * String is composed of the characters currently in this StringBuilder. Note + * that the result is a copy, and that future modifications to this buffer + * do not affect the String. + * + * @return the characters in this StringBuilder + */ + public String toString() + { + return new String(this); + } + + /** + * Predicate which determines if a substring of this matches another String + * starting at a specified offset for each String and continuing for a + * specified length. This is more efficient than creating a String to call + * indexOf on. + * + * @param toffset index to start comparison at for this String + * @param other non-null String to compare to region of this + * @return true if regions match, false otherwise + * @see #indexOf(String, int) + * @see #lastIndexOf(String, int) + * @see String#regionMatches(boolean, int, String, int, int) + */ + private boolean regionMatches(int toffset, String other) + { + int len = other.count; + int index = other.offset; + while (--len >= 0) + if (value[toffset++] != other.value[index++]) + return false; + return true; + } +} diff --git a/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java b/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java new file mode 100644 index 00000000000..ebc4a04a391 --- /dev/null +++ b/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java @@ -0,0 +1,85 @@ +/* StringIndexOutOfBoundsException.java -- thrown to indicate attempt to + exceed string bounds + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception can be thrown to indicate an attempt to access an index + * which is out of bounds of a String. Any negative integer, and a positive + * integer greater than or equal to the size of the string, is an index + * which would be out of bounds. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6762910422159637258L; + + /** + * Create an exception without a message. + */ + public StringIndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public StringIndexOutOfBoundsException(String s) + { + super(s); + } + + /** + * Create an exception noting the illegal index. + * + * @param index the invalid index + */ + public StringIndexOutOfBoundsException(int index) + { + super("String index out of range: " + index); + } +} diff --git a/libjava/classpath/java/lang/System.java b/libjava/classpath/java/lang/System.java new file mode 100644 index 00000000000..e466d3b8cfb --- /dev/null +++ b/libjava/classpath/java/lang/System.java @@ -0,0 +1,528 @@ +/* System.java -- useful methods to interface with the system + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; + +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Properties; +import java.util.PropertyPermission; + +/** + * System represents system-wide resources; things that represent the + * general environment. As such, all methods are static. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status still missing 1.4 functionality + */ +public final class System +{ + // WARNING: System is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The standard InputStream. This is assigned at startup and starts its + * life perfectly valid. Although it is marked final, you can change it + * using {@link #setIn(InputStream)} through some hefty VM magic. + * + * <p>This corresponds to the C stdin and C++ cin variables, which + * typically input from the keyboard, but may be used to pipe input from + * other processes or files. That should all be transparent to you, + * however. + */ + public static final InputStream in = VMSystem.makeStandardInputStream(); + + /** + * The standard output PrintStream. This is assigned at startup and + * starts its life perfectly valid. Although it is marked final, you can + * change it using {@link #setOut(PrintStream)} through some hefty VM magic. + * + * <p>This corresponds to the C stdout and C++ cout variables, which + * typically output normal messages to the screen, but may be used to pipe + * output to other processes or files. That should all be transparent to + * you, however. + */ + public static final PrintStream out = VMSystem.makeStandardOutputStream(); + + /** + * The standard output PrintStream. This is assigned at startup and + * starts its life perfectly valid. Although it is marked final, you can + * change it using {@link #setErr(PrintStream)} through some hefty VM magic. + * + * <p>This corresponds to the C stderr and C++ cerr variables, which + * typically output error messages to the screen, but may be used to pipe + * output to other processes or files. That should all be transparent to + * you, however. + */ + public static final PrintStream err = VMSystem.makeStandardErrorStream(); + + /** + * This class is uninstantiable. + */ + private System() + { + } + + /** + * Set {@link #in} to a new InputStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * <code>RuntimePermission("setIO")</code>. + * + * @param in the new InputStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setIn(InputStream in) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + VMSystem.setIn(in); + } + + /** + * Set {@link #out} to a new PrintStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * <code>RuntimePermission("setIO")</code>. + * + * @param out the new PrintStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setOut(PrintStream out) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + + VMSystem.setOut(out); + } + + /** + * Set {@link #err} to a new PrintStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * <code>RuntimePermission("setIO")</code>. + * + * @param err the new PrintStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setErr(PrintStream err) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + VMSystem.setErr(err); + } + + /** + * Set the current SecurityManager. If a security manager already exists, + * then <code>RuntimePermission("setSecurityManager")</code> is checked + * first. Since this permission is denied by the default security manager, + * setting the security manager is often an irreversible action. + * + * <STRONG>Spec Note:</STRONG> Don't ask me, I didn't write it. It looks + * pretty vulnerable; whoever gets to the gate first gets to set the policy. + * There is probably some way to set the original security manager as a + * command line argument to the VM, but I don't know it. + * + * @param sm the new SecurityManager + * @throws SecurityException if permission is denied + */ + public static synchronized void setSecurityManager(SecurityManager sm) + { + // Implementation note: the field lives in SecurityManager because of + // bootstrap initialization issues. This method is synchronized so that + // no other thread changes it to null before this thread makes the change. + if (SecurityManager.current != null) + SecurityManager.current.checkPermission + (new RuntimePermission("setSecurityManager")); + SecurityManager.current = sm; + } + + /** + * Get the current SecurityManager. If the SecurityManager has not been + * set yet, then this method returns null. + * + * @return the current SecurityManager, or null + */ + public static SecurityManager getSecurityManager() + { + return SecurityManager.current; + } + + /** + * Get the current time, measured in the number of milliseconds from the + * beginning of Jan. 1, 1970. This is gathered from the system clock, with + * any attendant incorrectness (it may be timezone dependent). + * + * @return the current time + * @see java.util.Date + */ + public static long currentTimeMillis() + { + return VMSystem.currentTimeMillis(); + } + + /** + * Copy one array onto another from <code>src[srcStart]</code> ... + * <code>src[srcStart+len-1]</code> to <code>dest[destStart]</code> ... + * <code>dest[destStart+len-1]</code>. First, the arguments are validated: + * neither array may be null, they must be of compatible types, and the + * start and length must fit within both arrays. Then the copying starts, + * and proceeds through increasing slots. If src and dest are the same + * array, this will appear to copy the data to a temporary location first. + * An ArrayStoreException in the middle of copying will leave earlier + * elements copied, but later elements unchanged. + * + * @param src the array to copy elements from + * @param srcStart the starting position in src + * @param dest the array to copy elements to + * @param destStart the starting position in dest + * @param len the number of elements to copy + * @throws NullPointerException if src or dest is null + * @throws ArrayStoreException if src or dest is not an array, if they are + * not compatible array types, or if an incompatible runtime type + * is stored in dest + * @throws IndexOutOfBoundsException if len is negative, or if the start or + * end copy position in either array is out of bounds + */ + public static void arraycopy(Object src, int srcStart, + Object dest, int destStart, int len) + { + VMSystem.arraycopy(src, srcStart, dest, destStart, len); + } + + /** + * Get a hash code computed by the VM for the Object. This hash code will + * be the same as Object's hashCode() method. It is usually some + * convolution of the pointer to the Object internal to the VM. It + * follows standard hash code rules, in that it will remain the same for a + * given Object for the lifetime of that Object. + * + * @param o the Object to get the hash code for + * @return the VM-dependent hash code for this Object + * @since 1.1 + */ + public static int identityHashCode(Object o) + { + return VMSystem.identityHashCode(o); + } + + /** + * Get all the system properties at once. A security check may be performed, + * <code>checkPropertiesAccess</code>. Note that a security manager may + * allow getting a single property, but not the entire group. + * + * <p>The required properties include: + * <dl> + * <dt>java.version</dt> <dd>Java version number</dd> + * <dt>java.vendor</dt> <dd>Java vendor specific string</dd> + * <dt>java.vendor.url</dt> <dd>Java vendor URL</dd> + * <dt>java.home</dt> <dd>Java installation directory</dd> + * <dt>java.vm.specification.version</dt> <dd>VM Spec version</dd> + * <dt>java.vm.specification.vendor</dt> <dd>VM Spec vendor</dd> + * <dt>java.vm.specification.name</dt> <dd>VM Spec name</dd> + * <dt>java.vm.version</dt> <dd>VM implementation version</dd> + * <dt>java.vm.vendor</dt> <dd>VM implementation vendor</dd> + * <dt>java.vm.name</dt> <dd>VM implementation name</dd> + * <dt>java.specification.version</dt> <dd>Java Runtime Environment version</dd> + * <dt>java.specification.vendor</dt> <dd>Java Runtime Environment vendor</dd> + * <dt>java.specification.name</dt> <dd>Java Runtime Environment name</dd> + * <dt>java.class.version</dt> <dd>Java class version number</dd> + * <dt>java.class.path</dt> <dd>Java classpath</dd> + * <dt>java.library.path</dt> <dd>Path for finding Java libraries</dd> + * <dt>java.io.tmpdir</dt> <dd>Default temp file path</dd> + * <dt>java.compiler</dt> <dd>Name of JIT to use</dd> + * <dt>java.ext.dirs</dt> <dd>Java extension path</dd> + * <dt>os.name</dt> <dd>Operating System Name</dd> + * <dt>os.arch</dt> <dd>Operating System Architecture</dd> + * <dt>os.version</dt> <dd>Operating System Version</dd> + * <dt>file.separator</dt> <dd>File separator ("/" on Unix)</dd> + * <dt>path.separator</dt> <dd>Path separator (":" on Unix)</dd> + * <dt>line.separator</dt> <dd>Line separator ("\n" on Unix)</dd> + * <dt>user.name</dt> <dd>User account name</dd> + * <dt>user.home</dt> <dd>User home directory</dd> + * <dt>user.dir</dt> <dd>User's current working directory</dd> + * </dl> + * + * In addition, gnu defines several other properties, where ? stands for + * each character in '0' through '9': + * <dl> + * <dt>gnu.classpath.home</dt> <dd>Path to the classpath libraries.</dd> + * <dt>gnu.classpath.version</dt> <dd>Version of the classpath libraries.</dd> + * <dt>gnu.classpath.vm.shortname</dt> <dd>Succinct version of the VM name; + * used for finding property files in file system</dd> + * <dt>gnu.classpath.home.url</dt> <dd> Base URL; used for finding + * property files in file system</dd> + * <dt>gnu.cpu.endian</dt> <dd>big or little</dd> + * <dt>gnu.java.io.encoding_scheme_alias.iso-8859-?</dt> <dd>8859_?</dd> + * <dt>gnu.java.io.encoding_scheme_alias.iso8859_?</dt> <dd>8859_?</dd> + * <dt>gnu.java.io.encoding_scheme_alias.iso-latin-_?</dt> <dd>8859_?</dd> + * <dt>gnu.java.io.encoding_scheme_alias.latin?</dt> <dd>8859_?</dd> + * <dt>gnu.java.io.encoding_scheme_alias.utf-8</dt> <dd>UTF8</dd> + * </dl> + * + * @return the system properties, will never be null + * @throws SecurityException if permission is denied + */ + public static Properties getProperties() + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertiesAccess(); + return SystemProperties.getProperties(); + } + + /** + * Set all the system properties at once. A security check may be performed, + * <code>checkPropertiesAccess</code>. Note that a security manager may + * allow setting a single property, but not the entire group. An argument + * of null resets the properties to the startup default. + * + * @param properties the new set of system properties + * @throws SecurityException if permission is denied + */ + public static void setProperties(Properties properties) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertiesAccess(); + SystemProperties.setProperties(properties); + } + + /** + * Get a single system property by name. A security check may be performed, + * <code>checkPropertyAccess(key)</code>. + * + * @param key the name of the system property to get + * @return the property, or null if not found + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + */ + public static String getProperty(String key) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertyAccess(key); + else if (key.length() == 0) + throw new IllegalArgumentException("key can't be empty"); + return SystemProperties.getProperty(key); + } + + /** + * Get a single system property by name. A security check may be performed, + * <code>checkPropertyAccess(key)</code>. + * + * @param key the name of the system property to get + * @param def the default + * @return the property, or def if not found + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + */ + public static String getProperty(String key, String def) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertyAccess(key); + return SystemProperties.getProperty(key, def); + } + + /** + * Set a single system property by name. A security check may be performed, + * <code>checkPropertyAccess(key, "write")</code>. + * + * @param key the name of the system property to set + * @param value the new value + * @return the previous value, or null + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + * @since 1.2 + */ + public static String setProperty(String key, String value) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new PropertyPermission(key, "write")); + return SystemProperties.setProperty(key, value); + } + + /** + * Gets the value of an environment variable. + * + * @param name the name of the environment variable + * @return the string value of the variable or null when the + * environment variable is not defined. + * @throws NullPointerException + * @throws SecurityException if permission is denied + * @since 1.5 + * @specnote This method was deprecated in some JDK releases, but + * was restored in 1.5. + */ + public static String getenv(String name) + { + if (name == null) + throw new NullPointerException(); + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("getenv." + name)); + return VMSystem.getenv(name); + } + + /** + * Terminate the Virtual Machine. This just calls + * <code>Runtime.getRuntime().exit(status)</code>, and never returns. + * Obviously, a security check is in order, <code>checkExit</code>. + * + * @param status the exit status; by convention non-zero is abnormal + * @throws SecurityException if permission is denied + * @see Runtime#exit(int) + */ + public static void exit(int status) + { + Runtime.getRuntime().exit(status); + } + + /** + * Calls the garbage collector. This is only a hint, and it is up to the + * implementation what this hint suggests, but it usually causes a + * best-effort attempt to reclaim unused memory from discarded objects. + * This calls <code>Runtime.getRuntime().gc()</code>. + * + * @see Runtime#gc() + */ + public static void gc() + { + Runtime.getRuntime().gc(); + } + + /** + * Runs object finalization on pending objects. This is only a hint, and + * it is up to the implementation what this hint suggests, but it usually + * causes a best-effort attempt to run finalizers on all objects ready + * to be reclaimed. This calls + * <code>Runtime.getRuntime().runFinalization()</code>. + * + * @see Runtime#runFinalization() + */ + public static void runFinalization() + { + Runtime.getRuntime().runFinalization(); + } + + /** + * Tell the Runtime whether to run finalization before exiting the + * JVM. This is inherently unsafe in multi-threaded applications, + * since it can force initialization on objects which are still in use + * by live threads, leading to deadlock; therefore this is disabled by + * default. There may be a security check, <code>checkExit(0)</code>. This + * calls <code>Runtime.runFinalizersOnExit()</code>. + * + * @param finalizeOnExit whether to run finalizers on exit + * @throws SecurityException if permission is denied + * @see Runtime#runFinalizersOnExit() + * @since 1.1 + * @deprecated never rely on finalizers to do a clean, thread-safe, + * mop-up from your code + */ + public static void runFinalizersOnExit(boolean finalizeOnExit) + { + Runtime.runFinalizersOnExit(finalizeOnExit); + } + + /** + * Load a code file using its explicit system-dependent filename. A security + * check may be performed, <code>checkLink</code>. This just calls + * <code>Runtime.getRuntime().load(filename)</code>. + * + * <p> + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param filename the code file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the file cannot be loaded + * @see Runtime#load(String) + */ + public static void load(String filename) + { + Runtime.getRuntime().load(filename, VMStackWalker.getCallingClassLoader()); + } + + /** + * Load a library using its explicit system-dependent filename. A security + * check may be performed, <code>checkLink</code>. This just calls + * <code>Runtime.getRuntime().load(filename)</code>. + * + * <p> + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param libname the library file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the file cannot be loaded + * @see Runtime#load(String) + */ + public static void loadLibrary(String libname) + { + Runtime.getRuntime().loadLibrary(libname, + VMStackWalker.getCallingClassLoader()); + } + + /** + * Convert a library name to its platform-specific variant. + * + * @param libname the library name, as used in <code>loadLibrary</code> + * @return the platform-specific mangling of the name + * @since 1.2 + */ + public static String mapLibraryName(String libname) + { + return VMRuntime.mapLibraryName(libname); + } + +} // class System diff --git a/libjava/classpath/java/lang/Thread.java b/libjava/classpath/java/lang/Thread.java new file mode 100644 index 00000000000..37ca630b4ed --- /dev/null +++ b/libjava/classpath/java/lang/Thread.java @@ -0,0 +1,999 @@ +/* Thread -- an independent thread of executable code + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.util.Map; +import java.util.WeakHashMap; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete to version 1.4, with caveats. We do not + * implement the deprecated (and dangerous) stop, suspend, and resume + * methods. Security implementation is not complete. + */ + +/** + * Thread represents a single thread of execution in the VM. When an + * application VM starts up, it creates a non-daemon Thread which calls the + * main() method of a particular class. There may be other Threads running, + * such as the garbage collection thread. + * + * <p>Threads have names to identify them. These names are not necessarily + * unique. Every Thread has a priority, as well, which tells the VM which + * Threads should get more running time. New threads inherit the priority + * and daemon status of the parent thread, by default. + * + * <p>There are two methods of creating a Thread: you may subclass Thread and + * implement the <code>run()</code> method, at which point you may start the + * Thread by calling its <code>start()</code> method, or you may implement + * <code>Runnable</code> in the class you want to use and then call new + * <code>Thread(your_obj).start()</code>. + * + * <p>The virtual machine runs until all non-daemon threads have died (either + * by returning from the run() method as invoked by start(), or by throwing + * an uncaught exception); or until <code>System.exit</code> is called with + * adequate permissions. + * + * <p>It is unclear at what point a Thread should be added to a ThreadGroup, + * and at what point it should be removed. Should it be inserted when it + * starts, or when it is created? Should it be removed when it is suspended + * or interrupted? The only thing that is clear is that the Thread should be + * removed when it is stopped. + * + * @author Tom Tromey + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @see Runnable + * @see Runtime#exit(int) + * @see #run() + * @see #start() + * @see ThreadLocal + * @since 1.0 + * @status updated to 1.4 + */ +public class Thread implements Runnable +{ + /** The minimum priority for a Thread. */ + public static final int MIN_PRIORITY = 1; + + /** The priority a Thread gets by default. */ + public static final int NORM_PRIORITY = 5; + + /** The maximum priority for a Thread. */ + public static final int MAX_PRIORITY = 10; + + /** The underlying VM thread, only set when the thread is actually running. + */ + volatile VMThread vmThread; + + /** + * The group this thread belongs to. This is set to null by + * ThreadGroup.removeThread when the thread dies. + */ + volatile ThreadGroup group; + + /** The object to run(), null if this is the target. */ + final Runnable runnable; + + /** The thread name, non-null. */ + volatile String name; + + /** Whether the thread is a daemon. */ + volatile boolean daemon; + + /** The thread priority, 1 to 10. */ + volatile int priority; + + /** Native thread stack size. 0 = use default */ + private long stacksize; + + /** Was the thread stopped before it was started? */ + Throwable stillborn; + + /** The context classloader for this Thread. */ + private ClassLoader contextClassLoader; + + /** The next thread number to use. */ + private static int numAnonymousThreadsCreated; + + /** Thread local storage. Package accessible for use by + * InheritableThreadLocal. + */ + WeakHashMap locals; + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(null, null,</code> + * <i>gname</i><code>)</code>, where <b><i>gname</i></b> is + * a newly generated name. Automatically generated names are of the + * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer. + * <p> + * Threads created this way must have overridden their + * <code>run()</code> method to actually do anything. An example + * illustrating this method being used follows: + * <p><blockquote><pre> + * import java.lang.*; + * + * class plain01 implements Runnable { + * String name; + * plain01() { + * name = null; + * } + * plain01(String s) { + * name = s; + * } + * public void run() { + * if (name == null) + * System.out.println("A new thread created"); + * else + * System.out.println("A new thread with name " + name + + * " created"); + * } + * } + * class threadtest01 { + * public static void main(String args[] ) { + * int failed = 0 ; + * + * <b>Thread t1 = new Thread();</b> + * if (t1 != null) + * System.out.println("new Thread() succeed"); + * else { + * System.out.println("new Thread() failed"); + * failed++; + * } + * } + * } + * </pre></blockquote> + * + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread() + { + this(null, (Runnable) null); + } + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(null, target,</code> + * <i>gname</i><code>)</code>, where <i>gname</i> is + * a newly generated name. Automatically generated names are of the + * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer. + * + * @param target the object whose <code>run</code> method is called. + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread(Runnable target) + { + this(null, target); + } + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(null, null, name)</code>. + * + * @param name the name of the new thread. + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread(String name) + { + this(null, null, name, 0); + } + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(group, target,</code> + * <i>gname</i><code>)</code>, where <i>gname</i> is + * a newly generated name. Automatically generated names are of the + * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, Runnable target) + { + this(group, target, "Thread-" + ++numAnonymousThreadsCreated, 0); + } + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(group, null, name)</code> + * + * @param group the group to put the Thread into + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, String name) + { + this(group, null, name, 0); + } + + /** + * Allocates a new <code>Thread</code> object. This constructor has + * the same effect as <code>Thread(null, target, name)</code>. + * + * @param target the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(Runnable target, String name) + { + this(null, target, name, 0); + } + + /** + * Allocate a new Thread object, with the specified ThreadGroup and name, and + * using the specified Runnable object's <code>run()</code> method to + * execute. If the Runnable object is null, <code>this</code> (which is + * a Runnable) is used instead. + * + * <p>If the ThreadGroup is null, the security manager is checked. If a + * manager exists and returns a non-null object for + * <code>getThreadGroup</code>, that group is used; otherwise the group + * of the creating thread is used. Note that the security manager calls + * <code>checkAccess</code> if the ThreadGroup is not null. + * + * <p>The new Thread will inherit its creator's priority and daemon status. + * These can be changed with <code>setPriority</code> and + * <code>setDaemon</code>. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @see Runnable#run() + * @see #run() + * @see #setDaemon(boolean) + * @see #setPriority(int) + * @see SecurityManager#checkAccess(ThreadGroup) + * @see ThreadGroup#checkAccess() + */ + public Thread(ThreadGroup group, Runnable target, String name) + { + this(group, target, name, 0); + } + + /** + * Allocate a new Thread object, as if by + * <code>Thread(group, null, name)</code>, and give it the specified stack + * size, in bytes. The stack size is <b>highly platform independent</b>, + * and the virtual machine is free to round up or down, or ignore it + * completely. A higher value might let you go longer before a + * <code>StackOverflowError</code>, while a lower value might let you go + * longer before an <code>OutOfMemoryError</code>. Or, it may do absolutely + * nothing! So be careful, and expect to need to tune this value if your + * virtual machine even supports it. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @param size the stack size, in bytes; 0 to be ignored + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access <code>group</code> + * @throws IllegalThreadStateException if group is destroyed + * @since 1.4 + */ + public Thread(ThreadGroup group, Runnable target, String name, long size) + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + Thread current = currentThread(); + if (group == null) + { + if (sm != null) + group = sm.getThreadGroup(); + if (group == null) + group = current.group; + } + else if (sm != null) + sm.checkAccess(group); + + this.group = group; + // Use toString hack to detect null. + this.name = name.toString(); + this.runnable = target; + this.stacksize = size; + + priority = current.priority; + daemon = current.daemon; + contextClassLoader = current.contextClassLoader; + + group.addThread(this); + InheritableThreadLocal.newChildThread(this); + } + + /** + * Used by the VM to create thread objects for threads started outside + * of Java. Note: caller is responsible for adding the thread to + * a group and InheritableThreadLocal. + * + * @param vmThread the native thread + * @param name the thread name or null to use the default naming scheme + * @param priority current priority + * @param daemon is the thread a background thread? + */ + Thread(VMThread vmThread, String name, int priority, boolean daemon) + { + this.vmThread = vmThread; + this.runnable = null; + if (name == null) + name = "Thread-" + ++numAnonymousThreadsCreated; + this.name = name; + this.priority = priority; + this.daemon = daemon; + this.contextClassLoader = ClassLoader.getSystemClassLoader(); + } + + /** + * Get the number of active threads in the current Thread's ThreadGroup. + * This implementation calls + * <code>currentThread().getThreadGroup().activeCount()</code>. + * + * @return the number of active threads in the current ThreadGroup + * @see ThreadGroup#activeCount() + */ + public static int activeCount() + { + return currentThread().group.activeCount(); + } + + /** + * Check whether the current Thread is allowed to modify this Thread. This + * passes the check on to <code>SecurityManager.checkAccess(this)</code>. + * + * @throws SecurityException if the current Thread cannot modify this Thread + * @see SecurityManager#checkAccess(Thread) + */ + public final void checkAccess() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkAccess(this); + } + + /** + * Count the number of stack frames in this Thread. The Thread in question + * must be suspended when this occurs. + * + * @return the number of stack frames in this Thread + * @throws IllegalThreadStateException if this Thread is not suspended + * @deprecated pointless, since suspend is deprecated + */ + public int countStackFrames() + { + VMThread t = vmThread; + if (t == null || group == null) + throw new IllegalThreadStateException(); + + return t.countStackFrames(); + } + + /** + * Get the currently executing Thread. In the situation that the + * currently running thread was created by native code and doesn't + * have an associated Thread object yet, a new Thread object is + * constructed and associated with the native thread. + * + * @return the currently executing Thread + */ + public static Thread currentThread() + { + return VMThread.currentThread(); + } + + /** + * Originally intended to destroy this thread, this method was never + * implemented by Sun, and is hence a no-op. + */ + public void destroy() + { + throw new NoSuchMethodError(); + } + + /** + * Print a stack trace of the current thread to stderr using the same + * format as Throwable's printStackTrace() method. + * + * @see Throwable#printStackTrace() + */ + public static void dumpStack() + { + new Throwable().printStackTrace(); + } + + /** + * Copy every active thread in the current Thread's ThreadGroup into the + * array. Extra threads are silently ignored. This implementation calls + * <code>getThreadGroup().enumerate(array)</code>, which may have a + * security check, <code>checkAccess(group)</code>. + * + * @param array the array to place the Threads into + * @return the number of Threads placed into the array + * @throws NullPointerException if array is null + * @throws SecurityException if you cannot access the ThreadGroup + * @see ThreadGroup#enumerate(Thread[]) + * @see #activeCount() + * @see SecurityManager#checkAccess(ThreadGroup) + */ + public static int enumerate(Thread[] array) + { + return currentThread().group.enumerate(array); + } + + /** + * Get this Thread's name. + * + * @return this Thread's name + */ + public final String getName() + { + VMThread t = vmThread; + return t == null ? name : t.getName(); + } + + /** + * Get this Thread's priority. + * + * @return the Thread's priority + */ + public final synchronized int getPriority() + { + VMThread t = vmThread; + return t == null ? priority : t.getPriority(); + } + + /** + * Get the ThreadGroup this Thread belongs to. If the thread has died, this + * returns null. + * + * @return this Thread's ThreadGroup + */ + public final ThreadGroup getThreadGroup() + { + return group; + } + + /** + * Checks whether the current thread holds the monitor on a given object. + * This allows you to do <code>assert Thread.holdsLock(obj)</code>. + * + * @param obj the object to test lock ownership on. + * @return true if the current thread is currently synchronized on obj + * @throws NullPointerException if obj is null + * @since 1.4 + */ + public static boolean holdsLock(Object obj) + { + return VMThread.holdsLock(obj); + } + + /** + * Interrupt this Thread. First, there is a security check, + * <code>checkAccess</code>. Then, depending on the current state of the + * thread, various actions take place: + * + * <p>If the thread is waiting because of {@link #wait()}, + * {@link #sleep(long)}, or {@link #join()}, its <i>interrupt status</i> + * will be cleared, and an InterruptedException will be thrown. Notice that + * this case is only possible if an external thread called interrupt(). + * + * <p>If the thread is blocked in an interruptible I/O operation, in + * {@link java.nio.channels.InterruptibleChannel}, the <i>interrupt + * status</i> will be set, and ClosedByInterruptException will be thrown. + * + * <p>If the thread is blocked on a {@link java.nio.channels.Selector}, the + * <i>interrupt status</i> will be set, and the selection will return, with + * a possible non-zero value, as though by the wakeup() method. + * + * <p>Otherwise, the interrupt status will be set. + * + * @throws SecurityException if you cannot modify this Thread + */ + public synchronized void interrupt() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.interrupt(); + } + + /** + * Determine whether the current Thread has been interrupted, and clear + * the <i>interrupted status</i> in the process. + * + * @return whether the current Thread has been interrupted + * @see #isInterrupted() + */ + public static boolean interrupted() + { + return VMThread.interrupted(); + } + + /** + * Determine whether the given Thread has been interrupted, but leave + * the <i>interrupted status</i> alone in the process. + * + * @return whether the Thread has been interrupted + * @see #interrupted() + */ + public boolean isInterrupted() + { + VMThread t = vmThread; + return t != null && t.isInterrupted(); + } + + /** + * Determine whether this Thread is alive. A thread which is alive has + * started and not yet died. + * + * @return whether this Thread is alive + */ + public final boolean isAlive() + { + return vmThread != null && group != null; + } + + /** + * Tell whether this is a daemon Thread or not. + * + * @return whether this is a daemon Thread or not + * @see #setDaemon(boolean) + */ + public final boolean isDaemon() + { + VMThread t = vmThread; + return t == null ? daemon : t.isDaemon(); + } + + /** + * Wait forever for the Thread in question to die. + * + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + */ + public final void join() throws InterruptedException + { + join(0, 0); + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + */ + public final void join(long ms) throws InterruptedException + { + join(ms, 0); + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + * <p>Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs do + * not offer that fine a grain of timing resolution. Besides, there is + * no guarantee that this thread can start up immediately when time expires, + * because some other thread may be active. So don't expect real-time + * performance. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is interrupted; it's + * <i>interrupted status</i> will be cleared + * @throws IllegalArgumentException if ns is invalid + */ + public final void join(long ms, int ns) throws InterruptedException + { + if(ms < 0 || ns < 0 || ns > 999999) + throw new IllegalArgumentException(); + + VMThread t = vmThread; + if(t != null) + t.join(ms, ns); + } + + /** + * Resume this Thread. If the thread is not suspended, this method does + * nothing. To mirror suspend(), there may be a security check: + * <code>checkAccess</code>. + * + * @throws SecurityException if you cannot resume the Thread + * @see #checkAccess() + * @see #suspend() + * @deprecated pointless, since suspend is deprecated + */ + public final synchronized void resume() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.resume(); + } + + /** + * The method of Thread that will be run if there is no Runnable object + * associated with the Thread. Thread's implementation does nothing at all. + * + * @see #start() + * @see #Thread(ThreadGroup, Runnable, String) + */ + public void run() + { + if (runnable != null) + runnable.run(); + } + + /** + * Set the daemon status of this Thread. If this is a daemon Thread, then + * the VM may exit even if it is still running. This may only be called + * before the Thread starts running. There may be a security check, + * <code>checkAccess</code>. + * + * @param daemon whether this should be a daemon thread or not + * @throws SecurityException if you cannot modify this Thread + * @throws IllegalThreadStateException if the Thread is active + * @see #isDaemon() + * @see #checkAccess() + */ + public final synchronized void setDaemon(boolean daemon) + { + if (vmThread != null) + throw new IllegalThreadStateException(); + checkAccess(); + this.daemon = daemon; + } + + /** + * Returns the context classloader of this Thread. The context + * classloader can be used by code that want to load classes depending + * on the current thread. Normally classes are loaded depending on + * the classloader of the current class. There may be a security check + * for <code>RuntimePermission("getClassLoader")</code> if the caller's + * class loader is not null or an ancestor of this thread's context class + * loader. + * + * @return the context class loader + * @throws SecurityException when permission is denied + * @see setContextClassLoader(ClassLoader) + * @since 1.2 + */ + public synchronized ClassLoader getContextClassLoader() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + // XXX Don't check this if the caller's class loader is an ancestor. + sm.checkPermission(new RuntimePermission("getClassLoader")); + return contextClassLoader; + } + + /** + * Sets the context classloader for this Thread. When not explicitly set, + * the context classloader for a thread is the same as the context + * classloader of the thread that created this thread. The first thread has + * as context classloader the system classloader. There may be a security + * check for <code>RuntimePermission("setContextClassLoader")</code>. + * + * @param classloader the new context class loader + * @throws SecurityException when permission is denied + * @see getContextClassLoader() + * @since 1.2 + */ + public synchronized void setContextClassLoader(ClassLoader classloader) + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + this.contextClassLoader = classloader; + } + + /** + * Set this Thread's name. There may be a security check, + * <code>checkAccess</code>. + * + * @param name the new name for this Thread + * @throws NullPointerException if name is null + * @throws SecurityException if you cannot modify this Thread + */ + public final synchronized void setName(String name) + { + checkAccess(); + // The Class Libraries book says ``threadName cannot be null''. I + // take this to mean NullPointerException. + if (name == null) + throw new NullPointerException(); + VMThread t = vmThread; + if (t != null) + t.setName(name); + else + this.name = name; + } + + /** + * Yield to another thread. The Thread will not lose any locks it holds + * during this time. There are no guarantees which thread will be + * next to run, and it could even be this one, but most VMs will choose + * the highest priority thread that has been waiting longest. + */ + public static void yield() + { + VMThread.yield(); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority thread that has been waiting longest. + * + * @param ms the number of milliseconds to sleep. + * @throws InterruptedException if the Thread is (or was) interrupted; + * it's <i>interrupted status</i> will be cleared + * @throws IllegalArgumentException if ms is negative + * @see #interrupt() + */ + public static void sleep(long ms) throws InterruptedException + { + sleep(ms, 0); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority thread that has been waiting longest. + * <p> + * Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs + * do not offer that fine a grain of timing resolution. When ms is + * zero and ns is non-zero the Thread will sleep for at least one + * milli second. There is no guarantee that this thread can start up + * immediately when time expires, because some other thread may be + * active. So don't expect real-time performance. + * + * @param ms the number of milliseconds to sleep + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is (or was) interrupted; + * it's <i>interrupted status</i> will be cleared + * @throws IllegalArgumentException if ms or ns is negative + * or ns is larger than 999999. + * @see #interrupt() + */ + public static void sleep(long ms, int ns) throws InterruptedException + { + + // Check parameters + if (ms < 0 || ns < 0 || ns > 999999) + throw new IllegalArgumentException(); + + // Really sleep + VMThread.sleep(ms, ns); + } + + /** + * Start this Thread, calling the run() method of the Runnable this Thread + * was created with, or else the run() method of the Thread itself. This + * is the only way to start a new thread; calling run by yourself will just + * stay in the same thread. The virtual machine will remove the thread from + * its thread group when the run() method completes. + * + * @throws IllegalThreadStateException if the thread has already started + * @see #run() + */ + public synchronized void start() + { + if (vmThread != null || group == null) + throw new IllegalThreadStateException(); + + VMThread.create(this, stacksize); + } + + /** + * Cause this Thread to stop abnormally because of the throw of a ThreadDeath + * error. If you stop a Thread that has not yet started, it will stop + * immediately when it is actually started. + * + * <p>This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * <code>checkAccess(this)</code>, plus another one if the current thread + * is not this: <code>RuntimePermission("stopThread")</code>. If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @throws SecurityException if you cannot stop the Thread + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final void stop() + { + stop(new ThreadDeath()); + } + + /** + * Cause this Thread to stop abnormally and throw the specified exception. + * If you stop a Thread that has not yet started, the stop is ignored + * (contrary to what the JDK documentation says). + * <b>WARNING</b>This bypasses Java security, and can throw a checked + * exception which the call stack is unprepared to handle. Do not abuse + * this power. + * + * <p>This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * <code>checkAccess(this)</code>, plus another one if the current thread + * is not this: <code>RuntimePermission("stopThread")</code>. If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @param t the Throwable to throw when the Thread dies + * @throws SecurityException if you cannot stop the Thread + * @throws NullPointerException in the calling thread, if t is null + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final synchronized void stop(Throwable t) + { + if (t == null) + throw new NullPointerException(); + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + sm.checkAccess(this); + if (this != currentThread()) + sm.checkPermission(new RuntimePermission("stopThread")); + } + VMThread vt = vmThread; + if (vt != null) + vt.stop(t); + else + stillborn = t; + } + + /** + * Suspend this Thread. It will not come back, ever, unless it is resumed. + * + * <p>This is inherently unsafe, as the suspended thread still holds locks, + * and can potentially deadlock your program. Hence, there is a security + * check: <code>checkAccess</code>. + * + * @throws SecurityException if you cannot suspend the Thread + * @see #checkAccess() + * @see #resume() + * @deprecated unsafe operation, try not to use + */ + public final synchronized void suspend() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.suspend(); + } + + /** + * Set this Thread's priority. There may be a security check, + * <code>checkAccess</code>, then the priority is set to the smaller of + * priority and the ThreadGroup maximum priority. + * + * @param priority the new priority for this Thread + * @throws IllegalArgumentException if priority exceeds MIN_PRIORITY or + * MAX_PRIORITY + * @throws SecurityException if you cannot modify this Thread + * @see #getPriority() + * @see #checkAccess() + * @see ThreadGroup#getMaxPriority() + * @see #MIN_PRIORITY + * @see #MAX_PRIORITY + */ + public final synchronized void setPriority(int priority) + { + checkAccess(); + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) + throw new IllegalArgumentException("Invalid thread priority value " + + priority + "."); + priority = Math.min(priority, group.getMaxPriority()); + VMThread t = vmThread; + if (t != null) + t.setPriority(priority); + else + this.priority = priority; + } + + /** + * Returns a string representation of this thread, including the + * thread's name, priority, and thread group. + * + * @return a human-readable String representing this Thread + */ + public String toString() + { + return ("Thread[" + name + "," + priority + "," + + (group == null ? "" : group.getName()) + "]"); + } + + /** + * Clean up code, called by VMThread when thread dies. + */ + synchronized void die() + { + group.removeThread(this); + vmThread = null; + locals = null; + } + + /** + * Returns the map used by ThreadLocal to store the thread local values. + */ + static Map getThreadLocals() + { + Thread thread = currentThread(); + Map locals = thread.locals; + if (locals == null) + { + locals = thread.locals = new WeakHashMap(); + } + return locals; + } +} diff --git a/libjava/classpath/java/lang/ThreadDeath.java b/libjava/classpath/java/lang/ThreadDeath.java new file mode 100644 index 00000000000..c7d88fb2a25 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadDeath.java @@ -0,0 +1,68 @@ +/* ThreadDeath.java - special exception registering Thread death + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * ThreadDeath is thrown in a thread when someone calls <code>stop()</code> + * on that thread. <b>Important:</b> Make sure you rethrow this exception + * if you catch it. If you don't, the thread will not die. + * + * <p>This is an Error rather than an exception, so that normal code will + * not catch it. It is intended for asynchronous cleanup when using the + * deprecated Thread.stop() method. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread#stop() + * @status updated to 1.4 + */ +public class ThreadDeath extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4417128565033088268L; + + /** + * Create an error without a message. + */ + public ThreadDeath() + { + } +} diff --git a/libjava/classpath/java/lang/ThreadGroup.java b/libjava/classpath/java/lang/ThreadGroup.java new file mode 100644 index 00000000000..6e4c27a7135 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadGroup.java @@ -0,0 +1,749 @@ +/* ThreadGroup -- a group of Threads + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.util.Vector; + +/** + * ThreadGroup allows you to group Threads together. There is a hierarchy + * of ThreadGroups, and only the initial ThreadGroup has no parent. A Thread + * may access information about its own ThreadGroup, but not its parents or + * others outside the tree. + * + * @author John Keiser + * @author Tom Tromey + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Thread + * @since 1.0 + * @status updated to 1.4 + */ +public class ThreadGroup +{ + /** The Initial, top-level ThreadGroup. */ + static ThreadGroup root = new ThreadGroup(); + + /** + * This flag is set if an uncaught exception occurs. The runtime should + * check this and exit with an error status if it is set. + */ + static boolean had_uncaught_exception; + + /** The parent thread group. */ + private final ThreadGroup parent; + + /** The group name, non-null. */ + final String name; + + /** The threads in the group. */ + private final Vector threads = new Vector(); + + /** Child thread groups, or null when this group is destroyed. */ + private Vector groups = new Vector(); + + /** If all threads in the group are daemons. */ + private boolean daemon_flag = false; + + /** The maximum group priority. */ + private int maxpri; + + /** + * Hidden constructor to build the root node. + */ + private ThreadGroup() + { + name = "main"; + parent = null; + maxpri = Thread.MAX_PRIORITY; + } + + /** + * Create a new ThreadGroup using the given name and the current thread's + * ThreadGroup as a parent. There may be a security check, + * <code>checkAccess</code>. + * + * @param name the name to use for the ThreadGroup + * @throws SecurityException if the current thread cannot create a group + * @see #checkAccess() + */ + public ThreadGroup(String name) + { + this(Thread.currentThread().group, name); + } + + /** + * Create a new ThreadGroup using the given name and parent group. The new + * group inherits the maximum priority and daemon status of its parent + * group. There may be a security check, <code>checkAccess</code>. + * + * @param name the name to use for the ThreadGroup + * @param parent the ThreadGroup to use as a parent + * @throws NullPointerException if parent is null + * @throws SecurityException if the current thread cannot create a group + * @throws IllegalThreadStateException if the parent is destroyed + * @see #checkAccess() + */ + public ThreadGroup(ThreadGroup parent, String name) + { + parent.checkAccess(); + this.parent = parent; + this.name = name; + maxpri = parent.maxpri; + daemon_flag = parent.daemon_flag; + synchronized (parent) + { + if (parent.groups == null) + throw new IllegalThreadStateException(); + parent.groups.add(this); + } + } + + /** + * Get the name of this ThreadGroup. + * + * @return the name of this ThreadGroup + */ + public final String getName() + { + return name; + } + + /** + * Get the parent of this ThreadGroup. If the parent is not null, there + * may be a security check, <code>checkAccess</code>. + * + * @return the parent of this ThreadGroup + * @throws SecurityException if permission is denied + */ + public final ThreadGroup getParent() + { + if (parent != null) + parent.checkAccess(); + return parent; + } + + /** + * Get the maximum priority of Threads in this ThreadGroup. Threads created + * after this call in this group may not exceed this priority. + * + * @return the maximum priority of Threads in this ThreadGroup + */ + public final int getMaxPriority() + { + return maxpri; + } + + /** + * Tell whether this ThreadGroup is a daemon group. A daemon group will + * be automatically destroyed when its last thread is stopped and + * its last thread group is destroyed. + * + * @return whether this ThreadGroup is a daemon group + */ + public final boolean isDaemon() + { + return daemon_flag; + } + + /** + * Tell whether this ThreadGroup has been destroyed or not. + * + * @return whether this ThreadGroup has been destroyed or not + * @since 1.1 + */ + public synchronized boolean isDestroyed() + { + return groups == null; + } + + /** + * Set whether this ThreadGroup is a daemon group. A daemon group will be + * destroyed when its last thread is stopped and its last thread group is + * destroyed. There may be a security check, <code>checkAccess</code>. + * + * @param daemon whether this ThreadGroup should be a daemon group + * @throws SecurityException if you cannot modify this ThreadGroup + * @see #checkAccess() + */ + public final void setDaemon(boolean daemon) + { + checkAccess(); + daemon_flag = daemon; + } + + /** + * Set the maximum priority for Threads in this ThreadGroup. setMaxPriority + * can only be used to reduce the current maximum. If maxpri is greater + * than the current Maximum of the parent group, the current value is not + * changed. Otherwise, all groups which belong to this have their priority + * adjusted as well. Calling this does not affect threads already in this + * ThreadGroup. There may be a security check, <code>checkAccess</code>. + * + * @param maxpri the new maximum priority for this ThreadGroup + * @throws SecurityException if you cannot modify this ThreadGroup + * @see #getMaxPriority() + * @see #checkAccess() + */ + public final synchronized void setMaxPriority(int maxpri) + { + checkAccess(); + if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY) + return; + if (parent != null && maxpri > parent.maxpri) + maxpri = parent.maxpri; + this.maxpri = maxpri; + if (groups == null) + return; + int i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).setMaxPriority(maxpri); + } + + /** + * Check whether this ThreadGroup is an ancestor of the specified + * ThreadGroup, or if they are the same. + * + * @param group the group to test on + * @return whether this ThreadGroup is a parent of the specified group + */ + public final boolean parentOf(ThreadGroup group) + { + while (group != null) + { + if (group == this) + return true; + group = group.parent; + } + return false; + } + + /** + * Find out if the current Thread can modify this ThreadGroup. This passes + * the check on to <code>SecurityManager.checkAccess(this)</code>. + * + * @throws SecurityException if the current Thread cannot modify this + * ThreadGroup + * @see SecurityManager#checkAccess(ThreadGroup) + */ + public final void checkAccess() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkAccess(this); + } + + /** + * Return an estimate of the total number of active threads in this + * ThreadGroup and all its descendants. This cannot return an exact number, + * since the status of threads may change after they were counted; but it + * should be pretty close. Based on a JDC bug, + * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html"> + * 4089701</a>, we take active to mean isAlive(). + * + * @return count of active threads in this ThreadGroup and its descendants + */ + public int activeCount() + { + int total = 0; + if (groups == null) + return total; + int i = threads.size(); + while (--i >= 0) + if (((Thread) threads.get(i)).isAlive()) + total++; + i = groups.size(); + while (--i >= 0) + total += ((ThreadGroup) groups.get(i)).activeCount(); + return total; + } + + /** + * Copy all of the active Threads from this ThreadGroup and its descendants + * into the specified array. If the array is not big enough to hold all + * the Threads, extra Threads will simply not be copied. There may be a + * security check, <code>checkAccess</code>. + * + * @param array the array to put the threads into + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #activeCount() + * @see #checkAccess() + * @see #enumerate(Thread[], boolean) + */ + public int enumerate(Thread[] array) + { + return enumerate(array, 0, true); + } + + /** + * Copy all of the active Threads from this ThreadGroup and, if desired, + * from its descendants, into the specified array. If the array is not big + * enough to hold all the Threads, extra Threads will simply not be copied. + * There may be a security check, <code>checkAccess</code>. + * + * @param array the array to put the threads into + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #activeCount() + * @see #checkAccess() + */ + public int enumerate(Thread[] array, boolean recurse) + { + return enumerate(array, 0, recurse); + } + + /** + * Get the number of active groups in this ThreadGroup. This group itself + * is not included in the count. A sub-group is active if it has not been + * destroyed. This cannot return an exact number, since the status of + * threads may change after they were counted; but it should be pretty close. + * + * @return the number of active groups in this ThreadGroup + */ + public int activeGroupCount() + { + if (groups == null) + return 0; + int total = groups.size(); + int i = total; + while (--i >= 0) + total += ((ThreadGroup) groups.get(i)).activeGroupCount(); + return total; + } + + /** + * Copy all active ThreadGroups that are descendants of this ThreadGroup + * into the specified array. If the array is not large enough to hold all + * active ThreadGroups, extra ThreadGroups simply will not be copied. There + * may be a security check, <code>checkAccess</code>. + * + * @param array the array to put the ThreadGroups into + * @return the number of ThreadGroups copied into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #activeCount() + * @see #checkAccess() + * @see #enumerate(ThreadGroup[], boolean) + */ + public int enumerate(ThreadGroup[] array) + { + return enumerate(array, 0, true); + } + + /** + * Copy all active ThreadGroups that are children of this ThreadGroup into + * the specified array, and if desired, also all descendents. If the array + * is not large enough to hold all active ThreadGroups, extra ThreadGroups + * simply will not be copied. There may be a security check, + * <code>checkAccess</code>. + * + * @param array the array to put the ThreadGroups into + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of ThreadGroups copied into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #activeCount() + * @see #checkAccess() + */ + public int enumerate(ThreadGroup[] array, boolean recurse) + { + return enumerate(array, 0, recurse); + } + + /** + * Stop all Threads in this ThreadGroup and its descendants. + * + * <p>This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * <code>checkAccess()</code>, followed by further checks on each thread + * being stopped. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#stop(Throwable) + * @deprecated unsafe operation, try not to use + */ + public final synchronized void stop() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).stop(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).stop(); + } + + /** + * Interrupt all Threads in this ThreadGroup and its sub-groups. There may + * be a security check, <code>checkAccess</code>. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#interrupt() + * @since 1.2 + */ + public final synchronized void interrupt() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).interrupt(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).interrupt(); + } + + /** + * Suspend all Threads in this ThreadGroup and its descendants. + * + * <p>This is inherently unsafe, as suspended threads still hold locks, + * which can lead to deadlock. Hence, there is a security check: + * <code>checkAccess()</code>, followed by further checks on each thread + * being suspended. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#suspend() + * @deprecated unsafe operation, try not to use + */ + public final synchronized void suspend() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).suspend(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).suspend(); + } + + /** + * Resume all suspended Threads in this ThreadGroup and its descendants. + * To mirror suspend(), there is a security check: + * <code>checkAccess()</code>, followed by further checks on each thread + * being resumed. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#suspend() + * @deprecated pointless, since suspend is deprecated + */ + public final synchronized void resume() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).resume(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).resume(); + } + + /** + * Destroy this ThreadGroup. The group must be empty, meaning that all + * threads and sub-groups have completed execution. Daemon groups are + * destroyed automatically. There may be a security check, + * <code>checkAccess</code>. + * + * @throws IllegalThreadStateException if the ThreadGroup is not empty, or + * was previously destroyed + * @throws SecurityException if permission is denied + * @see #checkAccess() + */ + public final synchronized void destroy() + { + checkAccess(); + if (! threads.isEmpty() || groups == null) + throw new IllegalThreadStateException(); + int i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).destroy(); + groups = null; + if (parent != null) + parent.removeGroup(this); + } + + /** + * Print out information about this ThreadGroup to System.out. This is + * meant for debugging purposes. <b>WARNING:</b> This method is not secure, + * and can print the name of threads to standard out even when you cannot + * otherwise get at such threads. + */ + public void list() + { + list(""); + } + + /** + * When a Thread in this ThreadGroup does not catch an exception, the + * virtual machine calls this method. The default implementation simply + * passes the call to the parent; then in top ThreadGroup, it will + * ignore ThreadDeath and print the stack trace of any other throwable. + * Override this method if you want to handle the exception in a different + * manner. + * + * @param thread the thread that exited + * @param t the uncaught throwable + * @throws NullPointerException if t is null + * @see ThreadDeath + * @see System#err + * @see Throwable#printStackTrace() + */ + public void uncaughtException(Thread thread, Throwable t) + { + if (parent != null) + parent.uncaughtException(thread, t); + else if (! (t instanceof ThreadDeath)) + { + if (t == null) + throw new NullPointerException(); + had_uncaught_exception = true; + try + { + if (thread != null) + System.err.print("Exception in thread \"" + thread.name + "\" "); + t.printStackTrace(System.err); + } + catch (Throwable x) + { + // This means that something is badly screwed up with the runtime, + // or perhaps someone overloaded the Throwable.printStackTrace to + // die. In any case, try to deal with it gracefully. + try + { + System.err.println(t); + System.err.println("*** Got " + x + + " while trying to print stack trace."); + } + catch (Throwable x2) + { + // Here, someone may have overloaded t.toString() or + // x.toString() to die. Give up all hope; we can't even chain + // the exception, because the chain would likewise die. + System.err.println("*** Catastrophic failure while handling " + + "uncaught exception."); + throw new InternalError(); + } + } + } + } + + /** + * Originally intended to tell the VM whether it may suspend Threads in + * low memory situations, this method was never implemented by Sun, and + * is hence a no-op. + * + * @param allow whether to allow low-memory thread suspension; ignored + * @return false + * @since 1.1 + * @deprecated pointless, since suspend is deprecated + */ + public boolean allowThreadSuspension(boolean allow) + { + return false; + } + + /** + * Return a human-readable String representing this ThreadGroup. The format + * of the string is:<br> + * <code>getClass().getName() + "[name=" + getName() + ",maxpri=" + * + getMaxPriority() + ']'</code>. + * + * @return a human-readable String representing this ThreadGroup + */ + public String toString() + { + return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']'; + } + + /** + * Implements enumerate. + * + * @param list the array to put the threads into + * @param next the next open slot in the array + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if list is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #enumerate(Thread[]) + * @see #enumerate(Thread[], boolean) + */ + private int enumerate(Thread[] list, int next, boolean recurse) + { + checkAccess(); + if (groups == null) + return next; + int i = threads.size(); + while (--i >= 0 && next < list.length) + { + Thread t = (Thread) threads.get(i); + if (t.isAlive()) + list[next++] = t; + } + if (recurse) + { + i = groups.size(); + while (--i >= 0 && next < list.length) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + next = g.enumerate(list, next, true); + } + } + return next; + } + + /** + * Implements enumerate. + * + * @param list the array to put the groups into + * @param next the next open slot in the array + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of groups put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if list is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #enumerate(ThreadGroup[]) + * @see #enumerate(ThreadGroup[], boolean) + */ + private int enumerate(ThreadGroup[] list, int next, boolean recurse) + { + checkAccess(); + if (groups == null) + return next; + int i = groups.size(); + while (--i >= 0 && next < list.length) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + list[next++] = g; + if (recurse && next != list.length) + next = g.enumerate(list, next, true); + } + return next; + } + + /** + * Implements list. + * + * @param indentation the current level of indentation + * @see #list() + */ + private void list(String indentation) + { + if (groups == null) + return; + System.out.println(indentation + this); + indentation += " "; + int i = threads.size(); + while (--i >= 0) + System.out.println(indentation + threads.get(i)); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).list(indentation); + } + + /** + * Add a thread to the group. Called by Thread constructors. + * + * @param t the thread to add, non-null + * @throws IllegalThreadStateException if the group is destroyed + */ + final synchronized void addThread(Thread t) + { + if (groups == null) + throw new IllegalThreadStateException("ThreadGroup is destroyed"); + threads.add(t); + } + + /** + * Called by the VM to remove a thread that has died. + * + * @param t the thread to remove, non-null + * @XXX A ThreadListener to call this might be nice. + */ + final synchronized void removeThread(Thread t) + { + if (groups == null) + return; + threads.remove(t); + t.group = null; + // Daemon groups are automatically destroyed when all their threads die. + if (daemon_flag && groups.size() == 0 && threads.size() == 0) + { + // We inline destroy to avoid the access check. + groups = null; + if (parent != null) + parent.removeGroup(this); + } + } + + /** + * Called when a group is destroyed, to remove it from its parent. + * + * @param g the destroyed group, non-null + */ + final synchronized void removeGroup(ThreadGroup g) + { + groups.remove(g); + // Daemon groups are automatically destroyed when all their threads die. + if (daemon_flag && groups.size() == 0 && threads.size() == 0) + { + // We inline destroy to avoid the access check. + groups = null; + if (parent != null) + parent.removeGroup(this); + } + } +} // class ThreadGroup diff --git a/libjava/classpath/java/lang/ThreadLocal.java b/libjava/classpath/java/lang/ThreadLocal.java new file mode 100644 index 00000000000..0b2b60867b9 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadLocal.java @@ -0,0 +1,171 @@ +/* ThreadLocal -- a variable with a unique value per thread + Copyright (C) 2000, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.util.Map; +import java.util.WeakHashMap; + + +/** + * ThreadLocal objects have a different state associated with every + * Thread that accesses them. Every access to the ThreadLocal object + * (through the <code>get()</code> and <code>set()</code> methods) + * only affects the state of the object as seen by the currently + * executing Thread. + * + * <p>The first time a ThreadLocal object is accessed on a particular + * Thread, the state for that Thread's copy of the local variable is set by + * executing the method <code>initialValue()</code>. + * </p> + * + * <p>An example how you can use this: + * </p> + * + * <pre> + * class Connection + * { + * private static ThreadLocal owner = new ThreadLocal() + * { + * public Object initialValue() + * { + * return("nobody"); + * } + * }; + * ... + * } + * </pre> + * + * <p>Now all instances of connection can see who the owner of the currently + * executing Thread is by calling <code>owner.get()</code>. By default any + * Thread would be associated with 'nobody'. But the Connection object could + * offer a method that changes the owner associated with the Thread on + * which the method was called by calling <code>owner.put("somebody")</code>. + * (Such an owner changing method should then be guarded by security checks.) + * </p> + * + * <p>When a Thread is garbage collected all references to values of + * the ThreadLocal objects associated with that Thread are removed. + * </p> + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public class ThreadLocal +{ + /** + * Placeholder to distinguish between uninitialized and null set by the + * user. Do not expose this to the public. Package visible for use by + * InheritableThreadLocal + */ + static final Object NULL = new Object(); + + /** + * Serves as a key for the Thread.locals WeakHashMap. + * We can't use "this", because a subclass may override equals/hashCode + * and we need to use object identity for the map. + */ + final Key key = new Key(); + + class Key + { + ThreadLocal get() + { + return ThreadLocal.this; + } + } + + /** + * Creates a ThreadLocal object without associating any value to it yet. + */ + public ThreadLocal() + { + } + + /** + * Called once per thread on the first invocation of get(), if set() was + * not already called. The default implementation returns <code>null</code>. + * Often, this method is overridden to create the appropriate initial object + * for the current thread's view of the ThreadLocal. + * + * @return the initial value of the variable in this thread + */ + protected Object initialValue() + { + return null; + } + + /** + * Gets the value associated with the ThreadLocal object for the currently + * executing Thread. If this is the first time the current thread has called + * get(), and it has not already called set(), the value is obtained by + * <code>initialValue()</code>. + * + * @return the value of the variable in this thread + */ + public Object get() + { + Map map = Thread.getThreadLocals(); + // Note that we don't have to synchronize, as only this thread will + // ever modify the map. + Object value = map.get(key); + if (value == null) + { + value = initialValue(); + map.put(key, value == null ? NULL : value); + } + return value == NULL ? null : value; + } + + /** + * Sets the value associated with the ThreadLocal object for the currently + * executing Thread. This overrides any existing value associated with the + * current Thread and prevents <code>initialValue()</code> from being + * called if this is the first access to this ThreadLocal in this Thread. + * + * @param value the value to set this thread's view of the variable to + */ + public void set(Object value) + { + Map map = Thread.getThreadLocals(); + // Note that we don't have to synchronize, as only this thread will + // ever modify the map. + map.put(key, value == null ? NULL : value); + } +} diff --git a/libjava/classpath/java/lang/Throwable.java b/libjava/classpath/java/lang/Throwable.java new file mode 100644 index 00000000000..c47a14bf040 --- /dev/null +++ b/libjava/classpath/java/lang/Throwable.java @@ -0,0 +1,563 @@ +/* java.lang.Throwable -- Root class for all Exceptions and Errors + Copyright (C) 1998, 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.SystemProperties; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +/** + * Throwable is the superclass of all exceptions that can be raised. + * + * <p>There are two special cases: {@link Error} and {@link RuntimeException}: + * these two classes (and their subclasses) are considered unchecked + * exceptions, and are either frequent enough or catastrophic enough that you + * do not need to declare them in <code>throws</code> clauses. Everything + * else is a checked exception, and is ususally a subclass of + * {@link Exception}; these exceptions have to be handled or declared. + * + * <p>Instances of this class are usually created with knowledge of the + * execution context, so that you can get a stack trace of the problem spot + * in the code. Also, since JDK 1.4, Throwables participate in "exception + * chaining." This means that one exception can be caused by another, and + * preserve the information of the original. + * + * <p>One reason this is useful is to wrap exceptions to conform to an + * interface. For example, it would be bad design to require all levels + * of a program interface to be aware of the low-level exceptions thrown + * at one level of abstraction. Another example is wrapping a checked + * exception in an unchecked one, to communicate that failure occured + * while still obeying the method throws clause of a superclass. + * + * <p>A cause is assigned in one of two ways; but can only be assigned once + * in the lifetime of the Throwable. There are new constructors added to + * several classes in the exception hierarchy that directly initialize the + * cause, or you can use the <code>initCause</code> method. This second + * method is especially useful if the superclass has not been retrofitted + * with new constructors:<br> + * <pre> + * try + * { + * lowLevelOp(); + * } + * catch (LowLevelException lle) + * { + * throw (HighLevelException) new HighLevelException().initCause(lle); + * } + * </pre> + * Notice the cast in the above example; without it, your method would need + * a throws clase that declared Throwable, defeating the purpose of chainig + * your exceptions. + * + * <p>By convention, exception classes have two constructors: one with no + * arguments, and one that takes a String for a detail message. Further, + * classes which are likely to be used in an exception chain also provide + * a constructor that takes a Throwable, with or without a detail message + * string. + * + * <p>Another 1.4 feature is the StackTrace, a means of reflection that + * allows the program to inspect the context of the exception, and which is + * serialized, so that remote procedure calls can correctly pass exceptions. + * + * @author Brian Jones + * @author John Keiser + * @author Mark Wielaard + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Throwable implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3042686055658047285L; + + /** + * The detail message. + * + * @serial specific details about the exception, may be null + */ + private final String detailMessage; + + /** + * The cause of the throwable, including null for an unknown or non-chained + * cause. This may only be set once; so the field is set to + * <code>this</code> until initialized. + * + * @serial the cause, or null if unknown, or this if not yet set + * @since 1.4 + */ + private Throwable cause = this; + + /** + * The stack trace, in a serialized form. + * + * @serial the elements of the stack trace; this is non-null, and has + * no null entries + * @since 1.4 + */ + private StackTraceElement[] stackTrace; + + /** + * Instantiate this Throwable with an empty message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + */ + public Throwable() + { + this((String) null); + } + + /** + * Instantiate this Throwable with the given message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + * + * @param message the message to associate with the Throwable + */ + public Throwable(String message) + { + fillInStackTrace(); + detailMessage = message; + } + + /** + * Instantiate this Throwable with the given message and cause. Note that + * the message is unrelated to the message of the cause. + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param message the message to associate with the Throwable + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(String message, Throwable cause) + { + this(message); + this.cause = cause; + } + + /** + * Instantiate this Throwable with the given cause. The message is then + * built as <code>cause == null ? null : cause.toString()</code>. + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Get the message associated with this Throwable. + * + * @return the error message associated with this Throwable, may be null + */ + public String getMessage() + { + return detailMessage; + } + + /** + * Get a localized version of this Throwable's error message. + * This method must be overridden in a subclass of Throwable + * to actually produce locale-specific methods. The Throwable + * implementation just returns getMessage(). + * + * @return a localized version of this error message + * @see #getMessage() + * @since 1.1 + */ + public String getLocalizedMessage() + { + return getMessage(); + } + + /** + * Returns the cause of this exception, or null if the cause is not known + * or non-existant. This cause is initialized by the new constructors, + * or by calling initCause. + * + * @return the cause of this Throwable + * @since 1.4 + */ + public Throwable getCause() + { + return cause == this ? null : cause; + } + + /** + * Initialize the cause of this Throwable. This may only be called once + * during the object lifetime, including implicitly by chaining + * constructors. + * + * @param cause the cause of this Throwable, may be null + * @return this + * @throws IllegalArgumentException if cause is this (a Throwable can't be + * its own cause!) + * @throws IllegalStateException if the cause has already been set + * @since 1.4 + */ + public Throwable initCause(Throwable cause) + { + if (cause == this) + throw new IllegalArgumentException(); + if (this.cause != this) + throw new IllegalStateException(); + this.cause = cause; + return this; + } + + /** + * Get a human-readable representation of this Throwable. The detail message + * is retrieved by getLocalizedMessage(). Then, with a null detail + * message, this string is simply the object's class name; otherwise + * the string is <code>getClass().getName() + ": " + message</code>. + * + * @return a human-readable String represting this Throwable + */ + public String toString() + { + String msg = getLocalizedMessage(); + return getClass().getName() + (msg == null ? "" : ": " + msg); + } + + /** + * Print a stack trace to the standard error stream. This stream is the + * current contents of <code>System.err</code>. The first line of output + * is the result of {@link #toString()}, and the remaining lines represent + * the data created by {@link #fillInStackTrace()}. While the format is + * unspecified, this implementation uses the suggested format, demonstrated + * by this example:<br> + * <pre> + * public class Junk + * { + * public static void main(String args[]) + * { + * try + * { + * a(); + * } + * catch(HighLevelException e) + * { + * e.printStackTrace(); + * } + * } + * static void a() throws HighLevelException + * { + * try + * { + * b(); + * } + * catch(MidLevelException e) + * { + * throw new HighLevelException(e); + * } + * } + * static void b() throws MidLevelException + * { + * c(); + * } + * static void c() throws MidLevelException + * { + * try + * { + * d(); + * } + * catch(LowLevelException e) + * { + * throw new MidLevelException(e); + * } + * } + * static void d() throws LowLevelException + * { + * e(); + * } + * static void e() throws LowLevelException + * { + * throw new LowLevelException(); + * } + * } + * class HighLevelException extends Exception + * { + * HighLevelException(Throwable cause) { super(cause); } + * } + * class MidLevelException extends Exception + * { + * MidLevelException(Throwable cause) { super(cause); } + * } + * class LowLevelException extends Exception + * { + * } + * </pre> + * <p> + * <pre> + * HighLevelException: MidLevelException: LowLevelException + * at Junk.a(Junk.java:13) + * at Junk.main(Junk.java:4) + * Caused by: MidLevelException: LowLevelException + * at Junk.c(Junk.java:23) + * at Junk.b(Junk.java:17) + * at Junk.a(Junk.java:11) + * ... 1 more + * Caused by: LowLevelException + * at Junk.e(Junk.java:30) + * at Junk.d(Junk.java:27) + * at Junk.c(Junk.java:21) + * ... 3 more + * </pre> + */ + public void printStackTrace() + { + printStackTrace(System.err); + } + + /** + * Print a stack trace to the specified PrintStream. See + * {@link #printStackTrace()} for the sample format. + * + * @param s the PrintStream to write the trace to + */ + public void printStackTrace(PrintStream s) + { + s.print(stackTraceString()); + } + + /** + * Prints the exception, the detailed message and the stack trace + * associated with this Throwable to the given <code>PrintWriter</code>. + * The actual output written is implemention specific. Use the result of + * <code>getStackTrace()</code> when more precise information is needed. + * + * <p>This implementation first prints a line with the result of this + * object's <code>toString()</code> method. + * <br> + * Then for all elements given by <code>getStackTrace</code> it prints + * a line containing three spaces, the string "at " and the result of calling + * the <code>toString()</code> method on the <code>StackTraceElement</code> + * object. If <code>getStackTrace()</code> returns an empty array it prints + * a line containing three spaces and the string + * "<<No stacktrace available>>". + * <br> + * Then if <code>getCause()</code> doesn't return null it adds a line + * starting with "Caused by: " and the result of calling + * <code>toString()</code> on the cause. + * <br> + * Then for every cause (of a cause, etc) the stacktrace is printed the + * same as for the top level <code>Throwable</code> except that as soon + * as all the remaining stack frames of the cause are the same as the + * the last stack frames of the throwable that the cause is wrapped in + * then a line starting with three spaces and the string "... X more" is + * printed, where X is the number of remaining stackframes. + * + * @param pw the PrintWriter to write the trace to + * @since 1.1 + */ + public void printStackTrace (PrintWriter pw) + { + pw.print(stackTraceString()); + } + + /* + * We use inner class to avoid a static initializer in this basic class. + */ + private static class StaticData + { + static final String nl = SystemProperties.getProperty("line.separator"); + } + + // Create whole stack trace in a stringbuffer so we don't have to print + // it line by line. This prevents printing multiple stack traces from + // different threads to get mixed up when written to the same PrintWriter. + private String stackTraceString() + { + StringBuffer sb = new StringBuffer(); + + // Main stacktrace + StackTraceElement[] stack = getStackTrace(); + stackTraceStringBuffer(sb, this.toString(), stack, 0); + + // The cause(s) + Throwable cause = getCause(); + while (cause != null) + { + // Cause start first line + sb.append("Caused by: "); + + // Cause stacktrace + StackTraceElement[] parentStack = stack; + stack = cause.getStackTrace(); + if (parentStack == null || parentStack.length == 0) + stackTraceStringBuffer(sb, cause.toString(), stack, 0); + else + { + int equal = 0; // Count how many of the last stack frames are equal + int frame = stack.length-1; + int parentFrame = parentStack.length-1; + while (frame > 0 && parentFrame > 0) + { + if (stack[frame].equals(parentStack[parentFrame])) + { + equal++; + frame--; + parentFrame--; + } + else + break; + } + stackTraceStringBuffer(sb, cause.toString(), stack, equal); + } + cause = cause.getCause(); + } + + return sb.toString(); + } + + // Adds to the given StringBuffer a line containing the name and + // all stacktrace elements minus the last equal ones. + private static void stackTraceStringBuffer(StringBuffer sb, String name, + StackTraceElement[] stack, int equal) + { + String nl = StaticData.nl; + // (finish) first line + sb.append(name); + sb.append(nl); + + // The stacktrace + if (stack == null || stack.length == 0) + { + sb.append(" <<No stacktrace available>>"); + sb.append(nl); + } + else + { + for (int i = 0; i < stack.length-equal; i++) + { + sb.append(" at "); + sb.append(stack[i] == null ? "<<Unknown>>" : stack[i].toString()); + sb.append(nl); + } + if (equal > 0) + { + sb.append(" ..."); + sb.append(equal); + sb.append(" more"); + sb.append(nl); + } + } + } + + /** + * Fill in the stack trace with the current execution stack. + * + * @return this same throwable + * @see #printStackTrace() + */ + public Throwable fillInStackTrace() + { + vmState = VMThrowable.fillInStackTrace(this); + stackTrace = null; // Should be regenerated when used. + + return this; + } + + /** + * Provides access to the information printed in {@link #printStackTrace()}. + * The array is non-null, with no null entries, although the virtual + * machine is allowed to skip stack frames. If the array is not 0-length, + * then slot 0 holds the information on the stack frame where the Throwable + * was created (or at least where <code>fillInStackTrace()</code> was + * called). + * + * @return an array of stack trace information, as available from the VM + * @since 1.4 + */ + public StackTraceElement[] getStackTrace() + { + if (stackTrace == null) + if (vmState == null) + stackTrace = new StackTraceElement[0]; + else + { + stackTrace = vmState.getStackTrace(this); + vmState = null; // No longer needed + } + + return stackTrace; + } + + /** + * Change the stack trace manually. This method is designed for remote + * procedure calls, which intend to alter the stack trace before or after + * serialization according to the context of the remote call. + * <p> + * The contents of the given stacktrace is copied so changes to the + * original array do not change the stack trace elements of this + * throwable. + * + * @param stackTrace the new trace to use + * @throws NullPointerException if stackTrace is null or has null elements + * @since 1.4 + */ + public void setStackTrace(StackTraceElement[] stackTrace) + { + int i = stackTrace.length; + StackTraceElement[] st = new StackTraceElement[i]; + + while (--i >= 0) + { + st[i] = stackTrace[i]; + if (st[i] == null) + throw new NullPointerException("Element " + i + " null"); + } + + this.stackTrace = st; + } + + /** + * VM state when fillInStackTrace was called. + * Used by getStackTrace() to get an array of StackTraceElements. + * Cleared when no longer needed. + */ + private transient VMThrowable vmState; +} diff --git a/libjava/classpath/java/lang/TypeNotPresentException.java b/libjava/classpath/java/lang/TypeNotPresentException.java new file mode 100644 index 00000000000..3010c968b6d --- /dev/null +++ b/libjava/classpath/java/lang/TypeNotPresentException.java @@ -0,0 +1,97 @@ +/* TypeNotPresentException.java -- Thrown when a string-based type is missing + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * <p> + * Thrown when a type is accessed using a <code>String</code>-based + * representation, but no definition of the supplied type is found. + * This is effectively an unchecked equivalent of the existing + * <code>ClassNotFound</code> exception. + * </p> + * <p> + * It may occur due to an attempt to load a missing class, interface or + * annotation, or when an undefined type variable is accessed. + * </p> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see ClassNotFoundException + * @since 1.5 + */ +public class TypeNotPresentException + extends RuntimeException +{ + + /** + * Constructs a <code>TypeNotPresentException</code> for + * the supplied type. The specified cause <code>Throwable</code> + * may be used to provide additional history, with regards to the + * root of the problem. It is perfectly valid for this to be null, + * if the cause of the problem is unknown. + * + * @param typeName the name of the missing type. + * @param cause the cause of this exception, or null if the cause + * is unknown. + */ + public TypeNotPresentException(String typeName, Throwable cause) + { + super("type \"" + typeName + "\" not found", cause); + this.typeName = typeName; + } + + /** + * Returns the name of the missing type. + * + * @return the missing type's name. + */ + public String typeName() + { + return typeName; + } + + /** + * The name of the missing type. + * + * @serial the missing type's name. + */ + // Name fixed by serialization. + private String typeName; + +} diff --git a/libjava/classpath/java/lang/UnknownError.java b/libjava/classpath/java/lang/UnknownError.java new file mode 100644 index 00000000000..7b317bd2aa2 --- /dev/null +++ b/libjava/classpath/java/lang/UnknownError.java @@ -0,0 +1,72 @@ +/* UnknownError.java -- thrown when the VM cannot provide more information + about a catastrophic error + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>UnknownError</code> is thrown when a serious but unknown + * problem has occurred in the Java Virtual Machine. + * + * @author Brian Jones + * @status updated to 1.4 + */ +public class UnknownError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 2524784860676771849L; + + /** + * Create an error without a message. + */ + public UnknownError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnknownError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsatisfiedLinkError.java b/libjava/classpath/java/lang/UnsatisfiedLinkError.java new file mode 100644 index 00000000000..0d513d8e0ca --- /dev/null +++ b/libjava/classpath/java/lang/UnsatisfiedLinkError.java @@ -0,0 +1,74 @@ +/* UnsatisfiedLinkError.java -- thrown when a native method cannot be loaded + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>UnsatisfiedLinkError</code> is thrown if an appropriate + * native language definition of a method declared <code>native</code> + * cannot be found by the Java Virtual Machine. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @see Runtime + * @status updated to 1.4 + */ +public class UnsatisfiedLinkError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4019343241616879428L; + + /** + * Create an error without a message. + */ + public UnsatisfiedLinkError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnsatisfiedLinkError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsupportedClassVersionError.java b/libjava/classpath/java/lang/UnsupportedClassVersionError.java new file mode 100644 index 00000000000..d6974b7694d --- /dev/null +++ b/libjava/classpath/java/lang/UnsupportedClassVersionError.java @@ -0,0 +1,74 @@ +/* UnsupportedClassVersionError.java -- thrown when a class file version + exceeds the capability of the virtual machine + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An <code>UnsupportedClassVersionError</code> is thrown when the + * Java Virtual Machine determines it does not support the major and minor + * version numbers in the class file it is attempting to read. + * + * @author Brian Jones + * @since 1.2 + * @status updated to 1.4 + */ +public class UnsupportedClassVersionError extends ClassFormatError +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7123279212883497373L; + + /** + * Create an error without a message. + */ + public UnsupportedClassVersionError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnsupportedClassVersionError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsupportedOperationException.java b/libjava/classpath/java/lang/UnsupportedOperationException.java new file mode 100644 index 00000000000..0387d0ee29d --- /dev/null +++ b/libjava/classpath/java/lang/UnsupportedOperationException.java @@ -0,0 +1,73 @@ +/* UnsupportedOperationException.java -- thrown when an operation is not + supported + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception is thrown by an object when an operation is + * requested of it that it does not support. + * + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class UnsupportedOperationException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1242599979055084673L; + + /** + * Create an exception without a message. + */ + public UnsupportedOperationException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnsupportedOperationException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/VerifyError.java b/libjava/classpath/java/lang/VerifyError.java new file mode 100644 index 00000000000..350ceaa5e95 --- /dev/null +++ b/libjava/classpath/java/lang/VerifyError.java @@ -0,0 +1,72 @@ +/* VerifyError.java -- thrown when a class fails verification + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>VerifyError</code> is thrown if there is a security problem or + * internal inconsistency in a class file as detected by the "verifier." + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class VerifyError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 7001962396098498785L; + + /** + * Create an error without a message. + */ + public VerifyError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public VerifyError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/VirtualMachineError.java b/libjava/classpath/java/lang/VirtualMachineError.java new file mode 100644 index 00000000000..3062c4fe800 --- /dev/null +++ b/libjava/classpath/java/lang/VirtualMachineError.java @@ -0,0 +1,73 @@ +/* VirtualMachineError.java -- thrown when the Virtual Machine has a problem + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A <code>VirtualMachineError</code> or its subclasses are thrown to + * indicate there is something wrong with the Java Virtual Machine or that + * it does not have the resources needed for it to continue execution. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public abstract class VirtualMachineError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4161983926571568670L; + + /** + * Create an error without a message. + */ + public VirtualMachineError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public VirtualMachineError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Void.java b/libjava/classpath/java/lang/Void.java new file mode 100644 index 00000000000..15035426830 --- /dev/null +++ b/libjava/classpath/java/lang/Void.java @@ -0,0 +1,68 @@ +/* Void.class - defines void.class + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + + +/** + * Void is a placeholder class so that the variable <code>Void.TYPE</code> + * (also available as <code>void.class</code>) can be supported for + * reflection return types. + * + * <p>This class could be Serializable, but that is up to Sun.</p> + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public final class Void +{ + /** + * The return type <code>void</code> is represented by this + * <code>Class</code> object. + */ + public static final Class TYPE = VMClassLoader.getPrimitiveClass('V'); + + /** + * Void is non-instantiable. + */ + private Void() + { + } +} diff --git a/libjava/classpath/java/lang/annotation/AnnotationFormatError.java b/libjava/classpath/java/lang/annotation/AnnotationFormatError.java new file mode 100644 index 00000000000..40ce3ca10f7 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/AnnotationFormatError.java @@ -0,0 +1,104 @@ +/* AnnotationFormatError.java - Thrown when an binary annotation is malformed + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * Thrown when an annotation found in a class file is + * malformed. When the virtual machine finds a class file + * containing annotations, it attempts to parse them. + * This error is thrown if this operation fails. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AnnotationFormatError extends Error +{ + + /** + * Constructs a new <code>AnnotationFormatError</code> + * using the specified message to give details of the error. + * + * @param message the message to use in the error output. + */ + public AnnotationFormatError(String message) + { + super(message); + } + + /** + * <p> + * Constructs a new <code>AnnotationFormatError</code> + * using the specified message to give details of the error. + * The supplied cause <code>Throwable</code> is used to + * provide additional history, with regards to the root + * of the problem. It is perfectly valid for this to be null, if + * the cause is unknown. + * </p> + * <p> + * <strong>Note</strong>: if a cause is supplied, the error + * message from this cause is not automatically included in the + * error message given by this error. + * </p> + * + * @param message the message to use in the error output + * @param cause the cause of this error, or null if the cause + * is unknown. + */ + public AnnotationFormatError(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Constructs a new <code>AnnotationFormatError</code> using + * the supplied cause <code>Throwable</code> to + * provide additional history, with regards to the root + * of the problem. It is perfectly valid for this to be null, if + * the cause is unknown. If the cause is not null, the error + * message from this cause will also be used as the message + * for this error. + * + * @param cause the cause of the error. + */ + public AnnotationFormatError(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java b/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java new file mode 100644 index 00000000000..653305d632a --- /dev/null +++ b/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java @@ -0,0 +1,116 @@ +/* AnnotationTypeMismatchException.java - Thrown when annotation has changed + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import java.lang.reflect.Method; + +/** + * Thrown when accessing an element within an annotation for + * which the type has changed, since compilation or serialization + * took place. The mismatch between the compiled or serialized + * type and the current type causes this exception to be thrown. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AnnotationTypeMismatchException extends RuntimeException +{ + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 8125925355765570191L; + + /** + * Constructs an <code>AnnotationTypeMismatchException</code> + * which is due to a mismatched type in the annotation + * element, <code>m</code>. The erroneous type used for the + * data in <code>m</code> is represented by the string, + * <code>type</code>. This string is of an undefined format, + * and may contain the value as well as the type. + * + * @param m the element from the annotation. + * @param type the name of the erroneous type found in <code>m</code>. + */ + public AnnotationTypeMismatchException(Method m, String type) + { + this.element = m; + this.foundType = type; + } + + /** + * Returns the element from the annotation, for which a + * mismatch occurred. + * + * @return the element with the mismatched type. + */ + public Method element() + { + return element; + } + + /** + * Returns the erroneous type used by the element, + * represented as a <code>String</code>. The format + * of this <code>String</code> is not explicitly specified, + * and may contain the value as well as the type. + * + * @return the type found in the element. + */ + public String foundType() + { + return foundType; + } + + // Names are chosen from serialization spec. + /** + * The element from the annotation. + * + * @serial the element with the mismatched type. + */ + private Method element; + + /** + * The erroneous type used by the element. + * + * @serial the type found in the element. + */ + private String foundType; + +} diff --git a/libjava/classpath/java/lang/annotation/package.html b/libjava/classpath/java/lang/annotation/package.html new file mode 100644 index 00000000000..ee70daf9ea9 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang.annotation package. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.lang.annotation</title></head> + +<body> +<p>Classes to handle annotations.</p> + +</body> +</html> diff --git a/libjava/classpath/java/lang/class-dependencies.conf b/libjava/classpath/java/lang/class-dependencies.conf new file mode 100644 index 00000000000..4fbf75eb1ce --- /dev/null +++ b/libjava/classpath/java/lang/class-dependencies.conf @@ -0,0 +1,58 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +# end of file diff --git a/libjava/classpath/java/lang/package.html b/libjava/classpath/java/lang/package.html new file mode 100644 index 00000000000..715418dbd69 --- /dev/null +++ b/libjava/classpath/java/lang/package.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.lang</title></head> + +<body> +<p>Core classes including wrappers for primitive types, classes, packages +and class loaders, representations of the system, processes, threads and +the core exception hierarchy.</p> + +</body> +</html> diff --git a/libjava/classpath/java/lang/ref/PhantomReference.java b/libjava/classpath/java/lang/ref/PhantomReference.java new file mode 100644 index 00000000000..4d929c29c58 --- /dev/null +++ b/libjava/classpath/java/lang/ref/PhantomReference.java @@ -0,0 +1,73 @@ +/* java.lang.ref.PhantomReference + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A phantom reference is useful, to get notified, when an object got + * finalized. You can't access that object though, since it is + * finalized. This is the reason, why <code>get()</code> always + * returns null. + * + * @author Jochen Hoenicke + */ +public class PhantomReference + extends Reference +{ + /** + * Creates a new phantom reference. + * @param referent the object that should be watched. + * @param q the queue that should be notified, if the referent was + * finalized. This mustn't be <code>null</code>. + * @exception NullPointerException if q is null. + */ + public PhantomReference(Object referent, ReferenceQueue q) + { + super(referent, q); + } + + /** + * Returns the object, this reference refers to. + * @return <code>null</code>, since the refered object may be + * finalized and thus not accessible. + */ + public Object get() + { + return null; + } +} diff --git a/libjava/classpath/java/lang/ref/Reference.java b/libjava/classpath/java/lang/ref/Reference.java new file mode 100644 index 00000000000..1ec72438198 --- /dev/null +++ b/libjava/classpath/java/lang/ref/Reference.java @@ -0,0 +1,177 @@ +/* java.lang.ref.Reference + Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * This is the base class of all references. A reference allows + * refering to an object without preventing the garbage collector to + * collect it. The only way to get the referred object is via the + * <code>get()</code>-method. This method will return + * <code>null</code> if the object was collected. <br> + * + * A reference may be registered with a queue. When a referred + * element gets collected the reference will be put on the queue, so + * that you will be notified. <br> + * + * There are currently three types of references: soft reference, + * weak reference and phantom reference. <br> + * + * Soft references will be cleared if the garbage collector is told + * to free some memory and there are no unreferenced or weakly referenced + * objects. It is useful for caches. <br> + * + * Weak references will be cleared as soon as the garbage collector + * determines that the refered object is only weakly reachable. They + * are useful as keys in hashtables (see <code>WeakHashtable</code>) as + * you get notified when nobody has the key anymore. + * + * Phantom references don't prevent finalization. If an object is only + * phantom reachable, it will be finalized, and the reference will be + * enqueued, but not cleared. Since you mustn't access an finalized + * object, the <code>get</code> method of a phantom reference will never + * work. It is useful to keep track, when an object is finalized. + * + * @author Jochen Hoenicke + * @see java.util.WeakHashtable + */ +public abstract class Reference +{ + /** + * The underlying object. This field is handled in a special way by + * the garbage collector. + */ + Object referent; + + /** + * The queue this reference is registered on. This is null, if this + * wasn't registered to any queue or reference was already enqueued. + */ + ReferenceQueue queue; + + /** + * Link to the next entry on the queue. If this is null, this + * reference is not enqueued. Otherwise it points to the next + * reference. The last reference on a queue will point to itself + * (not to null, that value is used to mark a not enqueued + * reference). + */ + Reference nextOnQueue; + + /** + * This lock should be taken by the garbage collector, before + * determining reachability. It will prevent the get()-method to + * return the reference so that reachability doesn't change. + */ + static Object lock = new Object(); + + /** + * Creates a new reference that is not registered to any queue. + * Since it is package private, it is not possible to overload this + * class in a different package. + * @param referent the object we refer to. + */ + Reference(Object ref) + { + referent = ref; + } + + /** + * Creates a reference that is registered to a queue. Since this is + * package private, it is not possible to overload this class in a + * different package. + * @param referent the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + Reference(Object ref, ReferenceQueue q) + { + if (q == null) + throw new NullPointerException(); + referent = ref; + queue = q; + } + + /** + * Returns the object, this reference refers to. + * @return the object, this reference refers to, or null if the + * reference was cleared. + */ + public Object get() + { + synchronized (lock) + { + return referent; + } + } + + /** + * Clears the reference, so that it doesn't refer to its object + * anymore. For soft and weak references this is called by the + * garbage collector. For phantom references you should call + * this when enqueuing the reference. + */ + public void clear() + { + referent = null; + } + + /** + * Tells if the object is enqueued on a reference queue. + * @return true if it is enqueued, false otherwise. + */ + public boolean isEnqueued() + { + return nextOnQueue != null; + } + + /** + * Enqueue an object on a reference queue. This is normally executed + * by the garbage collector. + */ + public boolean enqueue() + { + if (queue != null && nextOnQueue == null) + { + queue.enqueue(this); + queue = null; + return true; + } + return false; + } +} diff --git a/libjava/classpath/java/lang/ref/ReferenceQueue.java b/libjava/classpath/java/lang/ref/ReferenceQueue.java new file mode 100644 index 00000000000..f4729f282be --- /dev/null +++ b/libjava/classpath/java/lang/ref/ReferenceQueue.java @@ -0,0 +1,145 @@ +/* java.lang.ref.ReferenceQueue + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * This is the queue, where references can enqueue themselve on. Each + * reference may be registered to a queue at initialization time and + * will be appended to the queue, when the enqueue method is called. + * + * The enqueue method may be automatically called by the garbage + * collector if it detects, that the object is only reachable through + * the Reference objects. + * + * @author Jochen Hoenicke + * @see Reference#enqueue() + */ +public class ReferenceQueue +{ + /** + * This is a linked list of references. If this is null, the list is + * empty. Otherwise this points to the first reference on the queue. + * The first reference will point to the next reference via the + * <code>nextOnQueue</code> field. The last reference will point to + * itself (not to null, since <code>nextOnQueue</code> is used to + * determine if a reference is enqueued). + */ + private Reference first; + + /** + * Creates a new empty reference queue. + */ + public ReferenceQueue() + { + } + + /** + * Checks if there is a reference on the queue, returning it + * immediately. The reference will be dequeued. + * + * @return a reference on the queue, if there is one, + * <code>null</code> otherwise. + */ + public synchronized Reference poll() + { + return dequeue(); + } + + /** + * This is called by reference to enqueue itself on this queue. + * @param ref the reference that should be enqueued. + */ + synchronized void enqueue(Reference ref) + { + /* last reference will point to itself */ + ref.nextOnQueue = first == null ? ref : first; + first = ref; + /* this wakes only one remove thread. */ + notify(); + } + + /** + * Remove a reference from the queue, if there is one. + * @return the first element of the queue, or null if there isn't any. + */ + private Reference dequeue() + { + if (first == null) + return null; + + Reference result = first; + first = (first == first.nextOnQueue) ? null : first.nextOnQueue; + result.nextOnQueue = null; + return result; + } + + /** + * Removes a reference from the queue, blocking for <code>timeout</code> + * until a reference is enqueued. + * @param timeout the timeout period in milliseconds, <code>0</code> means + * wait forever. + * @return the reference removed from the queue, or + * <code>null</code> if timeout period expired. + * @exception InterruptedException if the wait was interrupted. + */ + public synchronized Reference remove(long timeout) + throws InterruptedException + { + if (first == null) + { + wait(timeout); + } + + return dequeue(); + } + + + /** + * Removes a reference from the queue, blocking until a reference is + * enqueued. + * + * @return the reference removed from the queue. + * @exception InterruptedException if the wait was interrupted. + */ + public Reference remove() + throws InterruptedException + { + return remove(0L); + } +} diff --git a/libjava/classpath/java/lang/ref/SoftReference.java b/libjava/classpath/java/lang/ref/SoftReference.java new file mode 100644 index 00000000000..97395eacdfa --- /dev/null +++ b/libjava/classpath/java/lang/ref/SoftReference.java @@ -0,0 +1,84 @@ +/* java.lang.ref.SoftReference + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A soft reference will be cleared, if the object is only softly + * reachable and the garbage collection needs more memory. The garbage + * collection will use an intelligent strategy to determine which soft + * references it should clear. This makes a soft reference ideal for + * caches.<br> + * + * @author Jochen Hoenicke + */ +public class SoftReference + extends Reference +{ + /** + * Create a new soft reference, that is not registered to any queue. + * @param referent the object we refer to. + */ + public SoftReference(Object referent) + { + super(referent); + } + + /** + * Create a new soft reference. + * @param referent the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + public SoftReference(Object referent, ReferenceQueue q) + { + super(referent, q); + } + + /** + * Returns the object, this reference refers to. + * @return the object, this reference refers to, or null if the + * reference was cleared. + */ + public Object get() + { + /* Why is this overloaded??? + * Maybe for a kind of LRU strategy. */ + return super.get(); + } +} diff --git a/libjava/classpath/java/lang/ref/WeakReference.java b/libjava/classpath/java/lang/ref/WeakReference.java new file mode 100644 index 00000000000..9f758ca1eab --- /dev/null +++ b/libjava/classpath/java/lang/ref/WeakReference.java @@ -0,0 +1,79 @@ +/* java.lang.ref.WeakReference + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A weak reference will be cleared, if the object is only weakly + * reachable. It is useful for lookup tables, where you aren't + * interested in an entry, if the key isn't reachable anymore. + * <code>WeakHashtable</code> is a complete implementation of such a + * table. <br> + * + * It is also useful to make objects unique: You create a set of weak + * references to those objects, and when you create a new object you + * look in this set, if the object already exists and return it. If + * an object is not referenced anymore, the reference will + * automatically cleared, and you may remove it from the set. <br> + * + * @author Jochen Hoenicke + * @see java.util.WeakHashtable + */ +public class WeakReference + extends Reference +{ + /** + * Create a new weak reference, that is not registered to any queue. + * @param referent the object we refer to. + */ + public WeakReference(Object referent) + { + super(referent); + } + + /** + * Create a new weak reference. + * @param referent the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + public WeakReference(Object referent, ReferenceQueue q) + { + super(referent, q); + } +} diff --git a/libjava/classpath/java/lang/ref/package.html b/libjava/classpath/java/lang/ref/package.html new file mode 100644 index 00000000000..d3d176280fb --- /dev/null +++ b/libjava/classpath/java/lang/ref/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang.ref package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.lang.ref</title></head> + +<body> +<p>Low level manipulation and monitoring of object references.</p> + +</body> +</html> diff --git a/libjava/classpath/java/lang/reflect/AccessibleObject.java b/libjava/classpath/java/lang/reflect/AccessibleObject.java new file mode 100644 index 00000000000..24418c971c7 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/AccessibleObject.java @@ -0,0 +1,159 @@ +/* java.lang.reflect.AccessibleObject + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This class is the superclass of various reflection classes, and + * allows sufficiently trusted code to bypass normal restrictions to + * do necessary things like invoke private methods outside of the + * class during Serialization. If you don't have a good reason + * to mess with this, don't try. Fortunately, there are adequate + * security checks before you can set a reflection object as accessible. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Field + * @see Constructor + * @see Method + * @see ReflectPermission + * @since 1.2 + * @status updated to 1.4 + */ +public class AccessibleObject +{ + /** + * True if this object is marked accessible, which means the reflected + * object bypasses normal security checks. + */ + // default visibility for use by inherited classes + boolean flag = false; + + /** + * Only the three reflection classes that extend this can create an + * accessible object. This is not serializable for security reasons. + */ + protected AccessibleObject() + { + } + + /** + * Return the accessibility status of this object. + * + * @return true if this object bypasses security checks + */ + public boolean isAccessible() + { + return flag; + } + + /** + * Convenience method to set the flag on a number of objects with a single + * security check. If a security manager exists, it is checked for + * <code>ReflectPermission("suppressAccessChecks")</code>.<p> + * + * It is forbidden to set the accessibility flag to true on any constructor + * for java.lang.Class. This will result in a SecurityException. If the + * SecurityException is thrown for any of the passed AccessibleObjects, + * the accessibility flag will be set on AccessibleObjects in the array prior + * to the one which resulted in the exception. + * + * @param array the array of accessible objects + * @param flag the desired state of accessibility, true to bypass security + * @throws NullPointerException if array is null + * @throws SecurityException if the request is denied + * @see SecurityManager#checkPermission(java.security.Permission) + * @see RuntimePermission + */ + public static void setAccessible(AccessibleObject[] array, boolean flag) + { + checkPermission(); + for (int i = 0; i < array.length; i++) + array[i].secureSetAccessible(flag); + } + + /** + * Sets the accessibility flag for this reflection object. If a security + * manager exists, it is checked for + * <code>ReflectPermission("suppressAccessChecks")</code>.<p> + * + * It is forbidden to set the accessibility flag to true on any constructor for + * java.lang.Class. This will result in a SecurityException. + * + * @param flag the desired state of accessibility, true to bypass security + * @throws NullPointerException if array is null + * @throws SecurityException if the request is denied + * @see SecurityManager#checkPermission(java.security.Permission) + * @see RuntimePermission + */ + public void setAccessible(boolean flag) + { + checkPermission(); + secureSetAccessible(flag); + } + + /** + * Performs the specified security check, for + * <code>ReflectPermission("suppressAccessChecks")</code>. + * + * @throws SecurityException if permission is denied + */ + private static void checkPermission() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + + /** + * Performs the actual accessibility change, this must always be invoked + * after calling checkPermission. + * + * @param flag the desired status + * @throws SecurityException if flag is true and this is a constructor + * for <code>java.lang.Class</code>. + */ + private void secureSetAccessible(boolean flag) + { + if (flag && + (this instanceof Constructor + && ((Constructor) this).getDeclaringClass() == Class.class)) + throw new SecurityException("Cannot make object accessible: " + this); + this.flag = flag; + } +} diff --git a/libjava/classpath/java/lang/reflect/Array.java b/libjava/classpath/java/lang/reflect/Array.java new file mode 100644 index 00000000000..35c77da369a --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Array.java @@ -0,0 +1,675 @@ +/* java.lang.reflect.Array - manipulate arrays by reflection + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.classpath.Configuration; + +/** + * Array holds static helper functions that allow you to create and + * manipulate arrays by reflection. Operations know how to perform widening + * conversions, but throw {@link IllegalArgumentException} if you attempt + * a narrowing conversion. Also, when accessing primitive arrays, this + * class performs object wrapping and unwrapping as necessary.<p> + * + * <B>Note:</B> This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are <code>java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class, + * byte.class</code>, etc. These are not to be confused with the + * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are + * real classes. Note also that the shorthand <code>Object[].class</code> + * is a convenient way to get array Classes.<p> + * + * <B>Performance note:</B> This class performs best when it does not have + * to convert primitive types. The further along the chain it has to convert, + * the worse performance will be. You're best off using the array as whatever + * type it already is, and then converting the result. You will do even + * worse if you do this and use the generic set() function. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Per Bothner (bothner@cygnus.com) + * @see java.lang.Boolean#TYPE + * @see java.lang.Byte#TYPE + * @see java.lang.Short#TYPE + * @see java.lang.Character#TYPE + * @see java.lang.Integer#TYPE + * @see java.lang.Long#TYPE + * @see java.lang.Float#TYPE + * @see java.lang.Double#TYPE + * @since 1.1 + * @status updated to 1.4 + */ +public final class Array +{ + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javalangreflect"); + } + } + + /** + * This class is uninstantiable. + */ + private Array() + { + } + + /** + * Creates a new single-dimensioned array. + * @param componentType the type of the array to create + * @param length the length of the array to create + * @return the created array, cast to an Object + * @throws NullPointerException if <code>componentType</code> is null + * @throws IllegalArgumentException if <code>componentType</code> is + * <code>Void.TYPE</code> + * @throws NegativeArraySizeException when length is less than 0 + * @throws OutOfMemoryError if memory allocation fails + */ + public static Object newInstance(Class componentType, int length) + { + if (! componentType.isPrimitive()) + return createObjectArray(componentType, length); + if (componentType == boolean.class) + return new boolean[length]; + if (componentType == byte.class) + return new byte[length]; + if (componentType == char.class) + return new char[length]; + if (componentType == short.class) + return new short[length]; + if (componentType == int.class) + return new int[length]; + if (componentType == long.class) + return new long[length]; + if (componentType == float.class) + return new float[length]; + if (componentType == double.class) + return new double[length]; + // assert componentType == void.class + throw new IllegalArgumentException(); + } + + /** + * Creates a new multi-dimensioned array. The new array has the same + * component type as the argument class, and the number of dimensions + * in the new array is the sum of the dimensions of the argument class + * and the length of the argument dimensions. Virtual Machine limitations + * forbid too many dimensions (usually 255 is the maximum); but even + * 50 dimensions of 2 elements in each dimension would exceed your memory + * long beforehand! + * + * @param componentType the type of the array to create. + * @param dimensions the dimensions of the array to create. Each element + * in <code>dimensions</code> makes another dimension of the new + * array. Thus, <code>Array.newInstance(java.lang.Boolean, + * new int[]{1,2,3})</code> is the same as + * <code>new java.lang.Boolean[1][2][3]</code> + * @return the created array, cast to an Object + * @throws NullPointerException if componentType or dimension is null + * @throws IllegalArgumentException if the the size of + * <code>dimensions</code> is 0 or exceeds the maximum number of + * array dimensions in the VM; or if componentType is Void.TYPE + * @throws NegativeArraySizeException when any of the dimensions is less + * than 0 + * @throws OutOfMemoryError if memory allocation fails + */ + public static Object newInstance(Class componentType, int[] dimensions) + { + if (dimensions.length <= 0) + throw new IllegalArgumentException ("Empty dimensions array."); + return createMultiArray(componentType, dimensions, + dimensions.length - 1); + } + + /** + * Gets the array length. + * @param array the array + * @return the length of the array + * @throws IllegalArgumentException if <code>array</code> is not an array + * @throws NullPointerException if <code>array</code> is null + */ + public static int getLength(Object array) + { + if (array instanceof Object[]) + return ((Object[]) array).length; + if (array instanceof boolean[]) + return ((boolean[]) array).length; + if (array instanceof byte[]) + return ((byte[]) array). length; + if (array instanceof char[]) + return ((char[]) array).length; + if (array instanceof short[]) + return ((short[]) array).length; + if (array instanceof int[]) + return ((int[]) array).length; + if (array instanceof long[]) + return ((long[]) array).length; + if (array instanceof float[]) + return ((float[]) array).length; + if (array instanceof double[]) + return ((double[]) array).length; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of an array. Primitive elements will be wrapped in + * the corresponding class type. + * + * @param array the array to access + * @param index the array index to access + * @return the element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not an array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #getBoolean(Object, int) + * @see #getByte(Object, int) + * @see #getChar(Object, int) + * @see #getShort(Object, int) + * @see #getInt(Object, int) + * @see #getLong(Object, int) + * @see #getFloat(Object, int) + * @see #getDouble(Object, int) + */ + public static Object get(Object array, int index) + { + if (array instanceof Object[]) + return ((Object[]) array)[index]; + if (array instanceof boolean[]) + return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE; + if (array instanceof byte[]) + return new Byte(((byte[]) array)[index]); + if (array instanceof char[]) + return new Character(((char[]) array)[index]); + if (array instanceof short[]) + return new Short(((short[]) array)[index]); + if (array instanceof int[]) + return new Integer(((int[]) array)[index]); + if (array instanceof long[]) + return new Long(((long[]) array)[index]); + if (array instanceof float[]) + return new Float(((float[]) array)[index]); + if (array instanceof double[]) + return new Double(((double[]) array)[index]); + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a boolean array. + * + * @param array the array to access + * @param index the array index to access + * @return the boolean element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a boolean + * array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static boolean getBoolean(Object array, int index) + { + if (array instanceof boolean[]) + return ((boolean[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a byte array. + * + * @param array the array to access + * @param index the array index to access + * @return the byte element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte + * array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static byte getByte(Object array, int index) + { + if (array instanceof byte[]) + return ((byte[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a char array. + * + * @param array the array to access + * @param index the array index to access + * @return the char element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a char + * array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static char getChar(Object array, int index) + { + if (array instanceof char[]) + return ((char[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a short array. + * + * @param array the array to access + * @param index the array index to access + * @return the short element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte + * or char array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static short getShort(Object array, int index) + { + if (array instanceof short[]) + return ((short[]) array)[index]; + return getByte(array, index); + } + + /** + * Gets an element of an int array. + * + * @param array the array to access + * @param index the array index to access + * @return the int element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte, + * char, short, or int array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static int getInt(Object array, int index) + { + if (array instanceof int[]) + return ((int[]) array)[index]; + if (array instanceof char[]) + return ((char[]) array)[index]; + return getShort(array, index); + } + + /** + * Gets an element of a long array. + * + * @param array the array to access + * @param index the array index to access + * @return the long element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte, + * char, short, int, or long array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static long getLong(Object array, int index) + { + if (array instanceof long[]) + return ((long[]) array)[index]; + return getInt(array, index); + } + + /** + * Gets an element of a float array. + * + * @param array the array to access + * @param index the array index to access + * @return the float element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte, + * char, short, int, long, or float array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static float getFloat(Object array, int index) + { + if (array instanceof float[]) + return ((float[]) array)[index]; + return getLong(array, index); + } + + /** + * Gets an element of a double array. + * + * @param array the array to access + * @param index the array index to access + * @return the double element at <code>array[index]</code> + * @throws IllegalArgumentException if <code>array</code> is not a byte, + * char, short, int, long, float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #get(Object, int) + */ + public static double getDouble(Object array, int index) + { + if (array instanceof double[]) + return ((double[]) array)[index]; + return getFloat(array, index); + } + + /** + * Sets an element of an array. If the array is primitive, then the new + * value is unwrapped and widened. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not an array, + * or the array is primitive and unwrapping value fails, or the + * value is not assignable to the array component type + * @throws NullPointerException if array is null, or if array is primitive + * and value is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #setBoolean(Object, int, boolean) + * @see #setByte(Object, int, byte) + * @see #setChar(Object, int, char) + * @see #setShort(Object, int, short) + * @see #setInt(Object, int, int) + * @see #setLong(Object, int, long) + * @see #setFloat(Object, int, float) + * @see #setDouble(Object, int, double) + */ + public static void set(Object array, int index, Object value) + { + if (array instanceof Object[]) + { + // Too bad the API won't let us throw the easier ArrayStoreException! + if (value != null + && ! array.getClass().getComponentType().isInstance(value)) + throw new IllegalArgumentException(); + ((Object[]) array)[index] = value; + } + else if (value instanceof Byte) + setByte(array, index, ((Byte) value).byteValue()); + else if (value instanceof Short) + setShort(array, index, ((Short) value).shortValue()); + else if (value instanceof Integer) + setInt(array, index, ((Integer) value).intValue()); + else if (value instanceof Long) + setLong(array, index, ((Long) value).longValue()); + else if (value instanceof Float) + setFloat(array, index, ((Float) value).floatValue()); + else if (value instanceof Double) + setDouble(array, index, ((Double) value).doubleValue()); + else if (value instanceof Character) + setChar(array, index, ((Character) value).charValue()); + else if (value instanceof Boolean) + setBoolean(array, index, ((Boolean) value).booleanValue()); + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Sets an element of a boolean array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a boolean + * array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setBoolean(Object array, int index, boolean value) + { + if (array instanceof boolean[]) + ((boolean[]) array)[index] = value; + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Sets an element of a byte array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a byte, + * short, int, long, float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setByte(Object array, int index, byte value) + { + if (array instanceof byte[]) + ((byte[]) array)[index] = value; + else + setShort(array, index, value); + } + + /** + * Sets an element of a char array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a char, + * int, long, float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setChar(Object array, int index, char value) + { + if (array instanceof char[]) + ((char[]) array)[index] = value; + else + setInt(array, index, value); + } + + /** + * Sets an element of a short array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a short, + * int, long, float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setShort(Object array, int index, short value) + { + if (array instanceof short[]) + ((short[]) array)[index] = value; + else + setInt(array, index, value); + } + + /** + * Sets an element of an int array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not an int, + * long, float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setInt(Object array, int index, int value) + { + if (array instanceof int[]) + ((int[]) array)[index] = value; + else + setLong(array, index, value); + } + + /** + * Sets an element of a long array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a long, + * float, or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setLong(Object array, int index, long value) + { + if (array instanceof long[]) + ((long[]) array)[index] = value; + else + setFloat(array, index, value); + } + + /** + * Sets an element of a float array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a float + * or double array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setFloat(Object array, int index, float value) + { + if (array instanceof float[]) + ((float[]) array)[index] = value; + else + setDouble(array, index, value); + } + + /** + * Sets an element of a double array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if <code>array</code> is not a double + * array + * @throws NullPointerException if <code>array</code> is null + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setDouble(Object array, int index, double value) + { + if (array instanceof double[]) + ((double[]) array)[index] = value; + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Dynamically and recursively create a multi-dimensioned array of objects. + * + * @param type guaranteed to be a valid object type + * @param dimensions the dimensions of the array + * @param index index of the current dimension to build + * @return the new multi-dimensioned array + * @throws NegativeArraySizeException if any entry of dimensions is negative + * @throws OutOfMemoryError if memory allocation fails + */ + // This would be faster if implemented natively, using the multianewarray + // bytecode instead of this recursive call + private static Object createMultiArray(Class type, int[] dimensions, + int index) + { + if (index == 0) + return newInstance(type, dimensions[0]); + + Object toAdd = createMultiArray(type, dimensions, index - 1); + Class thisType = toAdd.getClass(); + Object[] retval + = (Object[]) createObjectArray(thisType, dimensions[index]); + if (dimensions[index] > 0) + retval[0] = toAdd; + int i = dimensions[index]; + while (--i > 0) + retval[i] = createMultiArray(type, dimensions, index - 1); + return retval; + } + + /** + * Dynamically create an array of objects. + * + * @param type guaranteed to be a valid object type + * @param dim the length of the array + * @return the new array + * @throws NegativeArraySizeException if dim is negative + * @throws OutOfMemoryError if memory allocation fails + */ + private static native Object createObjectArray(Class type, int dim); +} diff --git a/libjava/classpath/java/lang/reflect/GenericArrayType.java b/libjava/classpath/java/lang/reflect/GenericArrayType.java new file mode 100644 index 00000000000..8dc33a796c5 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/GenericArrayType.java @@ -0,0 +1,61 @@ +/* GenericArrayType.java - Represent an array type with a generic component + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents the type of an array's components, which may be + * either a parameterized type or a type variable. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface GenericArrayType + extends Type +{ + + /** + * Returns the <code>Type</code> of the components within the array. + * + * @return a <code>Type</code> instance representing the type of + * the array's components. + */ + Type getGenericComponentType(); + +} diff --git a/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java b/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java new file mode 100644 index 00000000000..ab6928de6c0 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java @@ -0,0 +1,62 @@ +/* GenericSignatureFormatError.java - Thrown when a signature is malformed. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Thrown on encountering a syntactically malformed signature in + * a reflective method. During reflection, the generic type signature + * of a type, method or constructor may be interpreted by the virtual + * machine. This error is thrown if this operation fails. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class GenericSignatureFormatError + extends ClassFormatError +{ + + /** + * Constructs a new <code>GenericSignatureFormatError</code>. + */ + public GenericSignatureFormatError() + { + } + +} diff --git a/libjava/classpath/java/lang/reflect/InvocationHandler.java b/libjava/classpath/java/lang/reflect/InvocationHandler.java new file mode 100644 index 00000000000..208e621eedc --- /dev/null +++ b/libjava/classpath/java/lang/reflect/InvocationHandler.java @@ -0,0 +1,137 @@ +/* java.lang.reflect.InvocationHandler - dynamically executes methods in + proxy instances + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This interface defines an invocation handler. Suppose you are using + * reflection, and found a method that requires that its parameter + * be an object of a given interface. You want to call this method, + * but have no idea what classes implement that interface. So, you can + * create a {@link Proxy} instance, a convenient way to dynamically + * generate a class that meets all the necessary properties of that + * interface. But in order for the proxy instance to do any good, it + * needs to know what to do when interface methods are invoked! So, + * this interface is basically a cool wrapper that provides runtime + * code generation needed by proxy instances. + * + * <p>While this interface was designed for use by Proxy, it will also + * work on any object in general.</p> + * + * <p>Hints for implementing this class:</p> + * + * <ul> + * <li>Don't forget that Object.equals, Object.hashCode, and + * Object.toString will call this handler. In particular, + * a naive call to proxy.equals, proxy.hashCode, or proxy.toString + * will put you in an infinite loop. And remember that string + * concatenation also invokes toString.</li> + * <li>Obey the contract of the Method object you are handling, or + * the proxy instance will be forced to throw a + * {@link NullPointerException}, {@link ClassCastException}, + * or {@link UndeclaredThrowableException}.</li> + * <li>Be prepared to wrap/unwrap primitives as necessary.</li> + * <li>The Method object may be owned by a different interface than + * what was actually used as the qualifying type of the method + * invocation in the Java source code. This means that it might + * not always be safe to throw an exception listed as belonging + * to the method's throws clause.</li> + * </ul> + * + * <p><small>For a fun time, create an InvocationHandler that handles the + * methods of a proxy instance of the InvocationHandler interface!</small></p> + * + * @see Proxy + * @see UndeclaredThrowableException + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InvocationHandler +{ + /** + * When a method is invoked on a proxy instance, it is wrapped and + * this method is called instead, so that you may decide at runtime + * how the original method should behave. + * + * @param proxy the instance that the wrapped method should be + * invoked on. When this method is called by a Proxy object, + * `proxy' will be an instance of {@link Proxy}, and oddly enough, + * <code>Proxy.getInvocationHandler(proxy)</code> will return + * <code>this</code>! + * @param method the reflected method to invoke on the proxy. + * When this method is called by a Proxy object, 'method' + * will be the reflection object owned by the declaring + * class or interface, which may be a supertype of the + * interfaces the proxy directly implements. + * @param args the arguments passed to the original method, or + * <code>null</code> if the method takes no arguments. + * (But also be prepared to handle a 0-length array). + * Arguments of primitive type, such as <code>boolean</code> + * or <code>int</code>, are wrapped in the appropriate + * class such as {@link Boolean} or {@link Integer}. + * @return whatever is necessary to return from the wrapped method. + * If the wrapped method is <code>void</code>, the proxy + * instance will ignore it. If the wrapped method returns + * a primitive, this must be the correct wrapper type whose value + * is exactly assignable to the appropriate type (no widening + * will be performed); a null object in this case causes a + * {@link NullPointerException}. In all remaining cases, if + * the returned object is not assignment compatible to the + * declared type of the original method, the proxy instance + * will generate a {@link ClassCastException}. + * @throws Throwable this interface is listed as throwing anything, + * but the implementation should only throw unchecked + * exceptions and exceptions listed in the throws clause of + * all methods being overridden by the proxy instance. If + * something is thrown that is not compatible with the throws + * clause of all overridden methods, the proxy instance will + * wrap the exception in an UndeclaredThrowableException. + * Note that an exception listed in the throws clause of the + * `method' parameter might not be declared in additional + * interfaces also implemented by the proxy object. + * + * @see Proxy + * @see UndeclaredThrowableException + */ + Object invoke(Object proxy, Method method, Object[] args) + throws Throwable; + +} diff --git a/libjava/classpath/java/lang/reflect/InvocationTargetException.java b/libjava/classpath/java/lang/reflect/InvocationTargetException.java new file mode 100644 index 00000000000..af79d3a199d --- /dev/null +++ b/libjava/classpath/java/lang/reflect/InvocationTargetException.java @@ -0,0 +1,123 @@ +/* InvocationTargetException.java -- Wrapper exception for reflection + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * InvocationTargetException is sort of a way to "wrap" whatever exception + * comes up when a method or constructor is called via Reflection. As of + * JDK 1.4, it was retrofitted to match the exception chaining of all other + * exceptions, but <code>getTargetException()</code> still works. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Method#invoke(Object,Object[]) + * @see Constructor#newInstance(Object[]) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvocationTargetException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4085088731926701167L; + + /** + * The chained exception. This field is only around for serial compatibility. + * + * @serial the chained exception + */ + private final Throwable target; + + /** + * Construct an exception with null as the cause. The cause is initialized + * to null. + */ + protected InvocationTargetException() + { + this(null, null); + } + + /** + * Create an <code>InvocationTargetException</code> using another + * exception. + * + * @param targetException the exception to wrap + */ + public InvocationTargetException(Throwable targetException) + { + this(targetException, null); + } + + /** + * Create an <code>InvocationTargetException</code> using another + * exception and an error message. + * + * @param targetException the exception to wrap + * @param err an extra reason for the exception-throwing + */ + public InvocationTargetException(Throwable targetException, String err) + { + super(err, targetException); + target = targetException; + } + + /** + * Get the wrapped (targeted) exception. + * + * @return the targeted exception + * @see #getCause() + */ + public Throwable getTargetException() + { + return target; + } + + /** + * Returns the cause of this exception (which may be null). + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return target; + } +} diff --git a/libjava/classpath/java/lang/reflect/Member.java b/libjava/classpath/java/lang/reflect/Member.java new file mode 100644 index 00000000000..9983b275a94 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Member.java @@ -0,0 +1,100 @@ +/* java.lang.reflect.Member - common query methods in reflection + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Member is an interface that represents any member of a class (field or + * method) or a constructor. You can get information about the declaring + * class, name or modifiers of the member with this interface. + * + * @author John Keiser + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class + * @see Field + * @see Method + * @see Constructor + * @since 1.1 + * @status updated to 1.4 + */ +public interface Member +{ + /** + * Represents all members, whether public, private, protected or + * package-protected, but only which are declared in this class. + * Used in SecurityManager.checkMemberAccess() to determine the + * type of members to access. + * @see SecurityManager#checkMemberAccess() + */ + int DECLARED = 1; + + /** + * Represents public members only, but includes all inherited members. + * Used in SecurityManager.checkMemberAccess() to determine the type of + * members to access. + * @see SecurityManager#checkMemberAccess() + */ + int PUBLIC = 0; + + /** + * Gets the class that declared this member. This is not the class where + * this method was called, or even the class where this Member object + * came to life, but the class that declares the member this represents. + * + * @return the class that declared this member + */ + Class getDeclaringClass(); + + /** + * Gets the simple name of this member. This will be a valid Java + * identifier, with no qualification. + * + * @return the name of this member + */ + String getName(); + + /** + * Gets the modifiers this member uses. Use the <code>Modifier</code> + * class to interpret the values. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + int getModifiers(); +} diff --git a/libjava/classpath/java/lang/reflect/Modifier.java b/libjava/classpath/java/lang/reflect/Modifier.java new file mode 100644 index 00000000000..efc88c941db --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Modifier.java @@ -0,0 +1,332 @@ +/* java.lang.reflect.Modifier + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Modifier is a helper class with static methods to determine whether an + * int returned from getModifiers() represents static, public, protected, + * native, final, etc... and provides an additional method to print + * out all of the modifiers in an int in order. + * <p> + * The methods in this class use the bitmask values in the VM spec to + * determine the modifiers of an int. This means that a VM must return a + * standard mask, conformant with the VM spec. I don't know if this is how + * Sun does it, but I'm willing to bet money that it is. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Member#getModifiers() + * @see Method#getModifiers() + * @see Field#getModifiers() + * @see Constructor#getModifiers() + * @see Class#getModifiers() + * @since 1.1 + */ +public class Modifier +{ + /** <STRONG>This constructor really shouldn't be here ... there are no + * instance methods or variables of this class, so instantiation is + * worthless. However, this function is in the 1.1 spec, so it is added + * for completeness.</STRONG> + */ + public Modifier() + { + } + + /** + * Public: accessible from any other class. + */ + public static final int PUBLIC = 0x0001; + + /** + * Private: accessible only from the same enclosing class. + */ + public static final int PRIVATE = 0x0002; + + /** + * Protected: accessible only to subclasses, or within the package. + */ + public static final int PROTECTED = 0x0004; + + /** + * Static:<br><ul> + * <li>Class: no enclosing instance for nested class.</li> + * <li>Field or Method: can be accessed or invoked without an + * instance of the declaring class.</li> + * </ul> + */ + public static final int STATIC = 0x0008; + + /** + * Final:<br><ul> + * <li>Class: no subclasses allowed.</li> + * <li>Field: cannot be changed.</li> + * <li>Method: cannot be overriden.</li> + * </ul> + */ + public static final int FINAL = 0x0010; + + /** + * Synchronized: Method: lock the class while calling this method. + */ + public static final int SYNCHRONIZED = 0x0020; + + /** + * Volatile: Field: cannot be cached. + */ + public static final int VOLATILE = 0x0040; + + /** + * Transient: Field: not serialized or deserialized. + */ + public static final int TRANSIENT = 0x0080; + + /** + * Native: Method: use JNI to call this method. + */ + public static final int NATIVE = 0x0100; + + /** + * Interface: Class: is an interface. + */ + public static final int INTERFACE = 0x0200; + + /** + * Abstract:<br><ul> + * <li>Class: may not be instantiated.</li> + * <li>Method: may not be called.</li> + * </ul> + */ + public static final int ABSTRACT = 0x0400; + + /** + * Strictfp: Method: expressions are FP-strict.<p> + * Also used as a modifier for classes, to mean that all initializers + * and constructors are FP-strict, but does not show up in + * Class.getModifiers. + */ + public static final int STRICT = 0x0800; + + + /** + * Super - treat invokespecial as polymorphic so that super.foo() works + * according to the JLS. This is a reuse of the synchronized constant + * to patch a hole in JDK 1.0. *shudder*. + */ + static final int SUPER = 0x0020; + + /** + * All the flags, only used by code in this package. + */ + static final int ALL_FLAGS = 0xfff; + + /** + * Check whether the given modifier is abstract. + * @param mod the modifier. + * @return <code>true</code> if abstract, <code>false</code> otherwise. + */ + public static boolean isAbstract(int mod) + { + return (mod & ABSTRACT) != 0; + } + + /** + * Check whether the given modifier is final. + * @param mod the modifier. + * @return <code>true</code> if final, <code>false</code> otherwise. + */ + public static boolean isFinal(int mod) + { + return (mod & FINAL) != 0; + } + + /** + * Check whether the given modifier is an interface. + * @param mod the modifier. + * @return <code>true</code> if an interface, <code>false</code> otherwise. + */ + public static boolean isInterface(int mod) + { + return (mod & INTERFACE) != 0; + } + + /** + * Check whether the given modifier is native. + * @param mod the modifier. + * @return <code>true</code> if native, <code>false</code> otherwise. + */ + public static boolean isNative(int mod) + { + return (mod & NATIVE) != 0; + } + + /** + * Check whether the given modifier is private. + * @param mod the modifier. + * @return <code>true</code> if private, <code>false</code> otherwise. + */ + public static boolean isPrivate(int mod) + { + return (mod & PRIVATE) != 0; + } + + /** + * Check whether the given modifier is protected. + * @param mod the modifier. + * @return <code>true</code> if protected, <code>false</code> otherwise. + */ + public static boolean isProtected(int mod) + { + return (mod & PROTECTED) != 0; + } + + /** + * Check whether the given modifier is public. + * @param mod the modifier. + * @return <code>true</code> if public, <code>false</code> otherwise. + */ + public static boolean isPublic(int mod) + { + return (mod & PUBLIC) != 0; + } + + /** + * Check whether the given modifier is static. + * @param mod the modifier. + * @return <code>true</code> if static, <code>false</code> otherwise. + */ + public static boolean isStatic(int mod) + { + return (mod & STATIC) != 0; + } + + /** + * Check whether the given modifier is strictfp. + * @param mod the modifier. + * @return <code>true</code> if strictfp, <code>false</code> otherwise. + */ + public static boolean isStrict(int mod) + { + return (mod & STRICT) != 0; + } + + /** + * Check whether the given modifier is synchronized. + * @param mod the modifier. + * @return <code>true</code> if synchronized, <code>false</code> otherwise. + */ + public static boolean isSynchronized(int mod) + { + return (mod & SYNCHRONIZED) != 0; + } + + /** + * Check whether the given modifier is transient. + * @param mod the modifier. + * @return <code>true</code> if transient, <code>false</code> otherwise. + */ + public static boolean isTransient(int mod) + { + return (mod & TRANSIENT) != 0; + } + + /** + * Check whether the given modifier is volatile. + * @param mod the modifier. + * @return <code>true</code> if volatile, <code>false</code> otherwise. + */ + public static boolean isVolatile(int mod) + { + return (mod & VOLATILE) != 0; + } + + /** + * Get a string representation of all the modifiers represented by the + * given int. The keywords are printed in this order: + * <code><public|protected|private> abstract static final transient + * volatile synchronized native strictfp interface</code>. + * + * @param mod the modifier. + * @return the String representing the modifiers. + */ + public static String toString(int mod) + { + return toString(mod, new StringBuffer()).toString(); + } + + /** + * Package helper method that can take a StringBuffer. + * @param mod the modifier + * @param r the StringBuffer to which the String representation is appended + * @return r, with information appended + */ + static StringBuffer toString(int mod, StringBuffer r) + { + if (isPublic(mod)) + r.append("public "); + if (isProtected(mod)) + r.append("protected "); + if (isPrivate(mod)) + r.append("private "); + if (isAbstract(mod)) + r.append("abstract "); + if (isStatic(mod)) + r.append("static "); + if (isFinal(mod)) + r.append("final "); + if (isTransient(mod)) + r.append("transient "); + if (isVolatile(mod)) + r.append("volatile "); + if (isSynchronized(mod)) + r.append("synchronized "); + if (isNative(mod)) + r.append("native "); + if (isStrict(mod)) + r.append("strictfp "); + if (isInterface(mod)) + r.append("interface "); + + // Trim trailing space. + if ((mod & ALL_FLAGS) != 0) + r.setLength(r.length() - 1); + return r; + } +} diff --git a/libjava/classpath/java/lang/reflect/ParameterizedType.java b/libjava/classpath/java/lang/reflect/ParameterizedType.java new file mode 100644 index 00000000000..61081c962db --- /dev/null +++ b/libjava/classpath/java/lang/reflect/ParameterizedType.java @@ -0,0 +1,122 @@ +/* ParameterizedType.java -- Represents parameterized types e.g. List<String> + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * <p> + * Represents a type which is parameterized over one or more other + * types. For example, <code>List<Integer></code> is a parameterized + * type, with <code>List</code> parameterized over the type + * <code>Integer</code>. + * </p> + * <p> + * Instances of this classes are created as needed, during reflection. + * On creating a parameterized type, <code>p</code>, the + * <code>GenericTypeDeclaration</code> corresponding to <code>p</code> + * is created and resolved. Each type argument of <code>p</code> + * is then created recursively; details of this process are availble + * in the documentation of <code>TypeVariable</code>. This creation + * process only happens once; repetition has no effect. + * </p> + * <p> + * Implementors of this interface must implement an appropriate + * <code>equals()</code> method. This method should equate any + * two instances of the implementing class that have the same + * <code>GenericTypeDeclaration</code> and <code>Type</code> + * parameters. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see GenericTypeDeclaration + * @see TypeVariable + * @since 1.5 + */ +public interface ParameterizedType + extends Type +{ + + /** + * <p> + * Returns an array of <code>Type</code> objects, which gives + * the parameters of this type. + * </p> + * <p> + * <strong>Note</code>: the returned array may be empty. This + * occurs if the supposed <code>ParameterizedType</code> is simply + * a normal type wrapped inside a parameterized type. + * </p> + * + * @return an array of <code>Type</code>s, representing the arguments + * of this type. + * @throws TypeNotPresentException if any of the types referred to by + * the parameters of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getActualTypeArguments(); + + /** + * Returns the type of which this type is a member. For example, + * in <code>Top<String>.Bottom<Integer></code>, + * <code>Bottom<Integer></code> is a member of + * <code>Top<String></code>, and so the latter is returned + * by this method. Calling this method on top-level types (such as + * <code>Top<String></code>) returns null. + * + * @return the type which owns this type. + * @throws TypeNotPresentException if the owner type referred to by + * this type do not actually exist. + * @throws MalformedParameterizedTypeException if the owner type + * referred to by this type can not be instantiated. + */ + Type getOwnerType(); + + /** + * Returns a version of this type without parameters, which corresponds + * to the class or interface which declared the type. For example, + * the raw type corresponding to <code>List<Double></code> + * is <code>List</code>, which was declared by the <code>List</code> + * class. + * + * @return the raw variant of this type (i.e. the type without + * parameters). + */ + Type getRawType(); + +} diff --git a/libjava/classpath/java/lang/reflect/Proxy.java b/libjava/classpath/java/lang/reflect/Proxy.java new file mode 100644 index 00000000000..dc1ac87e4e1 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Proxy.java @@ -0,0 +1,1615 @@ +/* Proxy.java -- build a proxy class that implements reflected interfaces + Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.classpath.Configuration; +import gnu.java.lang.reflect.TypeSignature; + +import java.io.Serializable; +import java.security.ProtectionDomain; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class allows you to dynamically create an instance of any (or + * even multiple) interfaces by reflection, and decide at runtime + * how that instance will behave by giving it an appropriate + * {@link InvocationHandler}. Proxy classes serialize specially, so + * that the proxy object can be reused between VMs, without requiring + * a persistent copy of the generated class code. + * + * <h3>Creation</h3> + * To create a proxy for some interface Foo: + * + * <pre> + * InvocationHandler handler = new MyInvocationHandler(...); + * Class proxyClass = Proxy.getProxyClass( + * Foo.class.getClassLoader(), new Class[] { Foo.class }); + * Foo f = (Foo) proxyClass + * .getConstructor(new Class[] { InvocationHandler.class }) + * .newInstance(new Object[] { handler }); + * </pre> + * or more simply: + * <pre> + * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), + * new Class[] { Foo.class }, + * handler); + * </pre> + * + * <h3>Dynamic Proxy Classes</h3> + * A dynamic proxy class is created at runtime, and has the following + * properties: + * <ul> + * <li>The class is <code>public</code> and <code>final</code>, + * and is neither <code>abstract</code> nor an inner class.</li> + * <li>The class has no canonical name (there is no formula you can use + * to determine or generate its name), but begins with the + * sequence "$Proxy". Abuse this knowledge at your own peril. + * (For now, '$' in user identifiers is legal, but it may not + * be that way forever. You weren't using '$' in your + * identifiers, were you?)</li> + * <li>The class extends Proxy, and explicitly implements all the + * interfaces specified at creation, in order (this is important + * for determining how method invocation is resolved). Note that + * a proxy class implements {@link Serializable}, at least + * implicitly, since Proxy does, but true serial behavior + * depends on using a serializable invocation handler as well.</li> + * <li>If at least one interface is non-public, the proxy class + * will be in the same package. Otherwise, the package is + * unspecified. This will work even if the package is sealed + * from user-generated classes, because Proxy classes are + * generated by a trusted source. Meanwhile, the proxy class + * belongs to the classloader you designated.</li> + * <li>Reflection works as expected: {@link Class#getInterfaces()} and + * {@link Class#getMethods()} work as they do on normal classes.</li> + * <li>The method {@link #isProxyClass()} will distinguish between + * true proxy classes and user extensions of this class. It only + * returns true for classes created by {@link #getProxyClass}.</li> + * <li>The {@link ProtectionDomain} of a proxy class is the same as for + * bootstrap classes, such as Object or Proxy, since it is created by + * a trusted source. This protection domain will typically be granted + * {@link java.security.AllPermission}. But this is not a security + * risk, since there are adequate permissions on reflection, which is + * the only way to create an instance of the proxy class.</li> + * <li>The proxy class contains a single constructor, which takes as + * its only argument an {@link InvocationHandler}. The method + * {@link #newInstance} is shorthand to do the necessary + * reflection.</li> + * </ul> + * + * <h3>Proxy Instances</h3> + * A proxy instance is an instance of a proxy class. It has the + * following properties, many of which follow from the properties of a + * proxy class listed above: + * <ul> + * <li>For a proxy class with Foo listed as one of its interfaces, the + * expression <code>proxy instanceof Foo</code> will return true, + * and the expression <code>(Foo) proxy</code> will succeed without + * a {@link ClassCastException}.</li> + * <li>Each proxy instance has an invocation handler, which can be + * accessed by {@link #getInvocationHandler(Object)}. Any call + * to an interface method, including {@link Object#hashcode()}, + * {@link Object#equals(Object)}, or {@link Object#toString()}, + * but excluding the public final methods of Object, will be + * encoded and passed to the {@link InvocationHandler#invoke} + * method of this handler.</li> + * </ul> + * + * <h3>Inheritance Issues</h3> + * A proxy class may inherit a method from more than one interface. + * The order in which interfaces are listed matters, because it determines + * which reflected {@link Method} object will be passed to the invocation + * handler. This means that the dynamically generated class cannot + * determine through which interface a method is being invoked.<p> + * + * In short, if a method is declared in Object (namely, hashCode, + * equals, or toString), then Object will be used; otherwise, the + * leftmost interface that inherits or declares a method will be used, + * even if it has a more permissive throws clause than what the proxy + * class is allowed. Thus, in the invocation handler, it is not always + * safe to assume that every class listed in the throws clause of the + * passed Method object can safely be thrown; fortunately, the Proxy + * instance is robust enough to wrap all illegal checked exceptions in + * {@link UndeclaredThrowableException}. + * + * @see InvocationHandler + * @see UndeclaredThrowableException + * @see Class + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4, except for the use of ProtectionDomain + */ +public class Proxy implements Serializable +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = -2222568056686623797L; + + /** + * Map of ProxyType to proxy class. + * + * @XXX This prevents proxy classes from being garbage collected. + * java.util.WeakHashSet is not appropriate, because that collects the + * keys, but we are interested in collecting the elements. + */ + private static final Map proxyClasses = new HashMap(); + + /** + * The invocation handler for this proxy instance. For Proxy, this + * field is unused, but it appears here in order to be serialized in all + * proxy classes. + * + * <em>NOTE</em>: This implementation is more secure for proxy classes + * than what Sun specifies. Sun does not require h to be immutable, but + * this means you could change h after the fact by reflection. However, + * by making h immutable, we may break non-proxy classes which extend + * Proxy. + * @serial invocation handler associated with this proxy instance + */ + protected InvocationHandler h; + + /** + * Constructs a new Proxy from a subclass (usually a proxy class), + * with the specified invocation handler. + * + * <em>NOTE</em>: This throws a NullPointerException if you attempt + * to create a proxy instance with a null handler using reflection. + * This behavior is not yet specified by Sun; see Sun Bug 4487672. + * + * @param handler the invocation handler, may be null if the subclass + * is not a proxy class + * @throws NullPointerException if handler is null and this is a proxy + * instance + */ + protected Proxy(InvocationHandler handler) + { + if (handler == null && isProxyClass(getClass())) + throw new NullPointerException("invalid handler"); + h = handler; + } + + /** + * Returns the proxy {@link Class} for the given ClassLoader and array + * of interfaces, dynamically generating it if necessary. + * + * <p>There are several restrictions on this method, the violation of + * which will result in an IllegalArgumentException or + * NullPointerException:</p> + * + * <ul> + * <li>All objects in `interfaces' must represent distinct interfaces. + * Classes, primitive types, null, and duplicates are forbidden.</li> + * <li>The interfaces must be visible in the specified ClassLoader. + * In other words, for each interface i: + * <code>Class.forName(i.getName(), false, loader) == i</code> + * must be true.</li> + * <li>All non-public interfaces (if any) must reside in the same + * package, or the proxy class would be non-instantiable. If + * there are no non-public interfaces, the package of the proxy + * class is unspecified.</li> + * <li>All interfaces must be compatible - if two declare a method + * with the same name and parameters, the return type must be + * the same and the throws clause of the proxy class will be + * the maximal subset of subclasses of the throws clauses for + * each method that is overridden.</li> + * <li>VM constraints limit the number of interfaces a proxy class + * may directly implement (however, the indirect inheritance + * of {@link Serializable} does not count against this limit). + * Even though most VMs can theoretically have 65535 + * superinterfaces for a class, the actual limit is smaller + * because a class's constant pool is limited to 65535 entries, + * and not all entries can be interfaces.</li> + * </ul> + * + * <p>Note that different orders of interfaces produce distinct classes.</p> + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @return the Class object of the proxy class + * @throws IllegalArgumentException if the constraints above were + * violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry + */ + // synchronized so that we aren't trying to build the same class + // simultaneously in two threads + public static synchronized Class getProxyClass(ClassLoader loader, + Class[] interfaces) + { + interfaces = (Class[]) interfaces.clone(); + ProxyType pt = new ProxyType(loader, interfaces); + Class clazz = (Class) proxyClasses.get(pt); + if (clazz == null) + { + if (Configuration.HAVE_NATIVE_GET_PROXY_CLASS) + clazz = getProxyClass0(loader, interfaces); + else + { + ProxyData data = (Configuration.HAVE_NATIVE_GET_PROXY_DATA + ? getProxyData0(loader, interfaces) + : ProxyData.getProxyData(pt)); + + clazz = (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS + ? generateProxyClass0(loader, data) + : new ClassFactory(data).generate(loader)); + } + + Object check = proxyClasses.put(pt, clazz); + // assert check == null && clazz != null; + if (check != null || clazz == null) + throw new InternalError(/*"Fatal flaw in getProxyClass"*/); + } + return clazz; + } + + /** + * Combines several methods into one. This is equivalent to: + * <pre> + * Proxy.getProxyClass(loader, interfaces) + * .getConstructor(new Class[] {InvocationHandler.class}) + * .newInstance(new Object[] {handler}); + * </pre> + * except that it will not fail with the normal problems caused + * by reflection. It can still fail for the same reasons documented + * in getProxyClass, or if handler is null. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @param handler the invocation handler, may not be null + * @return a proxy instance implementing the specified interfaces + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see #getProxyClass(ClassLoader, Class[]) + * @see Class#getConstructor(Class[]) + * @see Constructor#newInstance(Object[]) + */ + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler handler) + { + try + { + // getProxyClass() and Proxy() throw the necessary exceptions + return getProxyClass(loader, interfaces) + .getConstructor(new Class[] {InvocationHandler.class}) + .newInstance(new Object[] {handler}); + } + catch (RuntimeException e) + { + // Let IllegalArgumentException, NullPointerException escape. + // assert e instanceof IllegalArgumentException + // || e instanceof NullPointerException; + throw e; + } + catch (InvocationTargetException e) + { + // Let wrapped NullPointerException escape. + // assert e.getTargetException() instanceof NullPointerException + throw (NullPointerException) e.getCause(); + } + catch (Exception e) + { + // Covers InstantiationException, IllegalAccessException, + // NoSuchMethodException, none of which should be generated + // if the proxy class was generated correctly. + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Returns true if and only if the Class object is a dynamically created + * proxy class (created by <code>getProxyClass</code> or by the + * syntactic sugar of <code>newProxyInstance</code>). + * + * <p>This check is secure (in other words, it is not simply + * <code>clazz.getSuperclass() == Proxy.class</code>), it will not + * be spoofed by non-proxy classes that extend Proxy. + * + * @param clazz the class to check, must not be null + * @return true if the class represents a proxy class + * @throws NullPointerException if clazz is null + */ + // This is synchronized on the off chance that another thread is + // trying to add a class to the map at the same time we read it. + public static synchronized boolean isProxyClass(Class clazz) + { + if (! Proxy.class.isAssignableFrom(clazz)) + return false; + // This is a linear search, even though we could do an O(1) search + // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()). + return proxyClasses.containsValue(clazz); + } + + /** + * Returns the invocation handler for the given proxy instance.<p> + * + * <em>NOTE</em>: We guarantee a non-null result if successful, + * but Sun allows the creation of a proxy instance with a null + * handler. See the comments for {@link #Proxy(InvocationHandler)}. + * + * @param proxy the proxy instance, must not be null + * @return the invocation handler, guaranteed non-null. + * @throws IllegalArgumentException if + * <code>Proxy.isProxyClass(proxy.getClass())</code> returns false. + * @throws NullPointerException if proxy is null + */ + public static InvocationHandler getInvocationHandler(Object proxy) + { + if (! isProxyClass(proxy.getClass())) + throw new IllegalArgumentException("not a proxy instance"); + return ((Proxy) proxy).h; + } + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of getProxyClass. Only needed if + * Configuration.HAVE_NATIVE_GET_PROXY_CLASS is true, this does the + * work of both getProxyData0 and generateProxyClass0 with no + * intermediate form in Java. The native code may safely assume that + * this class must be created, and does not already exist. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the interfaces the class will extend + * @return the generated proxy class + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see Configuration#HAVE_NATIVE_GET_PROXY_CLASS + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyData0(ClassLoader, Class[]) + * @see #generateProxyClass0(ProxyData) + */ + private static native Class getProxyClass0(ClassLoader loader, + Class[] interfaces); + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of getProxyData. Only needed if + * Configuration.HAVE_NATIVE_GET_PROXY_DATA is true. The native code + * may safely assume that a new ProxyData object must be created which + * does not duplicate any existing ones. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the interfaces the class will extend + * @return all data that is required to make this proxy class + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see Configuration.HAVE_NATIVE_GET_PROXY_DATA + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyClass0(ClassLoader, Class[]) + * @see ProxyType#getProxyData() + */ + private static native ProxyData getProxyData0(ClassLoader loader, + Class[] interfaces); + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of generateProxyClass. Only needed if + * Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS is true. The native + * code may safely assume that a new Class must be created, and that + * the ProxyData object does not describe any existing class. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param data the struct of information to convert to a Class. This + * has already been verified for all problems except exceeding + * VM limitations + * @return the newly generated class + * @throws IllegalArgumentException if VM limitations are exceeded + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyClass0(ClassLoader, Class[]) + * @see ProxyData#generateProxyClass(ClassLoader) + */ + private static native Class generateProxyClass0(ClassLoader loader, + ProxyData data); + + /** + * Helper class for mapping unique ClassLoader and interface combinations + * to proxy classes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ProxyType + { + /** + * Store the class loader (may be null) + */ + final ClassLoader loader; + + /** + * Store the interfaces (never null, all elements are interfaces) + */ + final Class[] interfaces; + + /** + * Construct the helper object. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces an array of interfaces + */ + ProxyType(ClassLoader loader, Class[] interfaces) + { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + this.loader = loader; + this.interfaces = interfaces; + } + + /** + * Calculates the hash code. + * + * @return a combination of the classloader and interfaces hashcodes. + */ + public int hashCode() + { + //loader is always not null + int hash = loader.hashCode(); + for (int i = 0; i < interfaces.length; i++) + hash = hash * 31 + interfaces[i].hashCode(); + return hash; + } + + // A more comprehensive comparison of two arrays, + // ignore array element order, and + // ignore redundant elements + private static boolean sameTypes(Class arr1[], Class arr2[]) { + if (arr1.length == 1 && arr2.length == 1) { + return arr1[0] == arr2[0]; + } + + // total occurrance of elements of arr1 in arr2 + int total_occ_of_arr1_in_arr2 = 0; + each_type: + for (int i = arr1.length; --i >= 0; ) + { + Class t = arr1[i]; + for (int j = i; --j >= 0; ) + { + if (t == arr1[j]) + { //found duplicate type + continue each_type; + } + } + + // count c(a unique element of arr1)'s + // occurrences in arr2 + int occ_in_arr2 = 0; + for (int j = arr2.length; --j >= 0; ) + { + if (t == arr2[j]) + { + ++occ_in_arr2; + } + } + if (occ_in_arr2 == 0) + { // t does not occur in arr2 + return false; + } + + total_occ_of_arr1_in_arr2 += occ_in_arr2; + } + // now, each element of arr2 must have been visited + return total_occ_of_arr1_in_arr2 == arr2.length; + } + + /** + * Calculates equality. + * + * @param the object to compare to + * @return true if it is a ProxyType with same data + */ + public boolean equals(Object other) + { + ProxyType pt = (ProxyType) other; + if (loader != pt.loader || interfaces.length != pt.interfaces.length) + return false; + return sameTypes(interfaces, pt.interfaces); + } + } // class ProxyType + + /** + * Helper class which allows hashing of a method name and signature + * without worrying about return type, declaring class, or throws clause, + * and which reduces the maximally common throws clause between two methods + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ProxySignature + { + /** + * The core signatures which all Proxy instances handle. + */ + static final HashMap coreMethods = new HashMap(); + static + { + try + { + ProxySignature sig + = new ProxySignature(Object.class + .getMethod("equals", + new Class[] {Object.class})); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("hashCode", null)); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("toString", null)); + coreMethods.put(sig, sig); + } + catch (Exception e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * The underlying Method object, never null + */ + final Method method; + + /** + * The set of compatible thrown exceptions, may be empty + */ + final Set exceptions = new HashSet(); + + /** + * Construct a signature + * + * @param method the Method this signature is based on, never null + */ + ProxySignature(Method method) + { + this.method = method; + Class[] exc = method.getExceptionTypes(); + int i = exc.length; + while (--i >= 0) + { + // discard unchecked exceptions + if (Error.class.isAssignableFrom(exc[i]) + || RuntimeException.class.isAssignableFrom(exc[i])) + continue; + exceptions.add(exc[i]); + } + } + + /** + * Given a method, make sure it's return type is identical + * to this, and adjust this signature's throws clause appropriately + * + * @param other the signature to merge in + * @throws IllegalArgumentException if the return types conflict + */ + void checkCompatibility(ProxySignature other) + { + if (method.getReturnType() != other.method.getReturnType()) + throw new IllegalArgumentException("incompatible return types: " + + method + ", " + other.method); + + // if you can think of a more efficient way than this O(n^2) search, + // implement it! + int size1 = exceptions.size(); + int size2 = other.exceptions.size(); + boolean[] valid1 = new boolean[size1]; + boolean[] valid2 = new boolean[size2]; + Iterator itr = exceptions.iterator(); + int pos = size1; + while (--pos >= 0) + { + Class c1 = (Class) itr.next(); + Iterator itr2 = other.exceptions.iterator(); + int pos2 = size2; + while (--pos2 >= 0) + { + Class c2 = (Class) itr2.next(); + if (c2.isAssignableFrom(c1)) + valid1[pos] = true; + if (c1.isAssignableFrom(c2)) + valid2[pos2] = true; + } + } + pos = size1; + itr = exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid1[pos]) + itr.remove(); + } + pos = size2; + itr = other.exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid2[pos]) + itr.remove(); + } + exceptions.addAll(other.exceptions); + } + + /** + * Calculates the hash code. + * + * @return a combination of name and parameter types + */ + public int hashCode() + { + int hash = method.getName().hashCode(); + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) + hash = hash * 31 + types[i].hashCode(); + return hash; + } + + /** + * Calculates equality. + * + * @param the object to compare to + * @return true if it is a ProxySignature with same data + */ + public boolean equals(Object other) + { + ProxySignature ps = (ProxySignature) other; + Class[] types1 = method.getParameterTypes(); + Class[] types2 = ps.method.getParameterTypes(); + if (! method.getName().equals(ps.method.getName()) + || types1.length != types2.length) + return false; + int i = types1.length; + while (--i >= 0) + if (types1[i] != types2[i]) + return false; + return true; + } + } // class ProxySignature + + /** + * A flat representation of all data needed to generate bytecode/instantiate + * a proxy class. This is basically a struct. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ProxyData + { + /** + * The package this class is in <b>including the trailing dot</b> + * or an empty string for the unnamed (aka default) package. + */ + String pack; + + /** + * The interfaces this class implements. Non-null, but possibly empty. + */ + Class[] interfaces; + + /** + * The Method objects this class must pass as the second argument to + * invoke (also useful for determining what methods this class has). + * Non-null, non-empty (includes at least Object.hashCode, Object.equals, + * and Object.toString). + */ + Method[] methods; + + /** + * The exceptions that do not need to be wrapped in + * UndeclaredThrowableException. exceptions[i] is the same as, or a + * subset of subclasses, of methods[i].getExceptionTypes(), depending on + * compatible throws clauses with multiple inheritance. It is unspecified + * if these lists include or exclude subclasses of Error and + * RuntimeException, but excluding them is harmless and generates a + * smaller class. + */ + Class[][] exceptions; + + /** + * For unique id's + */ + private static int count; + + /** + * The id of this proxy class + */ + final int id = count++; + + /** + * Construct a ProxyData with uninitialized data members. + */ + ProxyData() + { + } + + /** + * Return the name of a package (including the trailing dot) + * given the name of a class. + * Returns an empty string if no package. We use this in preference to + * using Class.getPackage() to avoid problems with ClassLoaders + * that don't set the package. + */ + private static String getPackage(Class k) + { + String name = k.getName(); + int idx = name.lastIndexOf('.'); + return name.substring(0, idx + 1); + } + + /** + * Verifies that the arguments are legal, and sets up remaining data + * This should only be called when a class must be generated, as + * it is expensive. + * + * @param pt the ProxyType to convert to ProxyData + * @return the flattened, verified ProxyData structure for use in + * class generation + * @throws IllegalArgumentException if `interfaces' contains + * non-interfaces or incompatible combinations, and verify is true + * @throws NullPointerException if interfaces is null or contains null + */ + static ProxyData getProxyData(ProxyType pt) + { + Map method_set = (Map) ProxySignature.coreMethods.clone(); + boolean in_package = false; // true if we encounter non-public interface + + ProxyData data = new ProxyData(); + data.interfaces = pt.interfaces; + + // if interfaces is too large, we croak later on when the constant + // pool overflows + int i = data.interfaces.length; + while (--i >= 0) + { + Class inter = data.interfaces[i]; + if (! inter.isInterface()) + throw new IllegalArgumentException("not an interface: " + inter); + try + { + if (Class.forName(inter.getName(), false, pt.loader) != inter) + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + catch (ClassNotFoundException e) + { + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + if (! Modifier.isPublic(inter.getModifiers())) + if (in_package) + { + String p = getPackage(inter); + if (! data.pack.equals(p)) + throw new IllegalArgumentException("non-public interfaces " + + "from different " + + "packages"); + } + else + { + in_package = true; + data.pack = getPackage(inter); + } + for (int j = i-1; j >= 0; j--) + if (data.interfaces[j] == inter) + throw new IllegalArgumentException("duplicate interface: " + + inter); + Method[] methods = inter.getMethods(); + int j = methods.length; + while (--j >= 0) + { + ProxySignature sig = new ProxySignature(methods[j]); + ProxySignature old = (ProxySignature) method_set.put(sig, sig); + if (old != null) + sig.checkCompatibility(old); + } + } + + i = method_set.size(); + data.methods = new Method[i]; + data.exceptions = new Class[i][]; + Iterator itr = method_set.values().iterator(); + while (--i >= 0) + { + ProxySignature sig = (ProxySignature) itr.next(); + data.methods[i] = sig.method; + data.exceptions[i] = (Class[]) sig.exceptions + .toArray(new Class[sig.exceptions.size()]); + } + return data; + } + } // class ProxyData + + /** + * Does all the work of building a class. By making this a nested class, + * this code is not loaded in memory if the VM has a native + * implementation instead. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ClassFactory + { + /** Constants for assisting the compilation */ + private static final byte POOL = 0; + private static final byte FIELD = 1; + private static final byte METHOD = 2; + private static final byte INTERFACE = 3; + private static final String CTOR_SIG + = "(Ljava/lang/reflect/InvocationHandler;)V"; + private static final String INVOKE_SIG = "(Ljava/lang/Object;" + + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"; + + /** Bytecodes for insertion in the class definition byte[] */ + private static final char ACONST_NULL = 1; + private static final char ICONST_0 = 3; + private static final char BIPUSH = 16; + private static final char SIPUSH = 17; + private static final char ILOAD = 21; + private static final char ILOAD_0 = 26; + private static final char ALOAD_0 = 42; + private static final char ALOAD_1 = 43; + private static final char AALOAD = 50; + private static final char AASTORE = 83; + private static final char DUP = 89; + private static final char DUP_X1 = 90; + private static final char SWAP = 95; + private static final char IRETURN = 172; + private static final char LRETURN = 173; + private static final char FRETURN = 174; + private static final char DRETURN = 175; + private static final char ARETURN = 176; + private static final char RETURN = 177; + private static final char GETSTATIC = 178; + private static final char GETFIELD = 180; + private static final char INVOKEVIRTUAL = 182; + private static final char INVOKESPECIAL = 183; + private static final char INVOKESTATIC = 184; + private static final char INVOKEINTERFACE = 185; + private static final char NEW = 187; + private static final char ANEWARRAY = 189; + private static final char ATHROW = 191; + private static final char CHECKCAST = 192; + + // Implementation note: we use StringBuffers to hold the byte data, since + // they automatically grow. However, we only use the low 8 bits of + // every char in the array, so we are using twice the necessary memory + // for the ease StringBuffer provides. + + /** The constant pool. */ + private final StringBuffer pool = new StringBuffer(); + /** The rest of the class data. */ + private final StringBuffer stream = new StringBuffer(); + + /** Map of strings to byte sequences, to minimize size of pool. */ + private final Map poolEntries = new HashMap(); + + /** The VM name of this proxy class. */ + private final String qualName; + + /** + * The Method objects the proxy class refers to when calling the + * invocation handler. + */ + private final Method[] methods; + + /** + * Initializes the buffers with the bytecode contents for a proxy class. + * + * @param data the remainder of the class data + * @throws IllegalArgumentException if anything else goes wrong this + * late in the game; as far as I can tell, this will only happen + * if the constant pool overflows, which is possible even when + * the user doesn't exceed the 65535 interface limit + */ + ClassFactory(ProxyData data) + { + methods = data.methods; + + // magic = 0xcafebabe + // minor_version = 0 + // major_version = 46 + // constant_pool_count: place-holder for now + pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0"); + // constant_pool[], filled in as we go + + // access_flags + putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC); + // this_class + qualName = (data.pack + "$Proxy" + data.id); + putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false))); + // super_class + putU2(classInfo("java/lang/reflect/Proxy")); + + // interfaces_count + putU2(data.interfaces.length); + // interfaces[] + for (int i = 0; i < data.interfaces.length; i++) + putU2(classInfo(data.interfaces[i])); + + // Recall that Proxy classes serialize specially, so we do not need + // to worry about a <clinit> method for this field. Instead, we + // just assign it by reflection after the class is successfully loaded. + // fields_count - private static Method[] m; + putU2(1); + // fields[] + // m.access_flags + putU2(Modifier.PRIVATE | Modifier.STATIC); + // m.name_index + putU2(utf8Info("m")); + // m.descriptor_index + putU2(utf8Info("[Ljava/lang/reflect/Method;")); + // m.attributes_count + putU2(0); + // m.attributes[] + + // methods_count - # handler methods, plus <init> + putU2(methods.length + 1); + // methods[] + // <init>.access_flags + putU2(Modifier.PUBLIC); + // <init>.name_index + putU2(utf8Info("<init>")); + // <init>.descriptor_index + putU2(utf8Info(CTOR_SIG)); + // <init>.attributes_count - only Code is needed + putU2(1); + // <init>.Code.attribute_name_index + putU2(utf8Info("Code")); + // <init>.Code.attribute_length = 18 + // <init>.Code.info: + // $Proxynn(InvocationHandler h) { super(h); } + // <init>.Code.max_stack = 2 + // <init>.Code.max_locals = 2 + // <init>.Code.code_length = 6 + // <init>.Code.code[] + stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1 + + INVOKESPECIAL); + putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "<init>", CTOR_SIG)); + // <init>.Code.exception_table_length = 0 + // <init>.Code.exception_table[] + // <init>.Code.attributes_count = 0 + // <init>.Code.attributes[] + stream.append(RETURN + "\0\0\0\0"); + + for (int i = methods.length - 1; i >= 0; i--) + emitMethod(i, data.exceptions[i]); + + // attributes_count + putU2(0); + // attributes[] - empty; omit SourceFile attribute + // XXX should we mark this with a Synthetic attribute? + } + + /** + * Produce the bytecode for a single method. + * + * @param i the index of the method we are building + * @param e the exceptions possible for the method + */ + private void emitMethod(int i, Class[] e) + { + // First, we precalculate the method length and other information. + + Method m = methods[i]; + Class[] paramtypes = m.getParameterTypes(); + int wrap_overhead = 0; // max words taken by wrapped primitive + int param_count = 1; // 1 for this + int code_length = 16; // aload_0, getfield, aload_0, getstatic, const, + // aaload, const/aconst_null, invokeinterface + if (i > 5) + { + if (i > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (paramtypes.length > 0) + { + code_length += 3; // anewarray + if (paramtypes.length > Byte.MAX_VALUE) + code_length += 2; // sipush + else if (paramtypes.length > 5) + code_length++; // bipush + for (int j = 0; j < paramtypes.length; j++) + { + code_length += 4; // dup, const, load, store + Class type = paramtypes[j]; + if (j > 5) + { + if (j > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (param_count >= 4) + code_length++; // 2-byte load + param_count++; + if (type.isPrimitive()) + { + code_length += 7; // new, dup, invokespecial + if (type == long.class || type == double.class) + { + wrap_overhead = 3; + param_count++; + } + else if (wrap_overhead < 2) + wrap_overhead = 2; + } + } + } + int end_pc = code_length; + Class ret_type = m.getReturnType(); + if (ret_type == void.class) + code_length++; // return + else if (ret_type.isPrimitive()) + code_length += 7; // cast, invokevirtual, return + else + code_length += 4; // cast, return + int exception_count = 0; + boolean throws_throwable = false; + for (int j = 0; j < e.length; j++) + if (e[j] == Throwable.class) + { + throws_throwable = true; + break; + } + if (! throws_throwable) + { + exception_count = e.length + 3; // Throwable, Error, RuntimeException + code_length += 9; // new, dup_x1, swap, invokespecial, athrow + } + int handler_pc = code_length - 1; + StringBuffer signature = new StringBuffer("("); + for (int j = 0; j < paramtypes.length; j++) + signature.append(TypeSignature.getEncodingOfClass(paramtypes[j])); + signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type)); + + // Now we have enough information to emit the method. + + // handler.access_flags + putU2(Modifier.PUBLIC | Modifier.FINAL); + // handler.name_index + putU2(utf8Info(m.getName())); + // handler.descriptor_index + putU2(utf8Info(signature.toString())); + // handler.attributes_count - Code is necessary, Exceptions possible + putU2(e.length > 0 ? 2 : 1); + + // handler.Code.info: + // type name(args) { + // try { + // return (type) h.invoke(this, methods[i], new Object[] {args}); + // } catch (<declared Exceptions> e) { + // throw e; + // } catch (Throwable t) { + // throw new UndeclaredThrowableException(t); + // } + // } + // Special cases: + // if arg_n is primitive, wrap it + // if method throws Throwable, try-catch is not needed + // if method returns void, return statement not needed + // if method returns primitive, unwrap it + // save space by sharing code for all the declared handlers + + // handler.Code.attribute_name_index + putU2(utf8Info("Code")); + // handler.Code.attribute_length + putU4(12 + code_length + 8 * exception_count); + // handler.Code.max_stack + putU2(param_count == 1 ? 4 : 7 + wrap_overhead); + // handler.Code.max_locals + putU2(param_count); + // handler.Code.code_length + putU4(code_length); + // handler.Code.code[] + putU1(ALOAD_0); + putU1(GETFIELD); + putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h", + "Ljava/lang/reflect/InvocationHandler;")); + putU1(ALOAD_0); + putU1(GETSTATIC); + putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false), + "m", "[Ljava/lang/reflect/Method;")); + putConst(i); + putU1(AALOAD); + if (paramtypes.length > 0) + { + putConst(paramtypes.length); + putU1(ANEWARRAY); + putU2(classInfo("java/lang/Object")); + param_count = 1; + for (int j = 0; j < paramtypes.length; j++, param_count++) + { + putU1(DUP); + putConst(j); + if (paramtypes[j].isPrimitive()) + { + putU1(NEW); + putU2(classInfo(wrapper(paramtypes[j]))); + putU1(DUP); + } + putLoad(param_count, paramtypes[j]); + if (paramtypes[j].isPrimitive()) + { + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, wrapper(paramtypes[j]), "<init>", + '(' + (TypeSignature + .getEncodingOfClass(paramtypes[j]) + + ")V"))); + if (paramtypes[j] == long.class + || paramtypes[j] == double.class) + param_count++; + } + putU1(AASTORE); + } + } + else + putU1(ACONST_NULL); + putU1(INVOKEINTERFACE); + putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler", + "invoke", INVOKE_SIG)); + putU1(4); // InvocationHandler, this, Method, Object[] + putU1(0); + if (ret_type == void.class) + putU1(RETURN); + else if (ret_type.isPrimitive()) + { + putU1(CHECKCAST); + putU2(classInfo(wrapper(ret_type))); + putU1(INVOKEVIRTUAL); + putU2(refInfo(METHOD, wrapper(ret_type), + ret_type.getName() + "Value", + "()" + TypeSignature.getEncodingOfClass(ret_type))); + if (ret_type == long.class) + putU1(LRETURN); + else if (ret_type == float.class) + putU1(FRETURN); + else if (ret_type == double.class) + putU1(DRETURN); + else + putU1(IRETURN); + } + else + { + putU1(CHECKCAST); + putU2(classInfo(ret_type)); + putU1(ARETURN); + } + if (! throws_throwable) + { + putU1(NEW); + putU2(classInfo("java/lang/reflect/UndeclaredThrowableException")); + putU1(DUP_X1); + putU1(SWAP); + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, + "java/lang/reflect/UndeclaredThrowableException", + "<init>", "(Ljava/lang/Throwable;)V")); + putU1(ATHROW); + } + + // handler.Code.exception_table_length + putU2(exception_count); + // handler.Code.exception_table[] + if (! throws_throwable) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/Error")); + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/RuntimeException")); + for (int j = 0; j < e.length; j++) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo(e[j])); + } + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc - + // -8 for undeclared handler, which falls thru to normal one + putU2(handler_pc - 8); + // handler.Code.exception_table.catch_type + putU2(0); + } + // handler.Code.attributes_count + putU2(0); + // handler.Code.attributes[] + + if (e.length > 0) + { + // handler.Exceptions.attribute_name_index + putU2(utf8Info("Exceptions")); + // handler.Exceptions.attribute_length + putU4(2 * e.length + 2); + // handler.Exceptions.number_of_exceptions + putU2(e.length); + // handler.Exceptions.exception_index_table[] + for (int j = 0; j < e.length; j++) + putU2(classInfo(e[j])); + } + } + + /** + * Creates the Class object that corresponds to the bytecode buffers + * built when this object was constructed. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @return the proxy class Class object + */ + Class generate(ClassLoader loader) + { + byte[] bytecode = new byte[pool.length() + stream.length()]; + // More efficient to bypass calling charAt() repetitively. + char[] c = pool.toString().toCharArray(); + int i = c.length; + while (--i >= 0) + bytecode[i] = (byte) c[i]; + c = stream.toString().toCharArray(); + i = c.length; + int j = bytecode.length; + while (i > 0) + bytecode[--j] = (byte) c[--i]; + + // Patch the constant pool size, which we left at 0 earlier. + int count = poolEntries.size() + 1; + bytecode[8] = (byte) (count >> 8); + bytecode[9] = (byte) count; + + try + { + Class vmClassLoader = Class.forName("java.lang.VMClassLoader"); + Class[] types = {ClassLoader.class, String.class, + byte[].class, int.class, int.class, + ProtectionDomain.class }; + Method m = vmClassLoader.getDeclaredMethod("defineClass", types); + // We can bypass the security check of setAccessible(true), since + // we're in the same package. + m.flag = true; + + Object[] args = {loader, qualName, bytecode, new Integer(0), + new Integer(bytecode.length), + Object.class.getProtectionDomain() }; + Class clazz = (Class) m.invoke(null, args); + + // Finally, initialize the m field of the proxy class, before + // returning it. + Field f = clazz.getDeclaredField("m"); + f.flag = true; + // we can share the array, because it is not publicized + f.set(null, methods); + + return clazz; + } + catch (Exception e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Put a single byte on the stream. + * + * @param i the information to add (only lowest 8 bits are used) + */ + private void putU1(int i) + { + stream.append((char) i); + } + + /** + * Put two bytes on the stream. + * + * @param i the information to add (only lowest 16 bits are used) + */ + private void putU2(int i) + { + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put four bytes on the stream. + * + * @param i the information to add (treated as unsigned) + */ + private void putU4(int i) + { + stream.append((char) (i >> 24)).append((char) (i >> 16)); + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put bytecode to load a constant integer on the stream. This only + * needs to work for values less than Short.MAX_VALUE. + * + * @param i the int to add + */ + private void putConst(int i) + { + if (i >= -1 && i <= 5) + putU1(ICONST_0 + i); + else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) + { + putU1(BIPUSH); + putU1(i); + } + else + { + putU1(SIPUSH); + putU2(i); + } + } + + /** + * Put bytecode to load a given local variable on the stream. + * + * @param i the slot to load + * @param type the base type of the load + */ + private void putLoad(int i, Class type) + { + int offset = 0; + if (type == long.class) + offset = 1; + else if (type == float.class) + offset = 2; + else if (type == double.class) + offset = 3; + else if (! type.isPrimitive()) + offset = 4; + if (i < 4) + putU1(ILOAD_0 + 4 * offset + i); + else + { + putU1(ILOAD + offset); + putU1(i); + } + } + + /** + * Given a primitive type, return its wrapper class name. + * + * @param clazz the primitive type (but not void.class) + * @return the internal form of the wrapper class name + */ + private String wrapper(Class clazz) + { + if (clazz == boolean.class) + return "java/lang/Boolean"; + if (clazz == byte.class) + return "java/lang/Byte"; + if (clazz == short.class) + return "java/lang/Short"; + if (clazz == char.class) + return "java/lang/Character"; + if (clazz == int.class) + return "java/lang/Integer"; + if (clazz == long.class) + return "java/lang/Long"; + if (clazz == float.class) + return "java/lang/Float"; + if (clazz == double.class) + return "java/lang/Double"; + // assert false; + return null; + } + + /** + * Returns the entry of this String in the Constant pool, adding it + * if necessary. + * + * @param str the String to resolve + * @return the index of the String in the constant pool + */ + private char utf8Info(String str) + { + String utf8 = toUtf8(str); + int len = utf8.length(); + return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param name the class name, in internal form + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(String name) + { + char index = utf8Info(name); + char[] c = {7, (char) (index >> 8), (char) (index & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param clazz the class type + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(Class clazz) + { + return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(), + false)); + } + + /** + * Returns the entry of the appropriate fieldref, methodref, or + * interfacemethodref info structure in the Constant pool, adding it + * if necessary. + * + * @param structure FIELD, METHOD, or INTERFACE + * @param clazz the class name, in internal form + * @param name the simple reference name + * @param type the type of the reference + * @return the index of the appropriate Info structure in the constant pool + */ + private char refInfo(byte structure, String clazz, String name, + String type) + { + char cindex = classInfo(clazz); + char ntindex = nameAndTypeInfo(name, type); + // relies on FIELD == 1, METHOD == 2, INTERFACE == 3 + char[] c = {(char) (structure + 8), + (char) (cindex >> 8), (char) (cindex & 0xff), + (char) (ntindex >> 8), (char) (ntindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate nameAndTyperef info structure + * in the Constant pool, adding it if necessary. + * + * @param name the simple name + * @param type the reference type + * @return the index of the NameAndTypeInfo structure in the constant pool + */ + private char nameAndTypeInfo(String name, String type) + { + char nindex = utf8Info(name); + char tindex = utf8Info(type); + char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff), + (char) (tindex >> 8), (char) (tindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Converts a regular string to a UTF8 string, where the upper byte + * of every char is 0, and '\\u0000' is not in the string. This is + * basically to use a String as a fancy byte[], and while it is less + * efficient in memory use, it is easier for hashing. + * + * @param str the original, in straight unicode + * @return a modified string, in UTF8 format in the low bytes + */ + private String toUtf8(String str) + { + final char[] ca = str.toCharArray(); + final int len = ca.length; + + // Avoid object creation, if str is already fits UTF8. + int i; + for (i = 0; i < len; i++) + if (ca[i] == 0 || ca[i] > '\u007f') + break; + if (i == len) + return str; + + final StringBuffer sb = new StringBuffer(str); + sb.setLength(i); + for ( ; i < len; i++) + { + final char c = ca[i]; + if (c > 0 && c <= '\u007f') + sb.append(c); + else if (c <= '\u07ff') // includes '\0' + { + sb.append((char) (0xc0 | (c >> 6))); + sb.append((char) (0x80 | (c & 0x6f))); + } + else + { + sb.append((char) (0xe0 | (c >> 12))); + sb.append((char) (0x80 | ((c >> 6) & 0x6f))); + sb.append((char) (0x80 | (c & 0x6f))); + } + } + return sb.toString(); + } + + /** + * Returns the location of a byte sequence (conveniently wrapped in + * a String with all characters between \u0001 and \u00ff inclusive) + * in the constant pool, adding it if necessary. + * + * @param sequence the byte sequence to look for + * @return the index of the sequence + * @throws IllegalArgumentException if this would make the constant + * pool overflow + */ + private char poolIndex(String sequence) + { + Integer i = (Integer) poolEntries.get(sequence); + if (i == null) + { + // pool starts at index 1 + int size = poolEntries.size() + 1; + if (size >= 65535) + throw new IllegalArgumentException("exceeds VM limitations"); + i = new Integer(size); + poolEntries.put(sequence, i); + pool.append(sequence); + } + return (char) i.intValue(); + } + } // class ClassFactory +} diff --git a/libjava/classpath/java/lang/reflect/README b/libjava/classpath/java/lang/reflect/README new file mode 100644 index 00000000000..99ea224d7f6 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/README @@ -0,0 +1,4 @@ +README for java.lang.reflect: + +java.lang.reflect is now mostly empty. We've carved out the classes that have +to do with the VM and put them into the VM interface. diff --git a/libjava/classpath/java/lang/reflect/ReflectPermission.java b/libjava/classpath/java/lang/reflect/ReflectPermission.java new file mode 100644 index 00000000000..56eccf8130c --- /dev/null +++ b/libjava/classpath/java/lang/reflect/ReflectPermission.java @@ -0,0 +1,102 @@ +/* ReflectPermission.java - named permission for reflaction + Copyright (C) 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.reflect; + +import java.security.BasicPermission; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * This class implements permissions for reflection. This is a named + * permission, and the only defined name is suppressAccessChecks, which + * allows suppression of normal Java objects when using reflection. + * + * <table> + * <tr> + * <th>Permission Target Name</th> + * <th>What Permission Allows</th> + * <th>Risk of Allowing Permission</th> + * </tr> + * <tr> + * <td><code>suppressAccessChecks</code></td> + * <td>Ability to access fields, invoke methods, and construct objects + * via reflection, including non-public members in contexts where + * such access is not legal at compile-time.</td> + * <td>This is dangerous. It exposes possibly confidential information, + * and malicious code could interfere with the internals of the Virtual + * Machine by corrupting private data.</td> + * </tr> + * </table> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class ReflectPermission + extends BasicPermission +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 7412737110241507485L; + + /** + * Construct a ReflectPermission with the given name. + * + * @param name The permission name + */ + public ReflectPermission(String name) + { + super(name); + } + + /** + * Construct a ReflectPermission with the given name. + * + * @param name The permission name + * @param actions The actions; this is ignored and should be null + */ + public ReflectPermission(String name, String actions) + { + super(name, actions); + } +} diff --git a/libjava/classpath/java/lang/reflect/TODO b/libjava/classpath/java/lang/reflect/TODO new file mode 100755 index 00000000000..6514c760317 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/TODO @@ -0,0 +1,4 @@ +TODO for java.lang.reflect Java side + +- more tests! +- Java 2 support (waiting on java.lang Java 2 support) diff --git a/libjava/classpath/java/lang/reflect/Type.java b/libjava/classpath/java/lang/reflect/Type.java new file mode 100644 index 00000000000..c9ea5bfb9cb --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Type.java @@ -0,0 +1,55 @@ +/* Type.java - Superinterface for all types. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents any <code>Type</code> within the Java programming + * language. This may be a primitive type (e.g. <code>int</code>, + * an array type (e.g. <code>double[]>/code>), a raw type + * (e.g. <code>Calendar</code>), a parameterized type + * (e.g. <code>List<Boolean></code>, or a type + * variable (e.g. <code>T extends String</code>). + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Type +{ +} diff --git a/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java b/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java new file mode 100644 index 00000000000..6d5a8008459 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java @@ -0,0 +1,128 @@ +/* UndeclaredThrowableException.java -- wraps an undeclared checked exception + thrown by a Proxy invocation handler + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This exception class is thrown by a {@link Proxy} instance if + * the {@link InvocationHandler#invoke(Object, Method, Object[]) invoke} + * method of that instance's InvocationHandler attempts to throw an + * exception that not declared by the throws clauses of all of the + * interface methods that the proxy instance is implementing. + * + * <p>When thrown by Proxy, this class will always wrap a checked + * exception, never {@link Error} or {@link RuntimeException}, + * which are unchecked. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Proxy + * @see InvocationHandler + * @since 1.3 + * @status updated to 1.4 + */ +public class UndeclaredThrowableException extends RuntimeException +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = 330127114055056639L; + + /** + * The immutable exception that this wraps. This field is redundant + * with {@link Throwable#cause}, but is necessary for serial compatibility. + * + * @serial the chained exception + */ + private final Throwable undeclaredThrowable; + + /** + * Wraps the given checked exception into a RuntimeException, with no + * detail message. {@link Throwable#initCause(Throwable)} will fail + * on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + */ + public UndeclaredThrowableException(Throwable cause) + { + this(cause, null); + } + + /** + * Wraps the given checked exception into a RuntimeException, with the + * specified detail message. {@link Throwable#initCause(Throwable)} will + * fail on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + * @param message the message, may be null + */ + public UndeclaredThrowableException(Throwable cause, String message) + { + super(message, cause); + undeclaredThrowable = cause; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. This method pre-dates exception chaining, and is now + * simply a longer way to call <code>getCause()</code>. + * + * @return the cause of this exception, may be null + * @see #getCause() + */ + public Throwable getUndeclaredThrowable() + { + return undeclaredThrowable; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. + * + * @return the cause of this exception, may be null + * @since 1.4 + */ + public Throwable getCause() + { + return undeclaredThrowable; + } +} diff --git a/libjava/classpath/java/lang/reflect/WildcardType.java b/libjava/classpath/java/lang/reflect/WildcardType.java new file mode 100644 index 00000000000..4f789067cf0 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/WildcardType.java @@ -0,0 +1,115 @@ +/* WildcardType.java -- A wildcard type expression e.g. ? extends String + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents a wildcard type expression, where the type variable + * is unnamed. The simplest example of this is <code>?</code>, + * which represents any unbounded type. Another example is + * <code>? extends Number</code>, which specifies any type + * which is a subclass of <code>Number</code> (<code>Number</code> + * is the upper bound). + * </p> + * <p> + * <code>? super String</code> gives the type a less common lower bound, + * which means that the type must be either a <code>String</code> or one + * of its superclasses. This can be useful in working with collections. + * You may want a method to add instances of a class to a collection + * with a more generic type (e.g. adding <code>String</code>s to + * a list of <code>Object</code>s), but don't want to allow users + * to pass in a collection with a more specific type. + * </p> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface WildcardType extends Type +{ + + /** + * <p> + * Returns an array of <code>Type</code>s, which specify the + * lower bounds of this type. The default lower bound is + * <code>null</code>, which causes this method to return an + * empty array. + * </p> + * <p> + * In generating the array of <code>Type</code>s, each + * <code>ParameterizedType</code> or <code>TypeVariable</code> is + * created, (see the documentation for these classes for details of this + * process), if necessary, while all other types are simply + * resolved. + * </p> + * + * @return an array of <code>Type</code> objects, representing + * the wildcard type's lower bounds. + * @throws TypeNotPresentException if any of the types referred to by + * the lower bounds of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getLowerBounds(); + + /** + * <p> + * Returns an array of <code>Type</code>s, which specify the + * upper bounds of this type. The default upper bound is + * <code>Object</code>, which causes this method to return an + * array, containing just the <code>Type</code> instance for + * <code>Object</code>. + * </p> + * <p> + * In generating the array of <code>Type</code>s, each + * <code>ParameterizedType</code> or <code>TypeVariable</code> is + * created, (see the documentation for these classes for details of this + * process), if necessary, while all other types are simply + * resolved. + * </p> + * + * @return an array of <code>Type</code> objects, representing + * the wildcard type's upper bounds. + * @throws TypeNotPresentException if any of the types referred to by + * the upper bounds of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getUpperBounds(); + +} diff --git a/libjava/classpath/java/lang/reflect/package.html b/libjava/classpath/java/lang/reflect/package.html new file mode 100644 index 00000000000..9f7ed632836 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang.reflect package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.lang.reflect</title></head> + +<body> +<p>Runtime inspection and manipulation of object classes, methods, arguments +and fields.</p> + +</body> +</html> diff --git a/libjava/classpath/java/math/BigDecimal.java b/libjava/classpath/java/math/BigDecimal.java new file mode 100644 index 00000000000..d99be0f56ba --- /dev/null +++ b/libjava/classpath/java/math/BigDecimal.java @@ -0,0 +1,517 @@ +/* java.math.BigDecimal -- Arbitrary precision decimals. + Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.math; + +public class BigDecimal extends Number implements Comparable +{ + private BigInteger intVal; + private int scale; + private static final long serialVersionUID = 6108874887143696463L; + + private static final BigDecimal ZERO = + new BigDecimal (BigInteger.valueOf (0), 0); + + private static final BigDecimal ONE = + new BigDecimal (BigInteger.valueOf (1), 0); + + public static final int ROUND_UP = 0; + public static final int ROUND_DOWN = 1; + public static final int ROUND_CEILING = 2; + public static final int ROUND_FLOOR = 3; + public static final int ROUND_HALF_UP = 4; + public static final int ROUND_HALF_DOWN = 5; + public static final int ROUND_HALF_EVEN = 6; + public static final int ROUND_UNNECESSARY = 7; + + public BigDecimal (BigInteger num) + { + this (num, 0); + } + + public BigDecimal (BigInteger num, int scale) throws NumberFormatException + { + if (scale < 0) + throw new NumberFormatException ("scale of " + scale + " is < 0"); + this.intVal = num; + this.scale = scale; + } + + public BigDecimal (double num) throws NumberFormatException + { + if (Double.isInfinite (num) || Double.isNaN (num)) + throw new NumberFormatException ("invalid argument: " + num); + // Note we can't convert NUM to a String and then use the + // String-based constructor. The BigDecimal documentation makes + // it clear that the two constructors work differently. + + final int mantissaBits = 52; + final int exponentBits = 11; + final long mantMask = (1L << mantissaBits) - 1; + final long expMask = (1L << exponentBits) - 1; + + long bits = Double.doubleToLongBits (num); + long mantissa = bits & mantMask; + long exponent = (bits >>> mantissaBits) & expMask; + boolean denormal = exponent == 0; + // Correct the exponent for the bias. + exponent -= denormal ? 1022 : 1023; + // Now correct the exponent to account for the bits to the right + // of the decimal. + exponent -= mantissaBits; + // Ordinary numbers have an implied leading `1' bit. + if (! denormal) + mantissa |= (1L << mantissaBits); + + // Shave off factors of 10. + while (exponent < 0 && (mantissa & 1) == 0) + { + ++exponent; + mantissa >>= 1; + } + + intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa); + if (exponent < 0) + { + // We have MANTISSA * 2 ^ (EXPONENT). + // Since (1/2)^N == 5^N * 10^-N we can easily convert this + // into a power of 10. + scale = (int) (- exponent); + BigInteger mult = BigInteger.valueOf (5).pow (scale); + intVal = intVal.multiply (mult); + } + else + { + intVal = intVal.shiftLeft ((int) exponent); + scale = 0; + } + } + + public BigDecimal (String num) throws NumberFormatException + { + int len = num.length(); + int start = 0, point = 0; + int dot = -1; + boolean negative = false; + if (num.charAt(0) == '+') + { + ++start; + ++point; + } + else if (num.charAt(0) == '-') + { + ++start; + ++point; + negative = true; + } + + while (point < len) + { + char c = num.charAt (point); + if (c == '.') + { + if (dot >= 0) + throw new NumberFormatException ("multiple `.'s in number"); + dot = point; + } + else if (c == 'e' || c == 'E') + break; + else if (Character.digit (c, 10) < 0) + throw new NumberFormatException ("unrecognized character: " + c); + ++point; + } + + String val; + if (dot >= 0) + { + val = num.substring (start, dot) + num.substring (dot + 1, point); + scale = point - 1 - dot; + } + else + { + val = num.substring (start, point); + scale = 0; + } + if (val.length () == 0) + throw new NumberFormatException ("no digits seen"); + + if (negative) + val = "-" + val; + intVal = new BigInteger (val); + + // Now parse exponent. + if (point < len) + { + point++; + if (num.charAt(point) == '+') + point++; + + if (point >= len ) + throw new NumberFormatException ("no exponent following e or E"); + + try + { + int exp = Integer.parseInt (num.substring (point)); + exp -= scale; + if (signum () == 0) + scale = 0; + else if (exp > 0) + { + intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp)); + scale = 0; + } + else + scale = - exp; + } + catch (NumberFormatException ex) + { + throw new NumberFormatException ("malformed exponent"); + } + } + } + + public static BigDecimal valueOf (long val) + { + return valueOf (val, 0); + } + + public static BigDecimal valueOf (long val, int scale) + throws NumberFormatException + { + if ((scale == 0) && ((int)val == val)) + switch ((int) val) + { + case 0: + return ZERO; + case 1: + return ONE; + } + + return new BigDecimal (BigInteger.valueOf (val), scale); + } + + public BigDecimal add (BigDecimal val) + { + // For addition, need to line up decimals. Note that the movePointRight + // method cannot be used for this as it might return a BigDecimal with + // scale == 0 instead of the scale we need. + BigInteger op1 = intVal; + BigInteger op2 = val.intVal; + if (scale < val.scale) + op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale)); + else if (scale > val.scale) + op2 = op2.multiply (BigInteger.valueOf (10).pow (scale - val.scale)); + + return new BigDecimal (op1.add (op2), Math.max (scale, val.scale)); + } + + public BigDecimal subtract (BigDecimal val) + { + return this.add(val.negate()); + } + + public BigDecimal multiply (BigDecimal val) + { + return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); + } + + public BigDecimal divide (BigDecimal val, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + return divide (val, scale, roundingMode); + } + + public BigDecimal divide(BigDecimal val, int newScale, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + if (roundingMode < 0 || roundingMode > 7) + throw + new IllegalArgumentException("illegal rounding mode: " + roundingMode); + + if (newScale < 0) + throw new ArithmeticException ("scale is negative: " + newScale); + + if (intVal.signum () == 0) // handle special case of 0.0/0.0 + return newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale); + + // Ensure that pow gets a non-negative value. + BigInteger valIntVal = val.intVal; + int power = newScale - (scale - val.scale); + if (power < 0) + { + // Effectively increase the scale of val to avoid an + // ArithmeticException for a negative power. + valIntVal = valIntVal.multiply (BigInteger.valueOf (10).pow (-power)); + power = 0; + } + + BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (power)); + + BigInteger parts[] = dividend.divideAndRemainder (valIntVal); + + BigInteger unrounded = parts[0]; + if (parts[1].signum () == 0) // no remainder, no rounding necessary + return new BigDecimal (unrounded, newScale); + + if (roundingMode == ROUND_UNNECESSARY) + throw new ArithmeticException ("newScale is not large enough"); + + int sign = intVal.signum () * valIntVal.signum (); + + if (roundingMode == ROUND_CEILING) + roundingMode = (sign > 0) ? ROUND_UP : ROUND_DOWN; + else if (roundingMode == ROUND_FLOOR) + roundingMode = (sign < 0) ? ROUND_UP : ROUND_DOWN; + else + { + // half is -1 if remainder*2 < positive intValue (*power), 0 if equal, + // 1 if >. This implies that the remainder to round is less than, + // equal to, or greater than half way to the next digit. + BigInteger posRemainder + = parts[1].signum () < 0 ? parts[1].negate() : parts[1]; + valIntVal = valIntVal.signum () < 0 ? valIntVal.negate () : valIntVal; + int half = posRemainder.shiftLeft(1).compareTo(valIntVal); + + switch(roundingMode) + { + case ROUND_HALF_UP: + roundingMode = (half < 0) ? ROUND_DOWN : ROUND_UP; + break; + case ROUND_HALF_DOWN: + roundingMode = (half > 0) ? ROUND_UP : ROUND_DOWN; + break; + case ROUND_HALF_EVEN: + if (half < 0) + roundingMode = ROUND_DOWN; + else if (half > 0) + roundingMode = ROUND_UP; + else if (unrounded.testBit(0)) // odd, then ROUND_HALF_UP + roundingMode = ROUND_UP; + else // even, ROUND_HALF_DOWN + roundingMode = ROUND_DOWN; + break; + } + } + + if (roundingMode == ROUND_UP) + unrounded = unrounded.add (BigInteger.valueOf (sign > 0 ? 1 : -1)); + + // roundingMode == ROUND_DOWN + return new BigDecimal (unrounded, newScale); + } + + public int compareTo (BigDecimal val) + { + if (scale == val.scale) + return intVal.compareTo (val.intVal); + + BigInteger thisParts[] = + intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); + BigInteger valParts[] = + val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); + + int compare; + if ((compare = thisParts[0].compareTo (valParts[0])) != 0) + return compare; + + // quotients are the same, so compare remainders + + // remove trailing zeros + if (thisParts[1].equals (BigInteger.valueOf (0)) == false) + while (thisParts[1].mod (BigInteger.valueOf (10)).equals + (BigInteger.valueOf (0))) + thisParts[1] = thisParts[1].divide (BigInteger.valueOf (10)); + // again... + if (valParts[1].equals(BigInteger.valueOf (0)) == false) + while (valParts[1].mod (BigInteger.valueOf (10)).equals + (BigInteger.valueOf (0))) + valParts[1] = valParts[1].divide (BigInteger.valueOf (10)); + + // and compare them + return thisParts[1].compareTo (valParts[1]); + } + + public int compareTo (Object val) + { + return(compareTo((BigDecimal)val)); + } + + public boolean equals (Object o) + { + return (o instanceof BigDecimal + && scale == ((BigDecimal) o).scale + && compareTo ((BigDecimal) o) == 0); + } + + public int hashCode() + { + return intValue() ^ scale; + } + + public BigDecimal max (BigDecimal val) + { + switch (compareTo (val)) + { + case 1: + return this; + default: + return val; + } + } + + public BigDecimal min (BigDecimal val) + { + switch (compareTo (val)) + { + case -1: + return this; + default: + return val; + } + } + + public BigDecimal movePointLeft (int n) + { + return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n); + } + + public BigDecimal movePointRight (int n) + { + if (n < 0) + return movePointLeft (-n); + + if (scale >= n) + return new BigDecimal (intVal, scale - n); + + return new BigDecimal (intVal.multiply + (BigInteger.valueOf (10).pow (n - scale)), 0); + } + + public int signum () + { + return intVal.signum (); + } + + public int scale () + { + return scale; + } + + public BigInteger unscaledValue() + { + return intVal; + } + + public BigDecimal abs () + { + return new BigDecimal (intVal.abs (), scale); + } + + public BigDecimal negate () + { + return new BigDecimal (intVal.negate (), scale); + } + + public String toString () + { + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + boolean negative = (bigStr.charAt(0) == '-'); + + int point = bigStr.length() - scale - (negative ? 1 : 0); + + StringBuffer sb = new StringBuffer(bigStr.length() + 2 + + (point <= 0 ? (-point + 1) : 0)); + if (point <= 0) + { + if (negative) + sb.append('-'); + sb.append('0').append('.'); + while (point < 0) + { + sb.append('0'); + point++; + } + sb.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + sb.append(bigStr); + sb.insert(point + (negative ? 1 : 0), '.'); + } + return sb.toString(); + } + + public BigInteger toBigInteger () + { + return scale == 0 ? intVal : + intVal.divide (BigInteger.valueOf (10).pow (scale)); + } + + public int intValue () + { + return toBigInteger ().intValue (); + } + + public long longValue () + { + return toBigInteger().longValue(); + } + + public float floatValue() + { + return Float.valueOf(toString()).floatValue(); + } + + public double doubleValue() + { + return Double.valueOf(toString()).doubleValue(); + } + + public BigDecimal setScale (int scale) throws ArithmeticException + { + return setScale (scale, ROUND_UNNECESSARY); + } + + public BigDecimal setScale (int scale, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + return divide (ONE, scale, roundingMode); + } +} diff --git a/libjava/classpath/java/math/BigInteger.java b/libjava/classpath/java/math/BigInteger.java new file mode 100644 index 00000000000..82f550d144c --- /dev/null +++ b/libjava/classpath/java/math/BigInteger.java @@ -0,0 +1,2230 @@ +/* java.math.BigInteger -- Arbitary precision integers + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.math; + +import gnu.java.math.MPN; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Random; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998) and + * "Applied Cryptography, Second Edition" by Bruce Schneier (Wiley, 1996). + * + * Based primarily on IntNum.java BitOps.java by Per Bothner (per@bothner.com) + * (found in Kawa 1.6.62). + * + * @author Warren Levy (warrenl@cygnus.com) + * @date December 20, 1999. + * @status believed complete and correct. + */ +public class BigInteger extends Number implements Comparable +{ + /** All integers are stored in 2's-complement form. + * If words == null, the ival is the value of this BigInteger. + * Otherwise, the first ival elements of words make the value + * of this BigInteger, stored in little-endian order, 2's-complement form. */ + private transient int ival; + private transient int[] words; + + // Serialization fields. + private int bitCount = -1; + private int bitLength = -1; + private int firstNonzeroByteNum = -2; + private int lowestSetBit = -2; + private byte[] magnitude; + private int signum; + private static final long serialVersionUID = -8287574255936472291L; + + + /** We pre-allocate integers in the range minFixNum..maxFixNum. */ + private static final int minFixNum = -100; + private static final int maxFixNum = 1024; + private static final int numFixNum = maxFixNum-minFixNum+1; + private static final BigInteger[] smallFixNums = new BigInteger[numFixNum]; + + static { + for (int i = numFixNum; --i >= 0; ) + smallFixNums[i] = new BigInteger(i + minFixNum); + } + + // JDK1.2 + public static final BigInteger ZERO = smallFixNums[-minFixNum]; + + // JDK1.2 + public static final BigInteger ONE = smallFixNums[1 - minFixNum]; + + /* Rounding modes: */ + private static final int FLOOR = 1; + private static final int CEILING = 2; + private static final int TRUNCATE = 3; + private static final int ROUND = 4; + + /** When checking the probability of primes, it is most efficient to + * first check the factoring of small primes, so we'll use this array. + */ + private static final int[] primes = + { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, + 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, + 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251 }; + + /** HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Table 4.4. */ + private static final int[] k = + {100,150,200,250,300,350,400,500,600,800,1250, Integer.MAX_VALUE}; + private static final int[] t = + { 27, 18, 15, 12, 9, 8, 7, 6, 5, 4, 3, 2}; + + private BigInteger() + { + } + + /* Create a new (non-shared) BigInteger, and initialize to an int. */ + private BigInteger(int value) + { + ival = value; + } + + public BigInteger(String val, int radix) + { + BigInteger result = valueOf(val, radix); + this.ival = result.ival; + this.words = result.words; + } + + public BigInteger(String val) + { + this(val, 10); + } + + /* Create a new (non-shared) BigInteger, and initialize from a byte array. */ + public BigInteger(byte[] val) + { + if (val == null || val.length < 1) + throw new NumberFormatException(); + + words = byteArrayToIntArray(val, val[0] < 0 ? -1 : 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + } + + public BigInteger(int signum, byte[] magnitude) + { + if (magnitude == null || signum > 1 || signum < -1) + throw new NumberFormatException(); + + if (signum == 0) + { + int i; + for (i = magnitude.length - 1; i >= 0 && magnitude[i] == 0; --i) + ; + if (i >= 0) + throw new NumberFormatException(); + return; + } + + // Magnitude is always positive, so don't ever pass a sign of -1. + words = byteArrayToIntArray(magnitude, 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + + if (signum < 0) + setNegative(); + } + + public BigInteger(int numBits, Random rnd) + { + if (numBits < 0) + throw new IllegalArgumentException(); + + init(numBits, rnd); + } + + private void init(int numBits, Random rnd) + { + int highbits = numBits & 31; + if (highbits > 0) + highbits = rnd.nextInt() >>> (32 - highbits); + int nwords = numBits / 32; + + while (highbits == 0 && nwords > 0) + { + highbits = rnd.nextInt(); + --nwords; + } + if (nwords == 0 && highbits >= 0) + { + ival = highbits; + } + else + { + ival = highbits < 0 ? nwords + 2 : nwords + 1; + words = new int[ival]; + words[nwords] = highbits; + while (--nwords >= 0) + words[nwords] = rnd.nextInt(); + } + } + + public BigInteger(int bitLength, int certainty, Random rnd) + { + this(bitLength, rnd); + + // Keep going until we find a probable prime. + while (true) + { + if (isProbablePrime(certainty)) + return; + + init(bitLength, rnd); + } + } + + /** + * Return a BigInteger that is bitLength bits long with a + * probability < 2^-100 of being composite. + * + * @param bitLength length in bits of resulting number + * @param rnd random number generator to use + * @throws ArithmeticException if bitLength < 2 + * @since 1.4 + */ + public static BigInteger probablePrime(int bitLength, Random rnd) + { + if (bitLength < 2) + throw new ArithmeticException(); + + return new BigInteger(bitLength, 100, rnd); + } + + /** Return a (possibly-shared) BigInteger with a given long value. */ + public static BigInteger valueOf(long val) + { + if (val >= minFixNum && val <= maxFixNum) + return smallFixNums[(int) val - minFixNum]; + int i = (int) val; + if ((long) i == val) + return new BigInteger(i); + BigInteger result = alloc(2); + result.ival = 2; + result.words[0] = i; + result.words[1] = (int)(val >> 32); + return result; + } + + /** Make a canonicalized BigInteger from an array of words. + * The array may be reused (without copying). */ + private static BigInteger make(int[] words, int len) + { + if (words == null) + return valueOf(len); + len = BigInteger.wordsNeeded(words, len); + if (len <= 1) + return len == 0 ? ZERO : valueOf(words[0]); + BigInteger num = new BigInteger(); + num.words = words; + num.ival = len; + return num; + } + + /** Convert a big-endian byte array to a little-endian array of words. */ + private static int[] byteArrayToIntArray(byte[] bytes, int sign) + { + // Determine number of words needed. + int[] words = new int[bytes.length/4 + 1]; + int nwords = words.length; + + // Create a int out of modulo 4 high order bytes. + int bptr = 0; + int word = sign; + for (int i = bytes.length % 4; i > 0; --i, bptr++) + word = (word << 8) | (bytes[bptr] & 0xff); + words[--nwords] = word; + + // Elements remaining in byte[] are a multiple of 4. + while (nwords > 0) + words[--nwords] = bytes[bptr++] << 24 | + (bytes[bptr++] & 0xff) << 16 | + (bytes[bptr++] & 0xff) << 8 | + (bytes[bptr++] & 0xff); + return words; + } + + /** Allocate a new non-shared BigInteger. + * @param nwords number of words to allocate + */ + private static BigInteger alloc(int nwords) + { + BigInteger result = new BigInteger(); + if (nwords > 1) + result.words = new int[nwords]; + return result; + } + + /** Change words.length to nwords. + * We allow words.length to be upto nwords+2 without reallocating. + */ + private void realloc(int nwords) + { + if (nwords == 0) + { + if (words != null) + { + if (ival > 0) + ival = words[0]; + words = null; + } + } + else if (words == null + || words.length < nwords + || words.length > nwords + 2) + { + int[] new_words = new int [nwords]; + if (words == null) + { + new_words[0] = ival; + ival = 1; + } + else + { + if (nwords < ival) + ival = nwords; + System.arraycopy(words, 0, new_words, 0, ival); + } + words = new_words; + } + } + + private boolean isNegative() + { + return (words == null ? ival : words[ival - 1]) < 0; + } + + public int signum() + { + int top = words == null ? ival : words[ival-1]; + if (top == 0 && words == null) + return 0; + return top < 0 ? -1 : 1; + } + + private static int compareTo(BigInteger x, BigInteger y) + { + if (x.words == null && y.words == null) + return x.ival < y.ival ? -1 : x.ival > y.ival ? 1 : 0; + boolean x_negative = x.isNegative(); + boolean y_negative = y.isNegative(); + if (x_negative != y_negative) + return x_negative ? -1 : 1; + int x_len = x.words == null ? 1 : x.ival; + int y_len = y.words == null ? 1 : y.ival; + if (x_len != y_len) + return (x_len > y_len) != x_negative ? 1 : -1; + return MPN.cmp(x.words, y.words, x_len); + } + + // JDK1.2 + public int compareTo(Object obj) + { + if (obj instanceof BigInteger) + return compareTo(this, (BigInteger) obj); + throw new ClassCastException(); + } + + public int compareTo(BigInteger val) + { + return compareTo(this, val); + } + + public BigInteger min(BigInteger val) + { + return compareTo(this, val) < 0 ? this : val; + } + + public BigInteger max(BigInteger val) + { + return compareTo(this, val) > 0 ? this : val; + } + + private boolean isZero() + { + return words == null && ival == 0; + } + + private boolean isOne() + { + return words == null && ival == 1; + } + + /** Calculate how many words are significant in words[0:len-1]. + * Returns the least value x such that x>0 && words[0:x-1]==words[0:len-1], + * when words is viewed as a 2's complement integer. + */ + private static int wordsNeeded(int[] words, int len) + { + int i = len; + if (i > 0) + { + int word = words[--i]; + if (word == -1) + { + while (i > 0 && (word = words[i - 1]) < 0) + { + i--; + if (word != -1) break; + } + } + else + { + while (word == 0 && i > 0 && (word = words[i - 1]) >= 0) i--; + } + } + return i + 1; + } + + private BigInteger canonicalize() + { + if (words != null + && (ival = BigInteger.wordsNeeded(words, ival)) <= 1) + { + if (ival == 1) + ival = words[0]; + words = null; + } + if (words == null && ival >= minFixNum && ival <= maxFixNum) + return smallFixNums[ival - minFixNum]; + return this; + } + + /** Add two ints, yielding a BigInteger. */ + private static BigInteger add(int x, int y) + { + return valueOf((long) x + (long) y); + } + + /** Add a BigInteger and an int, yielding a new BigInteger. */ + private static BigInteger add(BigInteger x, int y) + { + if (x.words == null) + return BigInteger.add(x.ival, y); + BigInteger result = new BigInteger(0); + result.setAdd(x, y); + return result.canonicalize(); + } + + /** Set this to the sum of x and y. + * OK if x==this. */ + private void setAdd(BigInteger x, int y) + { + if (x.words == null) + { + set((long) x.ival + (long) y); + return; + } + int len = x.ival; + realloc(len + 1); + long carry = y; + for (int i = 0; i < len; i++) + { + carry += ((long) x.words[i] & 0xffffffffL); + words[i] = (int) carry; + carry >>= 32; + } + if (x.words[len - 1] < 0) + carry--; + words[len] = (int) carry; + ival = wordsNeeded(words, len + 1); + } + + /** Destructively add an int to this. */ + private void setAdd(int y) + { + setAdd(this, y); + } + + /** Destructively set the value of this to a long. */ + private void set(long y) + { + int i = (int) y; + if ((long) i == y) + { + ival = i; + words = null; + } + else + { + realloc(2); + words[0] = i; + words[1] = (int) (y >> 32); + ival = 2; + } + } + + /** Destructively set the value of this to the given words. + * The words array is reused, not copied. */ + private void set(int[] words, int length) + { + this.ival = length; + this.words = words; + } + + /** Destructively set the value of this to that of y. */ + private void set(BigInteger y) + { + if (y.words == null) + set(y.ival); + else if (this != y) + { + realloc(y.ival); + System.arraycopy(y.words, 0, words, 0, y.ival); + ival = y.ival; + } + } + + /** Add two BigIntegers, yielding their sum as another BigInteger. */ + private static BigInteger add(BigInteger x, BigInteger y, int k) + { + if (x.words == null && y.words == null) + return valueOf((long) k * (long) y.ival + (long) x.ival); + if (k != 1) + { + if (k == -1) + y = BigInteger.neg(y); + else + y = BigInteger.times(y, valueOf(k)); + } + if (x.words == null) + return BigInteger.add(y, x.ival); + if (y.words == null) + return BigInteger.add(x, y.ival); + // Both are big + if (y.ival > x.ival) + { // Swap so x is longer then y. + BigInteger tmp = x; x = y; y = tmp; + } + BigInteger result = alloc(x.ival + 1); + int i = y.ival; + long carry = MPN.add_n(result.words, x.words, y.words, i); + long y_ext = y.words[i - 1] < 0 ? 0xffffffffL : 0; + for (; i < x.ival; i++) + { + carry += ((long) x.words[i] & 0xffffffffL) + y_ext;; + result.words[i] = (int) carry; + carry >>>= 32; + } + if (x.words[i - 1] < 0) + y_ext--; + result.words[i] = (int) (carry + y_ext); + result.ival = i+1; + return result.canonicalize(); + } + + public BigInteger add(BigInteger val) + { + return add(this, val, 1); + } + + public BigInteger subtract(BigInteger val) + { + return add(this, val, -1); + } + + private static BigInteger times(BigInteger x, int y) + { + if (y == 0) + return ZERO; + if (y == 1) + return x; + int[] xwords = x.words; + int xlen = x.ival; + if (xwords == null) + return valueOf((long) xlen * (long) y); + boolean negative; + BigInteger result = BigInteger.alloc(xlen + 1); + if (xwords[xlen - 1] < 0) + { + negative = true; + negate(result.words, xwords, xlen); + xwords = result.words; + } + else + negative = false; + if (y < 0) + { + negative = !negative; + y = -y; + } + result.words[xlen] = MPN.mul_1(result.words, xwords, xlen, y); + result.ival = xlen + 1; + if (negative) + result.setNegative(); + return result.canonicalize(); + } + + private static BigInteger times(BigInteger x, BigInteger y) + { + if (y.words == null) + return times(x, y.ival); + if (x.words == null) + return times(y, x.ival); + boolean negative = false; + int[] xwords; + int[] ywords; + int xlen = x.ival; + int ylen = y.ival; + if (x.isNegative()) + { + negative = true; + xwords = new int[xlen]; + negate(xwords, x.words, xlen); + } + else + { + negative = false; + xwords = x.words; + } + if (y.isNegative()) + { + negative = !negative; + ywords = new int[ylen]; + negate(ywords, y.words, ylen); + } + else + ywords = y.words; + // Swap if x is shorter then y. + if (xlen < ylen) + { + int[] twords = xwords; xwords = ywords; ywords = twords; + int tlen = xlen; xlen = ylen; ylen = tlen; + } + BigInteger result = BigInteger.alloc(xlen+ylen); + MPN.mul(result.words, xwords, xlen, ywords, ylen); + result.ival = xlen+ylen; + if (negative) + result.setNegative(); + return result.canonicalize(); + } + + public BigInteger multiply(BigInteger y) + { + return times(this, y); + } + + private static void divide(long x, long y, + BigInteger quotient, BigInteger remainder, + int rounding_mode) + { + boolean xNegative, yNegative; + if (x < 0) + { + xNegative = true; + if (x == Long.MIN_VALUE) + { + divide(valueOf(x), valueOf(y), + quotient, remainder, rounding_mode); + return; + } + x = -x; + } + else + xNegative = false; + + if (y < 0) + { + yNegative = true; + if (y == Long.MIN_VALUE) + { + if (rounding_mode == TRUNCATE) + { // x != Long.Min_VALUE implies abs(x) < abs(y) + if (quotient != null) + quotient.set(0); + if (remainder != null) + remainder.set(x); + } + else + divide(valueOf(x), valueOf(y), + quotient, remainder, rounding_mode); + return; + } + y = -y; + } + else + yNegative = false; + + long q = x / y; + long r = x % y; + boolean qNegative = xNegative ^ yNegative; + + boolean add_one = false; + if (r != 0) + { + switch (rounding_mode) + { + case TRUNCATE: + break; + case CEILING: + case FLOOR: + if (qNegative == (rounding_mode == FLOOR)) + add_one = true; + break; + case ROUND: + add_one = r > ((y - (q & 1)) >> 1); + break; + } + } + if (quotient != null) + { + if (add_one) + q++; + if (qNegative) + q = -q; + quotient.set(q); + } + if (remainder != null) + { + // The remainder is by definition: X-Q*Y + if (add_one) + { + // Subtract the remainder from Y. + r = y - r; + // In this case, abs(Q*Y) > abs(X). + // So sign(remainder) = -sign(X). + xNegative = ! xNegative; + } + else + { + // If !add_one, then: abs(Q*Y) <= abs(X). + // So sign(remainder) = sign(X). + } + if (xNegative) + r = -r; + remainder.set(r); + } + } + + /** Divide two integers, yielding quotient and remainder. + * @param x the numerator in the division + * @param y the denominator in the division + * @param quotient is set to the quotient of the result (iff quotient!=null) + * @param remainder is set to the remainder of the result + * (iff remainder!=null) + * @param rounding_mode one of FLOOR, CEILING, TRUNCATE, or ROUND. + */ + private static void divide(BigInteger x, BigInteger y, + BigInteger quotient, BigInteger remainder, + int rounding_mode) + { + if ((x.words == null || x.ival <= 2) + && (y.words == null || y.ival <= 2)) + { + long x_l = x.longValue(); + long y_l = y.longValue(); + if (x_l != Long.MIN_VALUE && y_l != Long.MIN_VALUE) + { + divide(x_l, y_l, quotient, remainder, rounding_mode); + return; + } + } + + boolean xNegative = x.isNegative(); + boolean yNegative = y.isNegative(); + boolean qNegative = xNegative ^ yNegative; + + int ylen = y.words == null ? 1 : y.ival; + int[] ywords = new int[ylen]; + y.getAbsolute(ywords); + while (ylen > 1 && ywords[ylen - 1] == 0) ylen--; + + int xlen = x.words == null ? 1 : x.ival; + int[] xwords = new int[xlen+2]; + x.getAbsolute(xwords); + while (xlen > 1 && xwords[xlen-1] == 0) xlen--; + + int qlen, rlen; + + int cmpval = MPN.cmp(xwords, xlen, ywords, ylen); + if (cmpval < 0) // abs(x) < abs(y) + { // quotient = 0; remainder = num. + int[] rwords = xwords; xwords = ywords; ywords = rwords; + rlen = xlen; qlen = 1; xwords[0] = 0; + } + else if (cmpval == 0) // abs(x) == abs(y) + { + xwords[0] = 1; qlen = 1; // quotient = 1 + ywords[0] = 0; rlen = 1; // remainder = 0; + } + else if (ylen == 1) + { + qlen = xlen; + // Need to leave room for a word of leading zeros if dividing by 1 + // and the dividend has the high bit set. It might be safe to + // increment qlen in all cases, but it certainly is only necessary + // in the following case. + if (ywords[0] == 1 && xwords[xlen-1] < 0) + qlen++; + rlen = 1; + ywords[0] = MPN.divmod_1(xwords, xwords, xlen, ywords[0]); + } + else // abs(x) > abs(y) + { + // Normalize the denominator, i.e. make its most significant bit set by + // shifting it normalization_steps bits to the left. Also shift the + // numerator the same number of steps (to keep the quotient the same!). + + int nshift = MPN.count_leading_zeros(ywords[ylen - 1]); + if (nshift != 0) + { + // Shift up the denominator setting the most significant bit of + // the most significant word. + MPN.lshift(ywords, 0, ywords, ylen, nshift); + + // Shift up the numerator, possibly introducing a new most + // significant word. + int x_high = MPN.lshift(xwords, 0, xwords, xlen, nshift); + xwords[xlen++] = x_high; + } + + if (xlen == ylen) + xwords[xlen++] = 0; + MPN.divide(xwords, xlen, ywords, ylen); + rlen = ylen; + MPN.rshift0 (ywords, xwords, 0, rlen, nshift); + + qlen = xlen + 1 - ylen; + if (quotient != null) + { + for (int i = 0; i < qlen; i++) + xwords[i] = xwords[i+ylen]; + } + } + + if (ywords[rlen-1] < 0) + { + ywords[rlen] = 0; + rlen++; + } + + // Now the quotient is in xwords, and the remainder is in ywords. + + boolean add_one = false; + if (rlen > 1 || ywords[0] != 0) + { // Non-zero remainder i.e. in-exact quotient. + switch (rounding_mode) + { + case TRUNCATE: + break; + case CEILING: + case FLOOR: + if (qNegative == (rounding_mode == FLOOR)) + add_one = true; + break; + case ROUND: + // int cmp = compareTo(remainder<<1, abs(y)); + BigInteger tmp = remainder == null ? new BigInteger() : remainder; + tmp.set(ywords, rlen); + tmp = shift(tmp, 1); + if (yNegative) + tmp.setNegative(); + int cmp = compareTo(tmp, y); + // Now cmp == compareTo(sign(y)*(remainder<<1), y) + if (yNegative) + cmp = -cmp; + add_one = (cmp == 1) || (cmp == 0 && (xwords[0]&1) != 0); + } + } + if (quotient != null) + { + quotient.set(xwords, qlen); + if (qNegative) + { + if (add_one) // -(quotient + 1) == ~(quotient) + quotient.setInvert(); + else + quotient.setNegative(); + } + else if (add_one) + quotient.setAdd(1); + } + if (remainder != null) + { + // The remainder is by definition: X-Q*Y + remainder.set(ywords, rlen); + if (add_one) + { + // Subtract the remainder from Y: + // abs(R) = abs(Y) - abs(orig_rem) = -(abs(orig_rem) - abs(Y)). + BigInteger tmp; + if (y.words == null) + { + tmp = remainder; + tmp.set(yNegative ? ywords[0] + y.ival : ywords[0] - y.ival); + } + else + tmp = BigInteger.add(remainder, y, yNegative ? 1 : -1); + // Now tmp <= 0. + // In this case, abs(Q) = 1 + floor(abs(X)/abs(Y)). + // Hence, abs(Q*Y) > abs(X). + // So sign(remainder) = -sign(X). + if (xNegative) + remainder.setNegative(tmp); + else + remainder.set(tmp); + } + else + { + // If !add_one, then: abs(Q*Y) <= abs(X). + // So sign(remainder) = sign(X). + if (xNegative) + remainder.setNegative(); + } + } + } + + public BigInteger divide(BigInteger val) + { + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger quot = new BigInteger(); + divide(this, val, quot, null, TRUNCATE); + return quot.canonicalize(); + } + + public BigInteger remainder(BigInteger val) + { + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger rem = new BigInteger(); + divide(this, val, null, rem, TRUNCATE); + return rem.canonicalize(); + } + + public BigInteger[] divideAndRemainder(BigInteger val) + { + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger[] result = new BigInteger[2]; + result[0] = new BigInteger(); + result[1] = new BigInteger(); + divide(this, val, result[0], result[1], TRUNCATE); + result[0].canonicalize(); + result[1].canonicalize(); + return result; + } + + public BigInteger mod(BigInteger m) + { + if (m.isNegative() || m.isZero()) + throw new ArithmeticException("non-positive modulus"); + + BigInteger rem = new BigInteger(); + divide(this, m, null, rem, FLOOR); + return rem.canonicalize(); + } + + /** Calculate the integral power of a BigInteger. + * @param exponent the exponent (must be non-negative) + */ + public BigInteger pow(int exponent) + { + if (exponent <= 0) + { + if (exponent == 0) + return ONE; + throw new ArithmeticException("negative exponent"); + } + if (isZero()) + return this; + int plen = words == null ? 1 : ival; // Length of pow2. + int blen = ((bitLength() * exponent) >> 5) + 2 * plen; + boolean negative = isNegative() && (exponent & 1) != 0; + int[] pow2 = new int [blen]; + int[] rwords = new int [blen]; + int[] work = new int [blen]; + getAbsolute(pow2); // pow2 = abs(this); + int rlen = 1; + rwords[0] = 1; // rwords = 1; + for (;;) // for (i = 0; ; i++) + { + // pow2 == this**(2**i) + // prod = this**(sum(j=0..i-1, (exponent>>j)&1)) + if ((exponent & 1) != 0) + { // r *= pow2 + MPN.mul(work, pow2, plen, rwords, rlen); + int[] temp = work; work = rwords; rwords = temp; + rlen += plen; + while (rwords[rlen - 1] == 0) rlen--; + } + exponent >>= 1; + if (exponent == 0) + break; + // pow2 *= pow2; + MPN.mul(work, pow2, plen, pow2, plen); + int[] temp = work; work = pow2; pow2 = temp; // swap to avoid a copy + plen *= 2; + while (pow2[plen - 1] == 0) plen--; + } + if (rwords[rlen - 1] < 0) + rlen++; + if (negative) + negate(rwords, rwords, rlen); + return BigInteger.make(rwords, rlen); + } + + private static int[] euclidInv(int a, int b, int prevDiv) + { + if (b == 0) + throw new ArithmeticException("not invertible"); + + if (b == 1) + // Success: values are indeed invertible! + // Bottom of the recursion reached; start unwinding. + return new int[] { -prevDiv, 1 }; + + int[] xy = euclidInv(b, a % b, a / b); // Recursion happens here. + a = xy[0]; // use our local copy of 'a' as a work var + xy[0] = a * -prevDiv + xy[1]; + xy[1] = a; + return xy; + } + + private static void euclidInv(BigInteger a, BigInteger b, + BigInteger prevDiv, BigInteger[] xy) + { + if (b.isZero()) + throw new ArithmeticException("not invertible"); + + if (b.isOne()) + { + // Success: values are indeed invertible! + // Bottom of the recursion reached; start unwinding. + xy[0] = neg(prevDiv); + xy[1] = ONE; + return; + } + + // Recursion happens in the following conditional! + + // If a just contains an int, then use integer math for the rest. + if (a.words == null) + { + int[] xyInt = euclidInv(b.ival, a.ival % b.ival, a.ival / b.ival); + xy[0] = new BigInteger(xyInt[0]); + xy[1] = new BigInteger(xyInt[1]); + } + else + { + BigInteger rem = new BigInteger(); + BigInteger quot = new BigInteger(); + divide(a, b, quot, rem, FLOOR); + // quot and rem may not be in canonical form. ensure + rem.canonicalize(); + quot.canonicalize(); + euclidInv(b, rem, quot, xy); + } + + BigInteger t = xy[0]; + xy[0] = add(xy[1], times(t, prevDiv), -1); + xy[1] = t; + } + + public BigInteger modInverse(BigInteger y) + { + if (y.isNegative() || y.isZero()) + throw new ArithmeticException("non-positive modulo"); + + // Degenerate cases. + if (y.isOne()) + return ZERO; + if (isOne()) + return ONE; + + // Use Euclid's algorithm as in gcd() but do this recursively + // rather than in a loop so we can use the intermediate results as we + // unwind from the recursion. + // Used http://www.math.nmsu.edu/~crypto/EuclideanAlgo.html as reference. + BigInteger result = new BigInteger(); + boolean swapped = false; + + if (y.words == null) + { + // The result is guaranteed to be less than the modulus, y (which is + // an int), so simplify this by working with the int result of this + // modulo y. Also, if this is negative, make it positive via modulo + // math. Note that BigInteger.mod() must be used even if this is + // already an int as the % operator would provide a negative result if + // this is negative, BigInteger.mod() never returns negative values. + int xval = (words != null || isNegative()) ? mod(y).ival : ival; + int yval = y.ival; + + // Swap values so x > y. + if (yval > xval) + { + int tmp = xval; xval = yval; yval = tmp; + swapped = true; + } + // Normally, the result is in the 2nd element of the array, but + // if originally x < y, then x and y were swapped and the result + // is in the 1st element of the array. + result.ival = + euclidInv(yval, xval % yval, xval / yval)[swapped ? 0 : 1]; + + // Result can't be negative, so make it positive by adding the + // original modulus, y.ival (not the possibly "swapped" yval). + if (result.ival < 0) + result.ival += y.ival; + } + else + { + // As above, force this to be a positive value via modulo math. + BigInteger x = isNegative() ? this.mod(y) : this; + + // Swap values so x > y. + if (x.compareTo(y) < 0) + { + result = x; x = y; y = result; // use 'result' as a work var + swapped = true; + } + // As above (for ints), result will be in the 2nd element unless + // the original x and y were swapped. + BigInteger rem = new BigInteger(); + BigInteger quot = new BigInteger(); + divide(x, y, quot, rem, FLOOR); + // quot and rem may not be in canonical form. ensure + rem.canonicalize(); + quot.canonicalize(); + BigInteger[] xy = new BigInteger[2]; + euclidInv(y, rem, quot, xy); + result = swapped ? xy[0] : xy[1]; + + // Result can't be negative, so make it positive by adding the + // original modulus, y (which is now x if they were swapped). + if (result.isNegative()) + result = add(result, swapped ? x : y, 1); + } + + return result; + } + + public BigInteger modPow(BigInteger exponent, BigInteger m) + { + if (m.isNegative() || m.isZero()) + throw new ArithmeticException("non-positive modulo"); + + if (exponent.isNegative()) + return modInverse(m); + if (exponent.isOne()) + return mod(m); + + // To do this naively by first raising this to the power of exponent + // and then performing modulo m would be extremely expensive, especially + // for very large numbers. The solution is found in Number Theory + // where a combination of partial powers and moduli can be done easily. + // + // We'll use the algorithm for Additive Chaining which can be found on + // p. 244 of "Applied Cryptography, Second Edition" by Bruce Schneier. + BigInteger s = ONE; + BigInteger t = this; + BigInteger u = exponent; + + while (!u.isZero()) + { + if (u.and(ONE).isOne()) + s = times(s, t).mod(m); + u = u.shiftRight(1); + t = times(t, t).mod(m); + } + + return s; + } + + /** Calculate Greatest Common Divisor for non-negative ints. */ + private static int gcd(int a, int b) + { + // Euclid's algorithm, copied from libg++. + int tmp; + if (b > a) + { + tmp = a; a = b; b = tmp; + } + for(;;) + { + if (b == 0) + return a; + if (b == 1) + return b; + tmp = b; + b = a % b; + a = tmp; + } + } + + public BigInteger gcd(BigInteger y) + { + int xval = ival; + int yval = y.ival; + if (words == null) + { + if (xval == 0) + return abs(y); + if (y.words == null + && xval != Integer.MIN_VALUE && yval != Integer.MIN_VALUE) + { + if (xval < 0) + xval = -xval; + if (yval < 0) + yval = -yval; + return valueOf(gcd(xval, yval)); + } + xval = 1; + } + if (y.words == null) + { + if (yval == 0) + return abs(this); + yval = 1; + } + int len = (xval > yval ? xval : yval) + 1; + int[] xwords = new int[len]; + int[] ywords = new int[len]; + getAbsolute(xwords); + y.getAbsolute(ywords); + len = MPN.gcd(xwords, ywords, len); + BigInteger result = new BigInteger(0); + result.ival = len; + result.words = xwords; + return result.canonicalize(); + } + + /** + * <p>Returns <code>true</code> if this BigInteger is probably prime, + * <code>false</code> if it's definitely composite. If <code>certainty</code> + * is <code><= 0</code>, <code>true</code> is returned.</p> + * + * @param certainty a measure of the uncertainty that the caller is willing + * to tolerate: if the call returns <code>true</code> the probability that + * this BigInteger is prime exceeds <code>(1 - 1/2<sup>certainty</sup>)</code>. + * The execution time of this method is proportional to the value of this + * parameter. + * @return <code>true</code> if this BigInteger is probably prime, + * <code>false</code> if it's definitely composite. + */ + public boolean isProbablePrime(int certainty) + { + if (certainty < 1) + return true; + + /** We'll use the Rabin-Miller algorithm for doing a probabilistic + * primality test. It is fast, easy and has faster decreasing odds of a + * composite passing than with other tests. This means that this + * method will actually have a probability much greater than the + * 1 - .5^certainty specified in the JCL (p. 117), but I don't think + * anyone will complain about better performance with greater certainty. + * + * The Rabin-Miller algorithm can be found on pp. 259-261 of "Applied + * Cryptography, Second Edition" by Bruce Schneier. + */ + + // First rule out small prime factors + BigInteger rem = new BigInteger(); + int i; + for (i = 0; i < primes.length; i++) + { + if (words == null && ival == primes[i]) + return true; + + divide(this, smallFixNums[primes[i] - minFixNum], null, rem, TRUNCATE); + if (rem.canonicalize().isZero()) + return false; + } + + // Now perform the Rabin-Miller test. + + // Set b to the number of times 2 evenly divides (this - 1). + // I.e. 2^b is the largest power of 2 that divides (this - 1). + BigInteger pMinus1 = add(this, -1); + int b = pMinus1.getLowestSetBit(); + + // Set m such that this = 1 + 2^b * m. + BigInteger m = pMinus1.divide(valueOf(2L << b - 1)); + + // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note + // 4.49 (controlling the error probability) gives the number of trials + // for an error probability of 1/2**80, given the number of bits in the + // number to test. we shall use these numbers as is if/when 'certainty' + // is less or equal to 80, and twice as much if it's greater. + int bits = this.bitLength(); + for (i = 0; i < k.length; i++) + if (bits <= k[i]) + break; + int trials = t[i]; + if (certainty > 80) + trials *= 2; + BigInteger z; + for (int t = 0; t < trials; t++) + { + // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. + // Remark 4.28 states: "...A strategy that is sometimes employed + // is to fix the bases a to be the first few primes instead of + // choosing them at random. + z = smallFixNums[primes[t] - minFixNum].modPow(m, this); + if (z.isOne() || z.equals(pMinus1)) + continue; // Passes the test; may be prime. + + for (i = 0; i < b; ) + { + if (z.isOne()) + return false; + i++; + if (z.equals(pMinus1)) + break; // Passes the test; may be prime. + + z = z.modPow(valueOf(2), this); + } + + if (i == b && !z.equals(pMinus1)) + return false; + } + return true; + } + + private void setInvert() + { + if (words == null) + ival = ~ival; + else + { + for (int i = ival; --i >= 0; ) + words[i] = ~words[i]; + } + } + + private void setShiftLeft(BigInteger x, int count) + { + int[] xwords; + int xlen; + if (x.words == null) + { + if (count < 32) + { + set((long) x.ival << count); + return; + } + xwords = new int[1]; + xwords[0] = x.ival; + xlen = 1; + } + else + { + xwords = x.words; + xlen = x.ival; + } + int word_count = count >> 5; + count &= 31; + int new_len = xlen + word_count; + if (count == 0) + { + realloc(new_len); + for (int i = xlen; --i >= 0; ) + words[i+word_count] = xwords[i]; + } + else + { + new_len++; + realloc(new_len); + int shift_out = MPN.lshift(words, word_count, xwords, xlen, count); + count = 32 - count; + words[new_len-1] = (shift_out << count) >> count; // sign-extend. + } + ival = new_len; + for (int i = word_count; --i >= 0; ) + words[i] = 0; + } + + private void setShiftRight(BigInteger x, int count) + { + if (x.words == null) + set(count < 32 ? x.ival >> count : x.ival < 0 ? -1 : 0); + else if (count == 0) + set(x); + else + { + boolean neg = x.isNegative(); + int word_count = count >> 5; + count &= 31; + int d_len = x.ival - word_count; + if (d_len <= 0) + set(neg ? -1 : 0); + else + { + if (words == null || words.length < d_len) + realloc(d_len); + MPN.rshift0 (words, x.words, word_count, d_len, count); + ival = d_len; + if (neg) + words[d_len-1] |= -2 << (31 - count); + } + } + } + + private void setShift(BigInteger x, int count) + { + if (count > 0) + setShiftLeft(x, count); + else + setShiftRight(x, -count); + } + + private static BigInteger shift(BigInteger x, int count) + { + if (x.words == null) + { + if (count <= 0) + return valueOf(count > -32 ? x.ival >> (-count) : x.ival < 0 ? -1 : 0); + if (count < 32) + return valueOf((long) x.ival << count); + } + if (count == 0) + return x; + BigInteger result = new BigInteger(0); + result.setShift(x, count); + return result.canonicalize(); + } + + public BigInteger shiftLeft(int n) + { + return shift(this, n); + } + + public BigInteger shiftRight(int n) + { + return shift(this, -n); + } + + private void format(int radix, StringBuffer buffer) + { + if (words == null) + buffer.append(Integer.toString(ival, radix)); + else if (ival <= 2) + buffer.append(Long.toString(longValue(), radix)); + else + { + boolean neg = isNegative(); + int[] work; + if (neg || radix != 16) + { + work = new int[ival]; + getAbsolute(work); + } + else + work = words; + int len = ival; + + if (radix == 16) + { + if (neg) + buffer.append('-'); + int buf_start = buffer.length(); + for (int i = len; --i >= 0; ) + { + int word = work[i]; + for (int j = 8; --j >= 0; ) + { + int hex_digit = (word >> (4 * j)) & 0xF; + // Suppress leading zeros: + if (hex_digit > 0 || buffer.length() > buf_start) + buffer.append(Character.forDigit(hex_digit, 16)); + } + } + } + else + { + int i = buffer.length(); + for (;;) + { + int digit = MPN.divmod_1(work, work, len, radix); + buffer.append(Character.forDigit(digit, radix)); + while (len > 0 && work[len-1] == 0) len--; + if (len == 0) + break; + } + if (neg) + buffer.append('-'); + /* Reverse buffer. */ + int j = buffer.length() - 1; + while (i < j) + { + char tmp = buffer.charAt(i); + buffer.setCharAt(i, buffer.charAt(j)); + buffer.setCharAt(j, tmp); + i++; j--; + } + } + } + } + + public String toString() + { + return toString(10); + } + + public String toString(int radix) + { + if (words == null) + return Integer.toString(ival, radix); + if (ival <= 2) + return Long.toString(longValue(), radix); + int buf_size = ival * (MPN.chars_per_word(radix) + 1); + StringBuffer buffer = new StringBuffer(buf_size); + format(radix, buffer); + return buffer.toString(); + } + + public int intValue() + { + if (words == null) + return ival; + return words[0]; + } + + public long longValue() + { + if (words == null) + return ival; + if (ival == 1) + return words[0]; + return ((long)words[1] << 32) + ((long)words[0] & 0xffffffffL); + } + + public int hashCode() + { + // FIXME: May not match hashcode of JDK. + return words == null ? ival : (words[0] + words[ival - 1]); + } + + /* Assumes x and y are both canonicalized. */ + private static boolean equals(BigInteger x, BigInteger y) + { + if (x.words == null && y.words == null) + return x.ival == y.ival; + if (x.words == null || y.words == null || x.ival != y.ival) + return false; + for (int i = x.ival; --i >= 0; ) + { + if (x.words[i] != y.words[i]) + return false; + } + return true; + } + + /* Assumes this and obj are both canonicalized. */ + public boolean equals(Object obj) + { + if (! (obj instanceof BigInteger)) + return false; + return equals(this, (BigInteger) obj); + } + + private static BigInteger valueOf(String s, int radix) + throws NumberFormatException + { + int len = s.length(); + // Testing (len < MPN.chars_per_word(radix)) would be more accurate, + // but slightly more expensive, for little practical gain. + if (len <= 15 && radix <= 16) + return valueOf(Long.parseLong(s, radix)); + + int byte_len = 0; + byte[] bytes = new byte[len]; + boolean negative = false; + for (int i = 0; i < len; i++) + { + char ch = s.charAt(i); + if (ch == '-') + negative = true; + else if (ch == '_' || (byte_len == 0 && (ch == ' ' || ch == '\t'))) + continue; + else + { + int digit = Character.digit(ch, radix); + if (digit < 0) + break; + bytes[byte_len++] = (byte) digit; + } + } + return valueOf(bytes, byte_len, negative, radix); + } + + private static BigInteger valueOf(byte[] digits, int byte_len, + boolean negative, int radix) + { + int chars_per_word = MPN.chars_per_word(radix); + int[] words = new int[byte_len / chars_per_word + 1]; + int size = MPN.set_str(words, digits, byte_len, radix); + if (size == 0) + return ZERO; + if (words[size-1] < 0) + words[size++] = 0; + if (negative) + negate(words, words, size); + return make(words, size); + } + + public double doubleValue() + { + if (words == null) + return (double) ival; + if (ival <= 2) + return (double) longValue(); + if (isNegative()) + return neg(this).roundToDouble(0, true, false); + return roundToDouble(0, false, false); + } + + public float floatValue() + { + return (float) doubleValue(); + } + + /** Return true if any of the lowest n bits are one. + * (false if n is negative). */ + private boolean checkBits(int n) + { + if (n <= 0) + return false; + if (words == null) + return n > 31 || ((ival & ((1 << n) - 1)) != 0); + int i; + for (i = 0; i < (n >> 5) ; i++) + if (words[i] != 0) + return true; + return (n & 31) != 0 && (words[i] & ((1 << (n & 31)) - 1)) != 0; + } + + /** Convert a semi-processed BigInteger to double. + * Number must be non-negative. Multiplies by a power of two, applies sign, + * and converts to double, with the usual java rounding. + * @param exp power of two, positive or negative, by which to multiply + * @param neg true if negative + * @param remainder true if the BigInteger is the result of a truncating + * division that had non-zero remainder. To ensure proper rounding in + * this case, the BigInteger must have at least 54 bits. */ + private double roundToDouble(int exp, boolean neg, boolean remainder) + { + // Compute length. + int il = bitLength(); + + // Exponent when normalized to have decimal point directly after + // leading one. This is stored excess 1023 in the exponent bit field. + exp += il - 1; + + // Gross underflow. If exp == -1075, we let the rounding + // computation determine whether it is minval or 0 (which are just + // 0x0000 0000 0000 0001 and 0x0000 0000 0000 0000 as bit + // patterns). + if (exp < -1075) + return neg ? -0.0 : 0.0; + + // gross overflow + if (exp > 1023) + return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + + // number of bits in mantissa, including the leading one. + // 53 unless it's denormalized + int ml = (exp >= -1022 ? 53 : 53 + exp + 1022); + + // Get top ml + 1 bits. The extra one is for rounding. + long m; + int excess_bits = il - (ml + 1); + if (excess_bits > 0) + m = ((words == null) ? ival >> excess_bits + : MPN.rshift_long(words, ival, excess_bits)); + else + m = longValue() << (- excess_bits); + + // Special rounding for maxval. If the number exceeds maxval by + // any amount, even if it's less than half a step, it overflows. + if (exp == 1023 && ((m >> 1) == (1L << 53) - 1)) + { + if (remainder || checkBits(il - ml)) + return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + else + return neg ? - Double.MAX_VALUE : Double.MAX_VALUE; + } + + // Normal round-to-even rule: round up if the bit dropped is a one, and + // the bit above it or any of the bits below it is a one. + if ((m & 1) == 1 + && ((m & 2) == 2 || remainder || checkBits(excess_bits))) + { + m += 2; + // Check if we overflowed the mantissa + if ((m & (1L << 54)) != 0) + { + exp++; + // renormalize + m >>= 1; + } + // Check if a denormalized mantissa was just rounded up to a + // normalized one. + else if (ml == 52 && (m & (1L << 53)) != 0) + exp++; + } + + // Discard the rounding bit + m >>= 1; + + long bits_sign = neg ? (1L << 63) : 0; + exp += 1023; + long bits_exp = (exp <= 0) ? 0 : ((long)exp) << 52; + long bits_mant = m & ~(1L << 52); + return Double.longBitsToDouble(bits_sign | bits_exp | bits_mant); + } + + /** Copy the abolute value of this into an array of words. + * Assumes words.length >= (this.words == null ? 1 : this.ival). + * Result is zero-extended, but need not be a valid 2's complement number. + */ + private void getAbsolute(int[] words) + { + int len; + if (this.words == null) + { + len = 1; + words[0] = this.ival; + } + else + { + len = this.ival; + for (int i = len; --i >= 0; ) + words[i] = this.words[i]; + } + if (words[len - 1] < 0) + negate(words, words, len); + for (int i = words.length; --i > len; ) + words[i] = 0; + } + + /** Set dest[0:len-1] to the negation of src[0:len-1]. + * Return true if overflow (i.e. if src is -2**(32*len-1)). + * Ok for src==dest. */ + private static boolean negate(int[] dest, int[] src, int len) + { + long carry = 1; + boolean negative = src[len-1] < 0; + for (int i = 0; i < len; i++) + { + carry += ((long) (~src[i]) & 0xffffffffL); + dest[i] = (int) carry; + carry >>= 32; + } + return (negative && dest[len-1] < 0); + } + + /** Destructively set this to the negative of x. + * It is OK if x==this.*/ + private void setNegative(BigInteger x) + { + int len = x.ival; + if (x.words == null) + { + if (len == Integer.MIN_VALUE) + set(- (long) len); + else + set(-len); + return; + } + realloc(len + 1); + if (negate(words, x.words, len)) + words[len++] = 0; + ival = len; + } + + /** Destructively negate this. */ + private void setNegative() + { + setNegative(this); + } + + private static BigInteger abs(BigInteger x) + { + return x.isNegative() ? neg(x) : x; + } + + public BigInteger abs() + { + return abs(this); + } + + private static BigInteger neg(BigInteger x) + { + if (x.words == null && x.ival != Integer.MIN_VALUE) + return valueOf(- x.ival); + BigInteger result = new BigInteger(0); + result.setNegative(x); + return result.canonicalize(); + } + + public BigInteger negate() + { + return neg(this); + } + + /** Calculates ceiling(log2(this < 0 ? -this : this+1)) + * See Common Lisp: the Language, 2nd ed, p. 361. + */ + public int bitLength() + { + if (words == null) + return MPN.intLength(ival); + return MPN.intLength(words, ival); + } + + public byte[] toByteArray() + { + // Determine number of bytes needed. The method bitlength returns + // the size without the sign bit, so add one bit for that and then + // add 7 more to emulate the ceil function using integer math. + byte[] bytes = new byte[(bitLength() + 1 + 7) / 8]; + int nbytes = bytes.length; + + int wptr = 0; + int word; + + // Deal with words array until one word or less is left to process. + // If BigInteger is an int, then it is in ival and nbytes will be <= 4. + while (nbytes > 4) + { + word = words[wptr++]; + for (int i = 4; i > 0; --i, word >>= 8) + bytes[--nbytes] = (byte) word; + } + + // Deal with the last few bytes. If BigInteger is an int, use ival. + word = (words == null) ? ival : words[wptr]; + for ( ; nbytes > 0; word >>= 8) + bytes[--nbytes] = (byte) word; + + return bytes; + } + + /** Return the boolean opcode (for bitOp) for swapped operands. + * I.e. bitOp(swappedOp(op), x, y) == bitOp(op, y, x). + */ + private static int swappedOp(int op) + { + return + "\000\001\004\005\002\003\006\007\010\011\014\015\012\013\016\017" + .charAt(op); + } + + /** Do one the the 16 possible bit-wise operations of two BigIntegers. */ + private static BigInteger bitOp(int op, BigInteger x, BigInteger y) + { + switch (op) + { + case 0: return ZERO; + case 1: return x.and(y); + case 3: return x; + case 5: return y; + case 15: return valueOf(-1); + } + BigInteger result = new BigInteger(); + setBitOp(result, op, x, y); + return result.canonicalize(); + } + + /** Do one the the 16 possible bit-wise operations of two BigIntegers. */ + private static void setBitOp(BigInteger result, int op, + BigInteger x, BigInteger y) + { + if (y.words == null) ; + else if (x.words == null || x.ival < y.ival) + { + BigInteger temp = x; x = y; y = temp; + op = swappedOp(op); + } + int xi; + int yi; + int xlen, ylen; + if (y.words == null) + { + yi = y.ival; + ylen = 1; + } + else + { + yi = y.words[0]; + ylen = y.ival; + } + if (x.words == null) + { + xi = x.ival; + xlen = 1; + } + else + { + xi = x.words[0]; + xlen = x.ival; + } + if (xlen > 1) + result.realloc(xlen); + int[] w = result.words; + int i = 0; + // Code for how to handle the remainder of x. + // 0: Truncate to length of y. + // 1: Copy rest of x. + // 2: Invert rest of x. + int finish = 0; + int ni; + switch (op) + { + case 0: // clr + ni = 0; + break; + case 1: // and + for (;;) + { + ni = xi & yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 1; + break; + case 2: // andc2 + for (;;) + { + ni = xi & ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 1; + break; + case 3: // copy x + ni = xi; + finish = 1; // Copy rest + break; + case 4: // andc1 + for (;;) + { + ni = ~xi & yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 2; + break; + case 5: // copy y + for (;;) + { + ni = yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + break; + case 6: // xor + for (;;) + { + ni = xi ^ yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + finish = yi < 0 ? 2 : 1; + break; + case 7: // ior + for (;;) + { + ni = xi | yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 1; + break; + case 8: // nor + for (;;) + { + ni = ~(xi | yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 2; + break; + case 9: // eqv [exclusive nor] + for (;;) + { + ni = ~(xi ^ yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + finish = yi >= 0 ? 2 : 1; + break; + case 10: // c2 + for (;;) + { + ni = ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + break; + case 11: // orc2 + for (;;) + { + ni = xi | ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 1; + break; + case 12: // c1 + ni = ~xi; + finish = 2; + break; + case 13: // orc1 + for (;;) + { + ni = ~xi | yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 2; + break; + case 14: // nand + for (;;) + { + ni = ~(xi & yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 2; + break; + default: + case 15: // set + ni = -1; + break; + } + // Here i==ylen-1; w[0]..w[i-1] have the correct result; + // and ni contains the correct result for w[i+1]. + if (i+1 == xlen) + finish = 0; + switch (finish) + { + case 0: + if (i == 0 && w == null) + { + result.ival = ni; + return; + } + w[i++] = ni; + break; + case 1: w[i] = ni; while (++i < xlen) w[i] = x.words[i]; break; + case 2: w[i] = ni; while (++i < xlen) w[i] = ~x.words[i]; break; + } + result.ival = i; + } + + /** Return the logical (bit-wise) "and" of a BigInteger and an int. */ + private static BigInteger and(BigInteger x, int y) + { + if (x.words == null) + return valueOf(x.ival & y); + if (y >= 0) + return valueOf(x.words[0] & y); + int len = x.ival; + int[] words = new int[len]; + words[0] = x.words[0] & y; + while (--len > 0) + words[len] = x.words[len]; + return make(words, x.ival); + } + + /** Return the logical (bit-wise) "and" of two BigIntegers. */ + public BigInteger and(BigInteger y) + { + if (y.words == null) + return and(this, y.ival); + else if (words == null) + return and(y, ival); + + BigInteger x = this; + if (ival < y.ival) + { + BigInteger temp = this; x = y; y = temp; + } + int i; + int len = y.isNegative() ? x.ival : y.ival; + int[] words = new int[len]; + for (i = 0; i < y.ival; i++) + words[i] = x.words[i] & y.words[i]; + for ( ; i < len; i++) + words[i] = x.words[i]; + return make(words, len); + } + + /** Return the logical (bit-wise) "(inclusive) or" of two BigIntegers. */ + public BigInteger or(BigInteger y) + { + return bitOp(7, this, y); + } + + /** Return the logical (bit-wise) "exclusive or" of two BigIntegers. */ + public BigInteger xor(BigInteger y) + { + return bitOp(6, this, y); + } + + /** Return the logical (bit-wise) negation of a BigInteger. */ + public BigInteger not() + { + return bitOp(12, this, ZERO); + } + + public BigInteger andNot(BigInteger val) + { + return and(val.not()); + } + + public BigInteger clearBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + return and(ONE.shiftLeft(n).not()); + } + + public BigInteger setBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + return or(ONE.shiftLeft(n)); + } + + public boolean testBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + return !and(ONE.shiftLeft(n)).isZero(); + } + + public BigInteger flipBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + return xor(ONE.shiftLeft(n)); + } + + public int getLowestSetBit() + { + if (isZero()) + return -1; + + if (words == null) + return MPN.findLowestBit(ival); + else + return MPN.findLowestBit(words); + } + + // bit4count[I] is number of '1' bits in I. + private static final byte[] bit4_count = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4}; + + private static int bitCount(int i) + { + int count = 0; + while (i != 0) + { + count += bit4_count[i & 15]; + i >>>= 4; + } + return count; + } + + private static int bitCount(int[] x, int len) + { + int count = 0; + while (--len >= 0) + count += bitCount(x[len]); + return count; + } + + /** Count one bits in a BigInteger. + * If argument is negative, count zero bits instead. */ + public int bitCount() + { + int i, x_len; + int[] x_words = words; + if (x_words == null) + { + x_len = 1; + i = bitCount(ival); + } + else + { + x_len = ival; + i = bitCount(x_words, x_len); + } + return isNegative() ? x_len * 32 - i : i; + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + words = byteArrayToIntArray(magnitude, signum < 0 ? -1 : 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + } + + private void writeObject(ObjectOutputStream s) + throws IOException, ClassNotFoundException + { + signum = signum(); + magnitude = toByteArray(); + s.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/math/class-dependencies.conf b/libjava/classpath/java/math/class-dependencies.conf new file mode 100644 index 00000000000..4fbf75eb1ce --- /dev/null +++ b/libjava/classpath/java/math/class-dependencies.conf @@ -0,0 +1,58 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +# end of file diff --git a/libjava/classpath/java/math/package.html b/libjava/classpath/java/math/package.html new file mode 100644 index 00000000000..62d12ea1683 --- /dev/null +++ b/libjava/classpath/java/math/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.math package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.math</title></head> + +<body> +<p>Arbitrary large decimals and integers.</p> + +</body> +</html> diff --git a/libjava/classpath/java/net/Authenticator.java b/libjava/classpath/java/net/Authenticator.java new file mode 100644 index 00000000000..229e140cc76 --- /dev/null +++ b/libjava/classpath/java/net/Authenticator.java @@ -0,0 +1,313 @@ +/* Authenticator.java -- Abstract class for obtaining authentication info + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This abstract class provides a model for obtaining authentication + * information (in the form of a username and password) required by + * some network operations (such as hitting a password protected + * web site). + * <p> + * To make use of this feature, a programmer must create a subclass + * that knows how to obtain the necessary info. An example + * would be a class that popped up a dialog box to prompt the user. + * After creating an instance of that subclass, the static + * <code>setDefault</code> method of this class is called to set up + * that instance as the object to use on subsequent calls to obtain + * authorization. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status Believed to be JDK 1.4 complete + */ +public abstract class Authenticator +{ + /* + * Class Variables + */ + + /** + * This is the default Authenticator object to use for password requests + */ + private static Authenticator defaultAuthenticator; + + /* + * Instance Variables + */ + + /** + * The hostname of the site requesting authentication + */ + private String host; + + /** + * InternetAddress of the site requesting authentication + */ + private InetAddress addr; + + /** + * The port number of the site requesting authentication + */ + private int port; + + /** + * The protocol name of the site requesting authentication + */ + private String protocol; + + /** + * The prompt to display to the user when requesting authentication info + */ + private String prompt; + + /** + * The authentication scheme in use + */ + private String scheme; + + /* + * Class Methods + */ + + /** + * This method sets the default <code>Authenticator</code> object (an + * instance of a subclass of <code>Authenticator</code>) to use when + * prompting the user for + * information. Note that this method checks to see if the caller is + * allowed to set this value (the "setDefaultAuthenticator" permission) + * and throws a <code>SecurityException</code> if it is not. + * + * @param defAuth The new default <code>Authenticator</code> object to use + * + * @exception SecurityException If the caller does not have permission + * to perform this operation + */ + public static void setDefault(Authenticator defAuth) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("setDefaultAuthenticator")); + + defaultAuthenticator = defAuth; + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default <code>Authenticator</code> object, the method then returns + * <code>null</code>. Otherwise, the default authenticators's instance + * variables are initialized and it's <code>getPasswordAuthentication</code> + * method is called to get the actual authentication information to return. + * + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A <code>PasswordAuthentication</code> object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + */ + public static PasswordAuthentication requestPasswordAuthentication(InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + return requestPasswordAuthentication(null, addr, port, protocol, prompt, + scheme); + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default <code>Authenticator</code> object, the method then returns + * <code>null</code>. Otherwise, the default authenticators's instance + * variables are initialized and it's <code>getPasswordAuthentication</code> + * method is called to get the actual authentication information to return. + * This method is the preferred one as it can be used with hostname + * when addr is unknown. + * + * @param host The hostname requesting authentication + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A <code>PasswordAuthentication</code> object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + * + * @since 1.4 + */ + public static PasswordAuthentication requestPasswordAuthentication(String host, + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("requestPasswordAuthentication")); + + if (defaultAuthenticator == null) + return null; + + defaultAuthenticator.host = host; + defaultAuthenticator.addr = addr; + defaultAuthenticator.port = port; + defaultAuthenticator.protocol = protocol; + defaultAuthenticator.prompt = prompt; + defaultAuthenticator.scheme = scheme; + + return defaultAuthenticator.getPasswordAuthentication(); + } + + /* + * Constructors + */ + + /** + * Default, no-argument constructor for subclasses to call. + */ + public Authenticator() + { + } + + /* + * Instance Methods + */ + + /** + * This method returns the address of the site that is requesting + * authentication. + * + * @return The requesting site's address + */ + protected final InetAddress getRequestingSite() + { + return addr; + } + + /** + * Returns the hostname of the host or proxy requesting authorization, + * or <code>null</code> if not available. + * + * @return The name of the host requesting authentication, or + * <code>null</code> if it is not available. + * + * @since 1.4 + */ + protected final String getRequestingHost() + { + return host; + } + + /** + * This method returns the port of the site that is requesting + * authentication. + * + * @return The requesting port + */ + protected final int getRequestingPort() + { + return port; + } + + /** + * This method returns the requesting protocol of the operation that is + * requesting authentication + * + * @return The requesting protocol + */ + protected final String getRequestingProtocol() + { + return protocol; + } + + /** + * Returns the prompt that should be used when requesting authentication + * information from the user + * + * @return The user prompt + */ + protected final String getRequestingPrompt() + { + return prompt; + } + + /** + * This method returns the authentication scheme in use + * + * @return The authentication scheme + */ + protected final String getRequestingScheme() + { + return scheme; + } + + /** + * This method is called whenever a request for authentication is made. It + * can call the other getXXX methods to determine the information relevant + * to this request. Subclasses should override this method, which returns + * <code>null</code> by default. + * + * @return The <code>PasswordAuthentication</code> information + */ + protected PasswordAuthentication getPasswordAuthentication() + { + return null; + } +} // class Authenticator diff --git a/libjava/classpath/java/net/BindException.java b/libjava/classpath/java/net/BindException.java new file mode 100644 index 00000000000..cfb509a708e --- /dev/null +++ b/libjava/classpath/java/net/BindException.java @@ -0,0 +1,74 @@ +/* BindException.java -- An exception occurred while binding to a socket + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to bind + * socket to a particular port. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class BindException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5945005768251722951L; + + /** + * Create a new instance without a descriptive error message. + */ + public BindException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public BindException(String message) + { + super(message); + } +} // class BindException diff --git a/libjava/classpath/java/net/ConnectException.java b/libjava/classpath/java/net/ConnectException.java new file mode 100644 index 00000000000..c115d2fe04e --- /dev/null +++ b/libjava/classpath/java/net/ConnectException.java @@ -0,0 +1,75 @@ +/* ConnectException.java -- An exception occurred while connecting to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to + * connect to a remote host. Often this indicates that the remote host + * refused the connection (ie, is not listening on the target socket). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3831404271622369215L; + + /** + * Create a new instance without a descriptive error message. + */ + public ConnectException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public ConnectException(String message) + { + super(message); + } +} // class ConnectException diff --git a/libjava/classpath/java/net/ContentHandler.java b/libjava/classpath/java/net/ContentHandler.java new file mode 100644 index 00000000000..7f63e740229 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandler.java @@ -0,0 +1,126 @@ +/* ContentHandler.java -- Abstract class for handling content from URL's + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This is an abstract class that is the superclass for classes that read + * objects from URL's. Calling the <code>getContent()</code> method in the + * <code>URL</code> class or the <code>URLConnection</code> class will cause + * an instance of a subclass of <code>ContentHandler</code> to be created for + * the MIME type of the object being downloaded from the URL. Thus, this + * class is seldom needed by applications/applets directly, but only + * indirectly through methods in other classes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class ContentHandler +{ + /* + * Constructors + */ + + /** + * Default, no-argument constructor. + */ + public ContentHandler() + { + } + + /* + * Instance Methods + */ + + /** + * This method reads from the <code>InputStream</code> of the passed in URL + * connection and uses the data downloaded to create an <code>Object</code> + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an <code>Image</code> object. This method + * must be implemented by subclasses. + * + * @param urlc A <code>URLConnection</code> object to read data from. + * + * @return An object representing the data read + * + * @exception IOException If an error occurs + */ + public abstract Object getContent(URLConnection urlc) + throws IOException; + + /** + * This method reads from the <code>InputStream</code> of the passed in URL + * connection and uses the data downloaded to create an <code>Object</code> + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an <code>Image</code> object. This method + * must be implemented by subclasses. This method uses the list of + * supplied classes as candidate types. If the data read doesn't match + * any of the supplied type, <code>null</code> is returned. + * + * @param urlc A <code>URLConnection</code> object to read data from. + * @param classes An array of types of objects that are candidate types + * for the data to be read. + * + * @return An object representing the data read, or <code>null</code> + * if the data does not match any of the candidate types. + * + * @exception IOException If an error occurs + * + * @since 1.3 + */ + public Object getContent(URLConnection urlc, Class[] classes) + throws IOException + { + Object obj = getContent(urlc); + + for (int i = 0; i < classes.length; i++) + { + if (classes[i].isInstance(obj)) + return obj; + } + + return null; + } +} // class ContentHandler diff --git a/libjava/classpath/java/net/ContentHandlerFactory.java b/libjava/classpath/java/net/ContentHandlerFactory.java new file mode 100644 index 00000000000..51a92cf1521 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandlerFactory.java @@ -0,0 +1,65 @@ +/* ContentHandlerFactory.java -- Interface for creating content handlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface maps MIME types to <code>ContentHandler</code> objects. + * It consists of one method that, when passed a MIME type, returns a + * handler for that type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface ContentHandlerFactory +{ + /** + * This method is passed a MIME type as a string and is responsible for + * returning the appropriate <code>ContentHandler</code> object. + * + * @param mimeType The MIME type to map to a <code>ContentHandler</code> + * + * @return The <code>ContentHandler</code> for the passed in MIME type + */ + ContentHandler createContentHandler(String mimeType); +} // interface ContentHandlerFactory diff --git a/libjava/classpath/java/net/DatagramPacket.java b/libjava/classpath/java/net/DatagramPacket.java new file mode 100644 index 00000000000..e642f889c32 --- /dev/null +++ b/libjava/classpath/java/net/DatagramPacket.java @@ -0,0 +1,391 @@ +/* DatagramPacket.java -- Class to model a packet to be sent via UDP + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class models a packet of data that is to be sent across the network + * using a connectionless protocol such as UDP. It contains the data + * to be send, as well as the destination address and port. Note that + * datagram packets can arrive in any order and are not guaranteed to be + * delivered at all. + * <p> + * This class can also be used for receiving data from the network. + * <p> + * Note that for all method below where the buffer length passed by the + * caller cannot exceed the actually length of the byte array passed as + * the buffer, if this condition is not true, then the method silently + * reduces the length value to maximum allowable value. + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aarom M. Renn (arenn@urbanophile.com) (Documentation comments) + * @date April 28, 1999. + */ +public final class DatagramPacket +{ + /** + * The data buffer to send + */ + private byte[] buffer; + + /** + * This is the offset into the buffer to start sending from or receiving to. + */ + private int offset; + + /** + * The length of the data buffer to send. + */ + int length; + + /** + * The maximal length of the buffer. + */ + int maxlen; + + /** + * The address to which the packet should be sent or from which it + * was received. + */ + private InetAddress address; + + /** + * The port to which the packet should be sent or from which it was + * was received. + */ + private int port; + + /** + * This method initializes a new instance of <code>DatagramPacket</code> + * which has the specified buffer, offset, and length. + * + * @param buf The buffer for holding the incoming datagram. + * @param offset The offset into the buffer to start writing. + * @param length The maximum number of bytes to read. + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length) + { + setData(buf, offset, length); + address = null; + port = -1; + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * receiving packets from the network. + * + * @param buf A buffer for storing the returned packet data + * @param length The length of the buffer (must be <= buf.length) + */ + public DatagramPacket(byte[] buf, int length) + { + this(buf, 0, length); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length, + InetAddress address, int port) + { + setData(buf, offset, length); + setAddress(address); + setPort(port); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + */ + public DatagramPacket(byte[] buf, int length, InetAddress address, int port) + { + this(buf, 0, length, address, port); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int offset, int length, + SocketAddress address) throws SocketException + { + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + setData(buf, offset, length); + setAddress(tmp.getAddress()); + setPort(tmp.getPort()); + } + + /** + * Initializes a new instance of <code>DatagramPacket</code> for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int length, SocketAddress address) + throws SocketException + { + this(buf, 0, length, address); + } + + /** + * Returns the address that this packet is being sent to or, if it was used + * to receive a packet, the address that is was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * returns <code>null</code>. + * + * @return The address for this packet. + */ + public synchronized InetAddress getAddress() + { + return address; + } + + /** + * Returns the port number this packet is being sent to or, if it was used + * to receive a packet, the port that it was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * will return 0. + * + * @return The port number for this packet + */ + public synchronized int getPort() + { + return port; + } + + /** + * Returns the data buffer for this packet + * + * @return This packet's data buffer + */ + public synchronized byte[] getData() + { + return buffer; + } + + /** + * This method returns the current offset value into the data buffer + * where data will be sent from. + * + * @return The buffer offset. + * + * @since 1.2 + */ + public synchronized int getOffset() + { + return offset; + } + + /** + * Returns the length of the data in the buffer + * + * @return The length of the data + */ + public synchronized int getLength() + { + return length; + } + + /** + * This sets the address to which the data packet will be transmitted. + * + * @param address The destination address + * + * @since 1.1 + */ + public synchronized void setAddress(InetAddress address) + { + this.address = address; + } + + /** + * This sets the port to which the data packet will be transmitted. + * + * @param port The destination port + * + * @since 1.1 + */ + public synchronized void setPort(int port) + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + this.port = port; + } + + /** + * Sets the address of the remote host this package will be sent + * + * @param address The socket address of the remove host + * + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public void setSocketAddress(SocketAddress address) + throws IllegalArgumentException + { + if (address == null) + throw new IllegalArgumentException("address may not be null"); + + InetSocketAddress tmp = (InetSocketAddress) address; + this.address = tmp.getAddress(); + this.port = tmp.getPort(); + } + + /** + * Gets the socket address of the host this packet + * will be sent to/is coming from + * + * @return The socket address of the remote host + * + * @since 1.4 + */ + public SocketAddress getSocketAddress() + { + return new InetSocketAddress(address, port); + } + + /** + * Sets the data buffer for this packet. + * + * @param buf The new buffer for this packet + * + * @exception NullPointerException If the argument is null + * + * @since 1.1 + */ + public void setData(byte[] buf) + { + setData(buf, 0, buf.length); + } + + /** + * This method sets the data buffer for the packet. + * + * @param buf The byte array containing the data for this packet. + * @param offset The offset into the buffer to start reading data from. + * @param length The number of bytes of data in the buffer. + * + * @exception NullPointerException If the argument is null + * + * @since 1.2 + */ + public synchronized void setData(byte[] buf, int offset, int length) + { + // This form of setData must be used if offset is to be changed. + if (buf == null) + throw new NullPointerException("Null buffer"); + if (offset < 0) + throw new IllegalArgumentException("Invalid offset: " + offset); + + buffer = buf; + this.offset = offset; + setLength(length); + } + + /** + * Sets the length of the data in the buffer. + * + * @param length The new length. (Where len <= buf.length) + * + * @exception IllegalArgumentException If the length is negative or + * if the length is greater than the packet's data buffer length + * + * @since 1.1 + */ + public synchronized void setLength(int length) + { + if (length < 0) + throw new IllegalArgumentException("Invalid length: " + length); + if (offset + length > buffer.length) + throw new IllegalArgumentException("Potential buffer overflow - offset: " + + offset + " length: " + length); + + this.length = length; + this.maxlen = length; + } +} diff --git a/libjava/classpath/java/net/DatagramSocket.java b/libjava/classpath/java/net/DatagramSocket.java new file mode 100644 index 00000000000..8596b7f807f --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocket.java @@ -0,0 +1,933 @@ +/* DatagramSocket.java -- A class to model UDP sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainDatagramSocketImpl; +import gnu.java.nio.DatagramChannelImpl; + +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.IllegalBlockingModeException; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a connectionless datagram socket that sends + * individual packets of data across the network. In the TCP/IP world, + * this means UDP. Datagram packets do not have guaranteed delivery, + * or any guarantee about the order the data will be received on the + * remote host. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date May 3, 1999. + */ +public class DatagramSocket +{ + /** + * This is the user DatagramSocketImplFactory for this class. If this + * variable is null, a default factory is used. + */ + private static DatagramSocketImplFactory factory; + + /** + * This is the implementation object used by this socket. + */ + private DatagramSocketImpl impl; + + /** + * True if socket implementation was created. + */ + private boolean implCreated; + + /** + * This is the address we are "connected" to + */ + private InetAddress remoteAddress; + + /** + * This is the port we are "connected" to + */ + private int remotePort = -1; + + /** + * True if socket is bound. + */ + private boolean bound; + + /** + * Creates a <code>DatagramSocket</code> from a specified + * <code>DatagramSocketImpl</code> instance + * + * @param impl The <code>DatagramSocketImpl</code> the socket will be + * created from + * + * @since 1.4 + */ + protected DatagramSocket(DatagramSocketImpl impl) + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.remoteAddress = null; + this.remotePort = -1; + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * a random port and every address on the local machine. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its <code>checkListen</code> method doesn't allow the operation. + */ + public DatagramSocket() throws SocketException + { + this(new InetSocketAddress(0)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified port and every address on the local machine. + * + * @param port The local port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * <code>checkListen</code> method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port) throws SocketException + { + this(new InetSocketAddress(port)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified local port and address. + * + * @param port The local port number to bind to. + * @param addr The local address to bind to. + * + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port, InetAddress addr) throws SocketException + { + this(new InetSocketAddress(addr, port)); + } + + /** + * Initializes a new instance of <code>DatagramSocket</code> that binds to + * the specified local port and address. + * + * @param address The local address and port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * <code>checkListen</code> method doesn't allow the operation. + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public DatagramSocket(SocketAddress address) throws SocketException + { + String propVal = System.getProperty("impl.prefix"); + if (propVal == null || propVal.equals("")) + impl = new PlainDatagramSocketImpl(); + else + try + { + impl = + (DatagramSocketImpl) Class.forName("java.net." + propVal + + "DatagramSocketImpl") + .newInstance(); + } + catch (Exception e) + { + System.err.println("Could not instantiate class: java.net." + + propVal + "DatagramSocketImpl"); + impl = new PlainDatagramSocketImpl(); + } + + if (address != null) + bind(address); + } + + // This needs to be accessible from java.net.MulticastSocket + DatagramSocketImpl getImpl() throws SocketException + { + try + { + if (! implCreated) + { + impl.create(); + implCreated = true; + } + + return impl; + } + catch (IOException e) + { + SocketException se = new SocketException(); + se.initCause(e); + throw se; + } + } + + /** + * Closes this datagram socket. + */ + public void close() + { + if (isClosed()) + return; + + try + { + getImpl().close(); + } + catch (SocketException e) + { + // Ignore this case, just close the socket in finally clause. + } + finally + { + remoteAddress = null; + remotePort = -1; + impl = null; + } + + try + { + if (getChannel() != null) + getChannel().close(); + } + catch (IOException e) + { + // Do nothing. + } + } + + /** + * This method returns the remote address to which this socket is + * connected. If this socket is not connected, then this method will + * return <code>null</code>. + * + * @return The remote address. + * + * @since 1.2 + */ + public InetAddress getInetAddress() + { + return remoteAddress; + } + + /** + * This method returns the remote port to which this socket is + * connected. If this socket is not connected, then this method will + * return -1. + * + * @return The remote port. + * + * @since 1.2 + */ + public int getPort() + { + return remotePort; + } + + /** + * Returns the local address this datagram socket is bound to. + * + * @return The local address is the socket is bound or null + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return null; + + InetAddress localAddr; + + try + { + localAddr = + (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkConnect(localAddr.getHostName(), -1); + } + catch (SecurityException e) + { + localAddr = InetAddress.ANY_IF; + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + + return localAddr; + } + + /** + * Returns the local port this socket is bound to. + * + * @return The local port number. + */ + public int getLocalPort() + { + if (isClosed()) + return -1; + + try + { + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return 0; + } + } + + /** + * Returns the value of the socket's SO_TIMEOUT setting. If this method + * returns 0 then SO_TIMEOUT is disabled. + * + * @return The current timeout in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_TIMEOUT); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the value of the socket's SO_TIMEOUT value. A value of 0 will + * disable SO_TIMEOUT. Any other value is the number of milliseconds + * a socket read/write will block before timing out. + * + * @param timeout The new SO_TIMEOUT value in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("Invalid timeout: " + timeout); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * This method connects this socket to the specified address and port. + * When a datagram socket is connected, it will only send or receive + * packets to and from the host to which it is connected. A multicast + * socket that is connected may only send and not receive packets. + * + * @param address The address to connect this socket to. + * @param port The port to connect this socket to. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If address or port are invalid. + * @exception SecurityException If the caller is not allowed to send + * datagrams to or receive from this address and port. + * + * @since 1.2 + */ + public void connect(InetAddress address, int port) + { + if (address == null) + throw new IllegalArgumentException("Connect address may not be null"); + + if ((port < 1) || (port > 65535)) + throw new IllegalArgumentException("Port number is illegal: " + port); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(address.getHostName(), port); + + try + { + getImpl().connect(address, port); + remoteAddress = address; + remotePort = port; + } + catch (SocketException e) + { + // This means simply not connected or connect not implemented. + } + } + + /** + * This method disconnects this socket from the address/port it was + * connected to. If the socket was not connected in the first place, + * this method does nothing. + * + * @since 1.2 + */ + public void disconnect() + { + if (! isConnected()) + return; + + try + { + getImpl().disconnect(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + finally + { + remoteAddress = null; + remotePort = -1; + } + } + + /** + * Reads a datagram packet from the socket. Note that this method + * will block until a packet is received from the network. On return, + * the passed in <code>DatagramPacket</code> is populated with the data + * received and all the other information about the packet. + * + * @param p A <code>DatagramPacket</code> for storing the data + * + * @exception IOException If an error occurs. + * @exception SocketTimeoutException If setSoTimeout was previously called + * and the timeout has expired. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + * @exception SecurityException If a security manager exists and its + * checkAccept method doesn't allow the receive. + */ + public synchronized void receive(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (remoteAddress != null && remoteAddress.isMulticastAddress()) + throw new IOException + ("Socket connected to a multicast address my not receive"); + + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + getImpl().receive(p); + + SecurityManager s = System.getSecurityManager(); + if (s != null && isConnected()) + s.checkAccept(p.getAddress().getHostName(), p.getPort()); + } + + /** + * Sends the specified packet. The host and port to which the packet + * are to be sent should be set inside the packet. + * + * @param p The datagram packet to send. + * + * @exception IOException If an error occurs. + * @exception SecurityException If a security manager exists and its + * checkMulticast or checkConnect method doesn't allow the send. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + */ + public void send(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // JDK1.2: Don't do security checks if socket is connected; see jdk1.2 api. + SecurityManager s = System.getSecurityManager(); + if (s != null && ! isConnected()) + { + InetAddress addr = p.getAddress(); + if (addr.isMulticastAddress()) + s.checkMulticast(addr); + else + s.checkConnect(addr.getHostAddress(), p.getPort()); + } + + if (isConnected()) + { + if (p.getAddress() != null + && (remoteAddress != p.getAddress() || remotePort != p.getPort())) + throw new IllegalArgumentException + ("DatagramPacket address does not match remote address"); + } + + // FIXME: if this is a subclass of MulticastSocket, + // use getTimeToLive for TTL val. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + getImpl().send(p); + } + + /** + * Binds the socket to the given socket address. + * + * @param address The socket address to bind to. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its checkListen method doesn't allow the operation. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void bind(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetAddress addr = ((InetSocketAddress) address).getAddress(); + int port = ((InetSocketAddress) address).getPort(); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + try + { + getImpl().bind(port, addr); + bound = true; + } + catch (SocketException exception) + { + getImpl().close(); + throw exception; + } + catch (RuntimeException exception) + { + getImpl().close(); + throw exception; + } + catch (Error error) + { + getImpl().close(); + throw error; + } + } + + /** + * Checks if the datagram socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + return impl == null; + } + + /** + * Returns the datagram channel assoziated with this datagram socket. + * + * @return The associated <code>DatagramChannel</code> object or null + * + * @since 1.4 + */ + public DatagramChannel getChannel() + { + return null; + } + + /** + * Connects the datagram socket to a specified socket address. + * + * @param address The socket address to connect to. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void connect(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + connect(tmp.getAddress(), tmp.getPort()); + } + + /** + * Returns the binding state of the socket. + * + * @return True if socket bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + return bound; + } + + /** + * Returns the connection state of the socket. + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + return remoteAddress != null; + } + + /** + * Returns the SocketAddress of the host this socket is conneted to + * or null if this socket is not connected. + * + * @return The socket address of the remote host if connected or null + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + return new InetSocketAddress(remoteAddress, remotePort); + } + + /** + * Returns the local SocketAddress this socket is bound to. + * + * @return The local SocketAddress or null if the socket is not bound. + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + return new InetSocketAddress(getLocalAddress(), getLocalPort()); + } + + /** + * Enables/Disables SO_REUSEADDR. + * + * @param on Whether or not to have SO_REUSEADDR turned on. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if SO_REUSEADDR is enabled. + * + * @return True if SO_REUSEADDR is set on the socket, false otherwise. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Enables/Disables SO_BROADCAST + * + * @param enable True if SO_BROADCAST should be enabled, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setBroadcast(boolean enable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(enable)); + } + + /** + * Checks if SO_BROADCAST is enabled + * + * @return Whether SO_BROADCAST is set + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getBroadcast() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_BROADCAST); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see DatagramSocket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, new Integer(tc)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @see DatagramSocket#setTrafficClass(int tc) + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_TOS); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the datagram socket implementation factory for the application + * + * @param fac The factory to set + * + * @exception IOException If an error occurs + * @exception SocketException If the factory is already defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) + throws IOException + { + if (factory != null) + throw new SocketException("DatagramSocketImplFactory already defined"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + factory = fac; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImpl.java b/libjava/classpath/java/net/DatagramSocketImpl.java new file mode 100644 index 00000000000..cfcde92e5fc --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImpl.java @@ -0,0 +1,296 @@ +/* DatagramSocketImpl.java -- Abstract class for UDP socket implementations + Copyright (C) 1998, 1999 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; + + +/** + * This abstract class models a datagram socket implementation. An + * actual implementation class would implement these methods, probably + * via redirecting them to native code. + * <p> + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * <p> + * Status: Believed complete and correct. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public abstract class DatagramSocketImpl implements SocketOptions +{ + /** + * The local port to which this socket is bound + */ + protected int localPort; + + /** + * The FileDescriptor object for this object. + */ + protected FileDescriptor fd; + + /** + * Default, no-argument constructor for subclasses to call. + */ + public DatagramSocketImpl() + { + } + + /** + * This method binds the socket to the specified local port and address. + * + * @param lport The port number to bind to + * @param laddr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected abstract void bind(int lport, InetAddress laddr) + throws SocketException; + + /** + * This methods closes the socket + */ + protected abstract void close(); + + /** + * Creates a new datagram socket. + * + * @exception SocketException If an error occurs + */ + protected abstract void create() throws SocketException; + + /** + * Takes a peek at the next packet received in order to retrieve the + * address of the sender + * + * @param i The <code>InetAddress</code> to fill in with the information + * about the sender if the next packet + * + * @return The port number of the sender of the packet + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract int peek(InetAddress i) throws IOException; + + /** + * Takes a peek at the next packet received. This packet is not consumed. + * With the next peekData/receive operation this packet will be read again. + * + * @param p The <code>DatagramPacket</code> to fill in with the data sent. + * + * @return The port number of the sender of the packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + * + * @since 1.4 + */ + protected abstract int peekData(DatagramPacket p) throws IOException; + + /** + * Transmits the specified packet of data to the network. The destination + * host and port should be encoded in the packet. + * + * @param p The packet to send + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void send(DatagramPacket p) throws IOException; + + /** + * Receives a packet of data from the network Will block until a packet + * arrives. The packet info in populated into the passed in + * <code>DatagramPacket</code> object. + * + * @param p A place to store the incoming packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void receive(DatagramPacket p) throws IOException; + + /** + * Connects the socket to a host specified by address and port. + * + * @param address The <code>InetAddress</code> of the host to connect to + * @param port The port number of the host to connect to + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + protected void connect(InetAddress address, int port) + throws SocketException + { + // This method has to be overwritten by real implementations + } + + /** + * Disconnects the socket. + * + * @since 1.4 + */ + protected void disconnect() + { + // This method has to be overwritten by real implementations + } + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. <b>Use <code>setTimeToLive(int)</code></b> instead. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + * @deprecated + */ + protected abstract void setTTL(byte ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. <b>Use <code>getTimeToLive()</code></b> instead. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + * + * @deprecated // FIXME: when ? + */ + protected abstract byte getTTL() throws IOException; + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + */ + protected abstract void setTimeToLive(int ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + */ + protected abstract int getTimeToLive() throws IOException; + + /** + * Causes this socket to join the specified multicast group + * + * @param inetaddr The multicast address to join with + * + * @exception IOException If an error occurs + */ + protected abstract void join(InetAddress inetaddr) throws IOException; + + /** + * Causes the socket to leave the specified multicast group. + * + * @param inetaddr The multicast address to leave + * + * @exception IOException If an error occurs + */ + protected abstract void leave(InetAddress inetaddr) throws IOException; + + /** + * Causes this socket to join the specified multicast group on a specified + * device + * + * @param mcastaddr The address to leave + * @param netIf The specified network interface to join the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void joinGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Leaves a multicast group + * + * @param mcastaddr The address to join + * @param netIf The specified network interface to leave the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void leaveGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Returns the FileDescriptor for this socket + * + * @return the file descriptor associated with this socket + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the local port this socket is bound to + * + * @return the local port + */ + protected int getLocalPort() + { + return localPort; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImplFactory.java b/libjava/classpath/java/net/DatagramSocketImplFactory.java new file mode 100644 index 00000000000..014651aa899 --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImplFactory.java @@ -0,0 +1,60 @@ +/* DatagramSocketImplFactory.java -- + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.4 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a + * <code>DatagramSocketImpl</code> object. + * This should not be needed by ordinary applications. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.3 + */ +public interface DatagramSocketImplFactory +{ + /** + * This method returns an instance of the DatagramSocketImpl object + * + * @return A DatagramSocketImpl object + */ + DatagramSocketImpl createDatagramSocketImpl(); +} // interface DatagramSocketImplFactory diff --git a/libjava/classpath/java/net/FileNameMap.java b/libjava/classpath/java/net/FileNameMap.java new file mode 100644 index 00000000000..6f1d25a6a52 --- /dev/null +++ b/libjava/classpath/java/net/FileNameMap.java @@ -0,0 +1,65 @@ +/* FileNameMap.java -- Maps filenames to MIME types + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface has one method which, when passed a filename, returns + * the MIME type associated with that filename. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public interface FileNameMap +{ + /** + * This method is passed a filename and is responsible for determining + * the appropriate MIME type for that file. + * + * @param filename The name of the file to generate a MIME type for. + * + * @return The MIME type for the filename passed in. + */ + String getContentTypeFor(String filename); +} // interface FileNameMap diff --git a/libjava/classpath/java/net/HttpURLConnection.java b/libjava/classpath/java/net/HttpURLConnection.java new file mode 100644 index 00000000000..07eae48e77a --- /dev/null +++ b/libjava/classpath/java/net/HttpURLConnection.java @@ -0,0 +1,589 @@ +/* HttpURLConnection.java -- Subclass of communications links using + Hypertext Transfer Protocol. + Copyright (C) 1998, 1999, 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.security.Permission; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class provides a common abstract implementation for those + * URL connection classes that will connect using the HTTP protocol. + * In addition to the functionality provided by the URLConnection + * class, it defines constants for HTTP return code values and + * methods for setting the HTTP request method and determining whether + * or not to follow redirects. + * + * @since 1.1 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class HttpURLConnection extends URLConnection +{ + /* HTTP Success Response Codes */ + + /** + * Indicates that the client may continue with its request. This value + * is specified as part of RFC 2068 but was not included in Sun's JDK, so + * beware of using this value + */ + static final int HTTP_CONTINUE = 100; + + /** + * Indicates the request succeeded. + */ + public static final int HTTP_OK = 200; + + /** + * The requested resource has been created. + */ + public static final int HTTP_CREATED = 201; + + /** + * The request has been accepted for processing but has not completed. + * There is no guarantee that the requested action will actually ever + * be completed succesfully, but everything is ok so far. + */ + public static final int HTTP_ACCEPTED = 202; + + /** + * The meta-information returned in the header is not the actual data + * from the original server, but may be from a local or other copy. + * Normally this still indicates a successful completion. + */ + public static final int HTTP_NOT_AUTHORITATIVE = 203; + + /** + * The server performed the request, but there is no data to send + * back. This indicates that the user's display should not be changed. + */ + public static final int HTTP_NO_CONTENT = 204; + + /** + * The server performed the request, but there is no data to sent back, + * however, the user's display should be "reset" to clear out any form + * fields entered. + */ + public static final int HTTP_RESET = 205; + + /** + * The server completed the partial GET request for the resource. + */ + public static final int HTTP_PARTIAL = 206; + + /* HTTP Redirection Response Codes */ + + /** + * There is a list of choices available for the requested resource. + */ + public static final int HTTP_MULT_CHOICE = 300; + + /** + * The resource has been permanently moved to a new location. + */ + public static final int HTTP_MOVED_PERM = 301; + + /** + * The resource requested has been temporarily moved to a new location. + */ + public static final int HTTP_MOVED_TEMP = 302; + + /** + * The response to the request issued is available at another location. + */ + public static final int HTTP_SEE_OTHER = 303; + + /** + * The document has not been modified since the criteria specified in + * a conditional GET. + */ + public static final int HTTP_NOT_MODIFIED = 304; + + /** + * The requested resource needs to be accessed through a proxy. + */ + public static final int HTTP_USE_PROXY = 305; + + /* HTTP Client Error Response Codes */ + + /** + * The request was misformed or could not be understood. + */ + public static final int HTTP_BAD_REQUEST = 400; + + /** + * The request made requires user authorization. Try again with + * a correct authentication header. + */ + public static final int HTTP_UNAUTHORIZED = 401; + + /** + * Code reserved for future use - I hope way in the future. + */ + public static final int HTTP_PAYMENT_REQUIRED = 402; + + /** + * There is no permission to access the requested resource. + */ + public static final int HTTP_FORBIDDEN = 403; + + /** + * The requested resource was not found. + */ + public static final int HTTP_NOT_FOUND = 404; + + /** + * The specified request method is not allowed for this resource. + */ + public static final int HTTP_BAD_METHOD = 405; + + /** + * Based on the input headers sent, the resource returned in response + * to the request would not be acceptable to the client. + */ + public static final int HTTP_NOT_ACCEPTABLE = 406; + + /** + * The client must authenticate with a proxy prior to attempting this + * request. + */ + public static final int HTTP_PROXY_AUTH = 407; + + /** + * The request timed out. + */ + public static final int HTTP_CLIENT_TIMEOUT = 408; + + /** + * There is a conflict between the current state of the resource and the + * requested action. + */ + public static final int HTTP_CONFLICT = 409; + + /** + * The requested resource is no longer available. This ususally indicates + * a permanent condition. + */ + public static final int HTTP_GONE = 410; + + /** + * A Content-Length header is required for this request, but was not + * supplied. + */ + public static final int HTTP_LENGTH_REQUIRED = 411; + + /** + * A client specified pre-condition was not met on the server. + */ + public static final int HTTP_PRECON_FAILED = 412; + + /** + * The request sent was too large for the server to handle. + */ + public static final int HTTP_ENTITY_TOO_LARGE = 413; + + /** + * The name of the resource specified was too long. + */ + public static final int HTTP_REQ_TOO_LONG = 414; + + /** + * The request is in a format not supported by the requested resource. + */ + public static final int HTTP_UNSUPPORTED_TYPE = 415; + + /* HTTP Server Error Response Codes */ + + /** + * This error code indicates that some sort of server error occurred. + * + * @deprecated + */ + public static final int HTTP_SERVER_ERROR = 500; + + /** + * The server encountered an unexpected error (such as a CGI script crash) + * that prevents the request from being fulfilled. + */ + public static final int HTTP_INTERNAL_ERROR = 500; + + /** + * The server does not support the requested functionality. + * @since 1.3 + */ + public static final int HTTP_NOT_IMPLEMENTED = 501; + + /** + * The proxy encountered a bad response from the server it was proxy-ing for + */ + public static final int HTTP_BAD_GATEWAY = 502; + + /** + * The HTTP service is not availalble, such as because it is overloaded + * and does not want additional requests. + */ + public static final int HTTP_UNAVAILABLE = 503; + + /** + * The proxy timed out getting a reply from the remote server it was + * proxy-ing for. + */ + public static final int HTTP_GATEWAY_TIMEOUT = 504; + + /** + * This server does not support the protocol version requested. + */ + public static final int HTTP_VERSION = 505; + + // Non-HTTP response static variables + + /** + * Flag to indicate whether or not redirects should be automatically + * followed by default. + */ + private static boolean followRedirects = true; + + /** + * This is a list of valid request methods, separated by "|" characters. + */ + private static final String valid_methods = + "|GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE|"; + + // Instance Variables + + /** + * The requested method in use for this connection. Default is GET. + */ + protected String method = "GET"; + + /** + * The response code received from the server + */ + protected int responseCode = -1; + + /** + * The response message string received from the server. + */ + protected String responseMessage; + + /** + * If this instance should follow redirect requests. + */ + protected boolean instanceFollowRedirects = followRedirects; + + /** + * Whether we already got a valid response code for this connection. + * Used by <code>getResponseCode()</code> and + * <code>getResponseMessage()</code>. + */ + private boolean gotResponseVals; + + /** + * Create an HttpURLConnection for the specified URL + * + * @param url The URL to create this connection for. + */ + protected HttpURLConnection(URL url) + { + super(url); + } + + /** + * Closes the connection to the server. + */ + public abstract void disconnect(); + + /** + * Returns a boolean indicating whether or not this connection is going + * through a proxy + * + * @return true if through a proxy, false otherwise + */ + public abstract boolean usingProxy(); + + /** + * Sets whether HTTP redirects (requests with response code 3xx) should be + * automatically followed by this class. True by default + * + * @param set true if redirects should be followed, false otherwis. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setFollowRedirects(boolean set) + { + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + followRedirects = set; + } + + /** + * Returns a boolean indicating whether or not HTTP redirects will + * automatically be followed or not. + * + * @return true if redirects will be followed, false otherwise + */ + public static boolean getFollowRedirects() + { + return followRedirects; + } + + /** + * Returns the value of this HttpURLConnection's instanceFollowRedirects + * field + * + * @return true if following redirects is enabled, false otherwise + */ + public boolean getInstanceFollowRedirects() + { + return instanceFollowRedirects; + } + + /** + * Sets the value of this HttpURLConnection's instanceFollowRedirects field + * + * @param follow true to enable following redirects, false otherwise + */ + public void setInstanceFollowRedirects(boolean follow) + { + instanceFollowRedirects = follow; + } + + /** + * Set the method for the URL request, one of: + * GET POST HEAD OPTIONS PUT DELETE TRACE are legal + * + * @param method the method to use + * + * @exception ProtocolException If the method cannot be reset or if the + * requested method isn't valid for HTTP + */ + public void setRequestMethod(String method) throws ProtocolException + { + if (connected) + throw new ProtocolException("Already connected"); + + method = method.toUpperCase(); + if (valid_methods.indexOf("|" + method + "|") != -1) + this.method = method; + else + throw new ProtocolException("Invalid HTTP request method: " + method); + } + + /** + * The request method currently in use for this connection. + * + * @return The request method + */ + public String getRequestMethod() + { + return method; + } + + /** + * Gets the status code from an HTTP response message, or -1 if + * the response code could not be determined. + * Note that all valid response codes have class variables + * defined for them in this class. + * + * @return The response code + * + * @exception IOException If an error occurs + */ + public int getResponseCode() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseCode; + } + + /** + * Gets the HTTP response message, if any, returned along with the + * response code from a server. Null if no response message was set + * or an error occured while connecting. + * + * @return The response message + * + * @exception IOException If an error occurs + */ + public String getResponseMessage() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseMessage; + } + + private void getResponseVals() throws IOException + { + // getHeaderField() will connect for us, but do it here first in + // order to pick up IOExceptions. + if (! connected) + connect(); + + gotResponseVals = true; + + // If responseCode not yet explicitly set by subclass + if (responseCode == -1) + { + // Response is the first header received from the connection. + String respField = getHeaderField(0); + + if (respField == null || ! respField.startsWith("HTTP/")) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + return; + } + + int firstSpc; + int nextSpc; + firstSpc = respField.indexOf(' '); + nextSpc = respField.indexOf(' ', firstSpc + 1); + responseMessage = respField.substring(nextSpc + 1); + String codeStr = respField.substring(firstSpc + 1, nextSpc); + try + { + responseCode = Integer.parseInt(codeStr); + } + catch (NumberFormatException e) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + } + } + } + + /** + * Returns a permission object representing the permission necessary to make + * the connection represented by this object + * + * @return the permission necessary for this connection + * + * @exception IOException If an error occurs + */ + public Permission getPermission() throws IOException + { + URL url = getURL(); + String host = url.getHost(); + int port = url.getPort(); + if (port == -1) + port = 80; + + host = host + ":" + port; + + return new SocketPermission(host, "connect"); + } + + /** + * This method allows the caller to retrieve any data that might have + * been sent despite the fact that an error occurred. For example, the + * HTML page sent along with a 404 File Not Found error. If the socket + * is not connected, or if no error occurred or no data was returned, + * this method returns <code>null</code>. + * + * @return An <code>InputStream</code> for reading error data. + */ + public InputStream getErrorStream() + { + if (! connected) + return null; + + int code; + try + { + code = getResponseCode(); + } + catch (IOException e) + { + code = -1; + } + + if (code == -1) + return null; + + if (((code / 100) != 4) || ((code / 100) != 5)) + return null; + + try + { + PushbackInputStream pbis = new PushbackInputStream(getInputStream()); + + int i = pbis.read(); + if (i == -1) + return null; + + pbis.unread(i); + return pbis; + } + catch (IOException e) + { + return null; + } + } + + /** + * Returns the value of the named field parsed as date + * + * @param key the key of the header field + * @param value the default value if the header field is not present + * + * @return the value of the header field + */ + public long getHeaderFieldDate(String key, long value) + { + // FIXME: implement this correctly + // http://www.w3.org/Protocols/HTTP-NG/ng-notes.txt + return super.getHeaderFieldDate(key, value); + } +} diff --git a/libjava/classpath/java/net/Inet4Address.java b/libjava/classpath/java/net/Inet4Address.java new file mode 100644 index 00000000000..00c6501de0c --- /dev/null +++ b/libjava/classpath/java/net/Inet4Address.java @@ -0,0 +1,233 @@ +/* Inet4Address.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.ObjectStreamException; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt), + * RFC 1918 (http://www.ietf.org/rfc/rfc1918.txt), + * RFC 2365 (http://www.ietf.org/rfc/rfc2365.txt) + * + * @author Michael Koch + * @status Believed complete and correct. + */ +public final class Inet4Address extends InetAddress +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + static final long serialVersionUID = 3286316764910316507L; + + /** + * needed for serialization + */ + private Object writeReplace() throws ObjectStreamException + { + return new InetAddress(addr, hostName); + } + + /** + * Initializes this object's addr instance variable from the passed in + * byte array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + */ + Inet4Address(byte[] addr, String host) + { + super(addr, host); + } + + /** + * Checks if the address is a multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return super.isMulticastAddress(); + } + + /** + * Checks if this address is a loopback address + */ + public boolean isLoopbackAddress() + { + return super.isLoopbackAddress(); + } + + /** + * Checks if this address is a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + return super.isAnyLocalAddress(); + } + + /** + * Checks if this address is a link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return super.isLinkLocalAddress(); + } + + /** + * Checks if this address is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + return super.isSiteLocalAddress(); + } + + /** + * Checks if this multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + return super.isMCGlobal(); + } + + /** + * Checks if this multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + return isMCNodeLocal(); + } + + /** + * Checks if this multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + return super.isMCLinkLocal(); + } + + /** + * Checks if this multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + return super.isMCSiteLocal(); + } + + /** + * Checks if this multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + return isMCOrgLocal(); + } + + /** + * Returns the address of the current instance + */ + public byte[] getAddress() + { + return (byte[]) addr.clone(); + } + + /** + * Returns the address as string + * + * @since 1.0.2 + */ + public String getHostAddress() + { + return super.getHostAddress(); + } + + /** + * Computes the hashcode of the instance + */ + public int hashCode() + { + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xFF); + + return hash; + } + + /** + * Compare the current Inet4Address instance with obj + * + * @param obj Object to compare with + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + byte[] addr1 = addr; + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr1.length != addr2.length) + return false; + + for (int i = addr1.length; --i >= 0;) + if (addr1[i] != addr2[i]) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/net/Inet6Address.java b/libjava/classpath/java/net/Inet6Address.java new file mode 100644 index 00000000000..0d62fe919a0 --- /dev/null +++ b/libjava/classpath/java/net/Inet6Address.java @@ -0,0 +1,261 @@ +/* Inet6Address.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.util.Arrays; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt) + * + * @author Michael Koch + * @status Believed complete and correct. + */ +public final class Inet6Address extends InetAddress +{ + static final long serialVersionUID = 6880410070516793377L; + + /** + * Needed for serialization + */ + byte[] ipaddress; + + /** + * Create an Inet6Address object + * + * @param addr The IP address + * @param host The hostname + */ + Inet6Address(byte[] addr, String host) + { + super(addr, host); + // Super constructor clones the addr. Get a reference to the clone. + this.ipaddress = this.addr; + } + + /** + * Utility routine to check if the InetAddress is an IP multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return ipaddress[0] == 0xFF; + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + byte[] anylocal = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + return Arrays.equals(ipaddress, anylocal); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + byte[] loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + + return Arrays.equals(ipaddress, loopback); + } + + /** + * Utility routine to check if the InetAddress is an link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return ipaddress[0] == 0xFA; + } + + /** + * Utility routine to check if the InetAddress is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + return ipaddress[0] == 0xFB; + } + + /** + * Utility routine to check if the multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0xE; + } + + /** + * Utility routine to check if the multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x1; + } + + /** + * Utility routine to check if the multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x2; + } + + /** + * Utility routine to check if the multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x5; + } + + /** + * Utility routine to check if the multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x8; + } + + /** + * Returns the raw IP address of this InetAddress object. The result is in + * network byte order: the highest order byte of the address is i + * n getAddress()[0] + */ + public byte[] getAddress() + { + return (byte[]) ipaddress.clone(); + } + + /** + * Returns the IP address string in textual presentation + */ + public String getHostAddress() + { + StringBuffer sbuf = new StringBuffer(40); + + for (int i = 0; i < 16; i += 2) + { + int x = ((ipaddress[i] & 0xFF) << 8) | (ipaddress[i + 1] & 0xFF); + + if (i > 0) + sbuf.append(':'); + + sbuf.append(Integer.toHexString(x)); + } + + return sbuf.toString(); + } + + /** + * Returns a hashcode for this IP address + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Compares this object against the specified object + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Inet6Address)) + return false; + + // this.ipaddress is never set in this class except to + // the value of the super class' addr. The super classes + // equals(Object) will do the compare. + return super.equals(obj); + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 compatible IPv6 address + * + * @since 1.4 + */ + public boolean isIPv4CompatibleAddress() + { + if (ipaddress[0] != 0x00 || ipaddress[1] != 0x00 || ipaddress[2] != 0x00 + || ipaddress[3] != 0x00 || ipaddress[4] != 0x00 + || ipaddress[5] != 0x00 || ipaddress[6] != 0x00 + || ipaddress[7] != 0x00 || ipaddress[8] != 0x00 + || ipaddress[9] != 0x00 || ipaddress[10] != 0x00 + || ipaddress[11] != 0x00) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/net/InetAddress.java b/libjava/classpath/java/net/InetAddress.java new file mode 100644 index 00000000000..94dc6cb6d84 --- /dev/null +++ b/libjava/classpath/java/net/InetAddress.java @@ -0,0 +1,813 @@ +/* InetAddress.java -- Class to model an Internet address + Copyright (C) 1998, 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.StringTokenizer; + +/** + * This class models an Internet address. It does not have a public + * constructor. Instead, new instances of this objects are created + * using the static methods getLocalHost(), getByName(), and + * getAllByName(). + * + * <p>This class fulfills the function of the C style functions gethostname(), + * gethostbyname(), and gethostbyaddr(). It resolves Internet DNS names + * into their corresponding numeric addresses and vice versa.</p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * + * @specnote This class is not final since JK 1.4 + */ +public class InetAddress implements Serializable +{ + private static final long serialVersionUID = 3286316764910316507L; + + /** + * The default DNS hash table size, + * Use a prime number happy with hash table. + */ + private static final int DEFAULT_CACHE_SIZE = 89; + + /** + * The default caching period in minutes. + */ + private static final int DEFAULT_CACHE_PERIOD = 4 * 60; + + /** + * Percentage of cache entries to purge when the table gets full. + */ + private static final int DEFAULT_CACHE_PURGE_PCT = 30; + + /** + * The special IP address INADDR_ANY. + */ + private static InetAddress inaddr_any; + + /** + * Dummy InetAddress, used to bind socket to any (all) network interfaces. + */ + static InetAddress ANY_IF; + + /** + * Stores static localhost address object. + */ + static InetAddress LOCALHOST; + + /** + * The size of the cache. + */ + private static int cache_size = 0; + + /** + * The length of time we will continue to read the address from cache + * before forcing another lookup. + */ + private static int cache_period = 0; + + /** + * What percentage of the cache we will purge if it gets full. + */ + private static int cache_purge_pct = 0; + + /** + * HashMap to use as DNS lookup cache. + * Use HashMap because all accesses to cache are already synchronized. + */ + private static HashMap cache; + + static + { + // Look for properties that override default caching behavior + cache_size = + Integer.getInteger("gnu.java.net.dns_cache_size", DEFAULT_CACHE_SIZE) + .intValue(); + cache_period = + Integer.getInteger("gnu.java.net.dns_cache_period", + DEFAULT_CACHE_PERIOD * 60 * 1000).intValue(); + + cache_purge_pct = + Integer.getInteger("gnu.java.net.dns_cache_purge_pct", + DEFAULT_CACHE_PURGE_PCT).intValue(); + + // Fallback to defaults if necessary + if ((cache_purge_pct < 1) || (cache_purge_pct > 100)) + cache_purge_pct = DEFAULT_CACHE_PURGE_PCT; + + // Create the cache + if (cache_size != 0) + cache = new HashMap(cache_size); + + // precompute the ANY_IF address + try + { + ANY_IF = getInaddrAny(); + + byte[] ip_localhost = { 127, 0, 0, 1 }; + LOCALHOST = new Inet4Address(ip_localhost, "localhost"); + } + catch (UnknownHostException uhe) + { + // Hmmm, make one up and hope that it works. + byte[] zeros = { 0, 0, 0, 0 }; + ANY_IF = new Inet4Address(zeros, "0.0.0.0"); + } + } + + /** + * The Serialized Form specifies that an int 'address' is saved/restored. + * This class uses a byte array internally so we'll just do the conversion + * at serialization time and leave the rest of the algorithm as is. + */ + private int address; + + /** + * An array of octets representing an IP address. + */ + transient byte[] addr; + + /** + * The name of the host for this address. + */ + String hostName; + + /** + * The time this address was looked up. + */ + transient long lookup_time; + + /** + * The field 'family' seems to be the AF_ value. + * FIXME: Much of the code in the other java.net classes does not make + * use of this family field. A better implementation would be to make + * use of getaddrinfo() and have other methods just check the family + * field rather than examining the length of the address each time. + */ + int family; + + /** + * Initializes this object's addr instance variable from the passed in + * byte array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param ipaddr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + */ + InetAddress(byte[] ipaddr, String hostname) + { + addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone(); + hostName = hostname; + + lookup_time = System.currentTimeMillis(); + + family = 2; /* AF_INET */ + } + + /** + * Returns true if this address is a multicast address, false otherwise. + * An address is multicast if the high four bits are "1110". These are + * also known as "Class D" addresses. + * + * @return true if mulitcast, false if not + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + // Mask against high order bits of 1110 + if (addr.length == 4) + return (addr[0] & 0xf0) == 0xe0; + + return false; + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + return equals(ANY_IF); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + return (addr[0] & 0xff) == 0x7f; + } + + /** + * Utility routine to check if InetAddress is a link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + // XXX: This seems to not exist with IPv4 addresses + return false; + } + + /** + * Utility routine to check if InetAddress is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + + // 10.0.0.0/8 + if ((addr[0] & 0xff) == 0x0a) + return true; + + // 172.16.0.0/12 + if ((addr[0] & 0xff) == 0xac && (addr[1] & 0xf0) == 0x10) + return true; + + // 192.168.0.0/16 + if ((addr[0] & 0xff) == 0xc0 && (addr[1] & 0xff) == 0xa8) + return true; + + // XXX: Do we need to check more addresses here ? + return false; + } + + /** + * Utility routine to check if InetAddress is a global multicast address + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + // XXX: This seems to not exist with IPv4 addresses + return false; + } + + /** + * Utility routine to check if InetAddress is a node local multicast address. + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + // XXX: This seems to not exist with IPv4 addresses + return false; + } + + /** + * Utility routine to check if InetAddress is a link local multicast address. + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + if (! isMulticastAddress()) + return false; + + return ((addr[0] & 0xff) == 0xe0 + && (addr[1] & 0xff) == 0x00 + && (addr[2] & 0xff) == 0x00); + } + + /** + * Utility routine to check if InetAddress is a site local multicast address. + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + // XXX: This seems to not exist with IPv4 addresses + return false; + } + + /** + * Utility routine to check if InetAddress is a organization local + * multicast address. + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + // This is the IPv4 implementation. + // Any class derived from InetAddress should override this. + // XXX: This seems to not exist with IPv4 addresses + return false; + } + + /** + * Returns the hostname for this address. This will return the IP address + * as a String if there is no hostname available for this address + * + * @return The hostname for this address + */ + public String getHostName() + { + if (hostName != null) + return hostName; + + try + { + hostName = VMInetAddress.getHostByAddr(addr); + return hostName; + } + catch (UnknownHostException e) + { + return getHostAddress(); + } + } + + /** + * Returns the canonical hostname represented by this InetAddress + * + * @since 1.4 + */ + public String getCanonicalHostName() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + try + { + sm.checkConnect(hostName, -1); + } + catch (SecurityException e) + { + return getHostAddress(); + } + } + + // Try to find the FDQN now + InetAddress address; + byte[] ipaddr = getAddress(); + + if (ipaddr.length == 16) + address = new Inet6Address(getAddress(), null); + else + address = new Inet4Address(getAddress(), null); + + return address.getHostName(); + } + + /** + * Returns the IP address of this object as a byte array. + * + * @return IP address + */ + public byte[] getAddress() + { + // An experiment shows that JDK1.2 returns a different byte array each + // time. This makes sense, in terms of security. + return (byte[]) addr.clone(); + } + + /** + * Returns the IP address of this object as a String. The address is in + * the dotted octet notation, for example, "127.0.0.1". + * + * @return The IP address of this object in String form + * + * @since 1.0.2 + */ + public String getHostAddress() + { + StringBuffer sb = new StringBuffer(40); + + int len = addr.length; + int i = 0; + + for ( ; ; ) + { + sb.append(addr[i] & 0xff); + i++; + + if (i == len) + break; + + sb.append('.'); + } + + return sb.toString(); + } + + /** + * Returns a hash value for this address. Useful for creating hash + * tables. Overrides Object.hashCode() + * + * @return A hash value for this address. + */ + public int hashCode() + { + // There hashing algorithm is not specified, but a simple experiment + // shows that it is equal to the address, as a 32-bit big-endian integer. + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xff); + + return hash; + } + + /** + * Tests this address for equality against another InetAddress. The two + * addresses are considered equal if they contain the exact same octets. + * This implementation overrides Object.equals() + * + * @param obj The address to test for equality + * + * @return true if the passed in object's address is equal to this one's, + * false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + // "The Java Class Libraries" 2nd edition says "If a machine has + // multiple names instances of InetAddress for different name of + // that same machine are not equal. This is because they have + // different host names." This violates the description in the + // JDK 1.2 API documentation. A little experimentation + // shows that the latter is correct. + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr.length != addr2.length) + return false; + + for (int i = 0; i < addr.length; i++) + if (addr[i] != addr2[i]) + return false; + + return true; + } + + /** + * Converts this address to a String. This string contains the IP in + * dotted decimal form. For example: "127.0.0.1" This method is equivalent + * to getHostAddress() and overrides Object.toString() + * + * @return This address in String form + */ + public String toString() + { + String addr = getHostAddress(); + String host = (hostName != null) ? hostName : ""; + return host + "/" + addr; + } + + /** + * Returns an InetAddress object given the raw IP address. + * + * The argument is in network byte order: the highest order byte of the + * address is in getAddress()[0]. + * + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address has illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(byte[] addr) + throws UnknownHostException + { + return getByAddress(null, addr); + } + + /** + * Creates an InetAddress based on the provided host name and IP address. + * No name service is checked for the validity of the address. + * + * @param host The hostname of the InetAddress object to create + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address is of illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(String host, byte[] addr) + throws UnknownHostException + { + if (addr.length == 4) + return new Inet4Address(addr, host); + + if (addr.length == 16) + return new Inet6Address(addr, host); + + throw new UnknownHostException("IP address has illegal length"); + } + + /** + * If hostname is a valid numeric IP address, return the numeric address. + * Otherwise, return null. + * + * @param hostname the name of the host + */ + private static byte[] aton(String hostname) + { + StringTokenizer st = new StringTokenizer(hostname, "."); + + if (st.countTokens() == 4) + { + int index; + byte[] address = new byte[4]; + + for (index = 0; index < 4; index++) + { + try + { + short n = Short.parseShort(st.nextToken()); + + if ((n < 0) || (n > 255)) + break; + + address[index] = (byte) n; + } + catch (NumberFormatException e) + { + break; + } + } + + if (index == 4) + return address; + } + + return null; + } + + /** + * Returns an InetAddress object representing the IP address of the given + * hostname. This name can be either a hostname such as "www.urbanophile.com" + * or an IP address in dotted decimal format such as "127.0.0.1". If the + * hostname is null or "", the hostname of the local machine is supplied by + * default. This method is equivalent to returning the first element in + * the InetAddress array returned from GetAllByName. + * + * @param hostname The name of the desired host, or null for the local + * loopback address. + * + * @return The address of the host as an InetAddress object. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress getByName(String hostname) + throws UnknownHostException + { + InetAddress[] addresses = getAllByName(hostname); + return addresses[0]; + } + + /** + * Returns an array of InetAddress objects representing all the host/ip + * addresses of a given host, given the host's name. This name can be + * either a hostname such as "www.urbanophile.com" or an IP address in + * dotted decimal format such as "127.0.0.1". If the value is null, the + * hostname of the local machine is supplied by default. + * + * @param hostname The name of the desired host, or null for the + * local loopback address. + * + * @return All addresses of the host as an array of InetAddress objects. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress[] getAllByName(String hostname) + throws UnknownHostException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkConnect(hostname, -1); + + InetAddress[] addresses; + + // Default to current host if necessary + if (hostname == null) + { + addresses = new InetAddress[1]; + addresses[0] = LOCALHOST; + return addresses; + } + + // Check the cache for this host before doing a lookup + addresses = checkCacheFor(hostname); + + if (addresses != null) + return addresses; + + // Not in cache, try the lookup + byte[][] iplist = VMInetAddress.getHostByName(hostname); + + if (iplist.length == 0) + throw new UnknownHostException(hostname); + + addresses = new InetAddress[iplist.length]; + + for (int i = 0; i < iplist.length; i++) + { + if (iplist[i].length != 4) + throw new UnknownHostException(hostname); + + addresses[i] = new Inet4Address(iplist[i], hostname); + } + + addToCache(hostname, addresses); + return addresses; + } + + /** + * This method checks the DNS cache to see if we have looked this hostname + * up before. If so, we return the cached addresses unless it has been in the + * cache too long. + * + * @param hostname The hostname to check for + * + * @return The InetAddress for this hostname or null if not available + */ + private static synchronized InetAddress[] checkCacheFor(String hostname) + { + InetAddress[] addresses = null; + + if (cache_size == 0) + return null; + + Object obj = cache.get(hostname); + if (obj == null) + return null; + + if (obj instanceof InetAddress[]) + addresses = (InetAddress[]) obj; + + if (addresses == null) + return null; + + if (cache_period != -1) + if ((System.currentTimeMillis() - addresses[0].lookup_time) > cache_period) + { + cache.remove(hostname); + return null; + } + + return addresses; + } + + /** + * This method adds an InetAddress object to our DNS cache. Note that + * if the cache is full, then we run a purge to get rid of old entries. + * This will cause a performance hit, thus applications using lots of + * lookups should set the cache size to be very large. + * + * @param hostname The hostname to cache this address under + * @param obj The InetAddress or InetAddress array to store + */ + private static synchronized void addToCache(String hostname, Object obj) + { + if (cache_size == 0) + return; + + // Check to see if hash table is full + if (cache_size != -1) + if (cache.size() == cache_size) + { + // FIXME Add code to purge later. + } + + cache.put(hostname, obj); + } + + /** + * Returns the special address INADDR_ANY used for binding to a local + * port on all IP addresses hosted by a the local host. + * + * @return An InetAddress object representing INDADDR_ANY + * + * @exception UnknownHostException If an error occurs + */ + static InetAddress getInaddrAny() throws UnknownHostException + { + if (inaddr_any == null) + { + byte[] tmp = VMInetAddress.lookupInaddrAny(); + inaddr_any = new Inet4Address(tmp, null); + } + + return inaddr_any; + } + + /** + * Returns an InetAddress object representing the address of the current + * host. + * + * @return The local host's address + * + * @exception UnknownHostException If no IP address for the host could + * be found + */ + public static InetAddress getLocalHost() throws UnknownHostException + { + String hostname = VMInetAddress.getLocalHostname(); + return getByName(hostname); + } + + /* + * Needed for serialization + */ + private void readResolve() throws ObjectStreamException + { + // FIXME: implement this + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + addr = new byte[4]; + addr[3] = (byte) address; + + for (int i = 2; i >= 0; --i) + addr[i] = (byte) (address >>= 8); + + family = 2; /* AF_INET */ + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + // Build a 32 bit address from the last 4 bytes of a 4 byte IPv4 address + // or a 16 byte IPv6 address. + int len = addr.length; + int i = len - 4; + + for (; i < len; i++) + address = address << 8 | (addr[i] & 0xff); + + oos.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/InetSocketAddress.java b/libjava/classpath/java/net/InetSocketAddress.java new file mode 100644 index 00000000000..30d34e7e808 --- /dev/null +++ b/libjava/classpath/java/net/InetSocketAddress.java @@ -0,0 +1,221 @@ +/* InetSocketAddress.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * InetSocketAddress instances represent socket addresses + * in the java.nio package. They encapsulate a InetAddress and + * a port number. + * + * @since 1.4 + */ +public class InetSocketAddress extends SocketAddress +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 5076001401234631237L; + + /** + * Name of host. + */ + private String hostname; + + /** + * Address of host. + */ + private InetAddress addr; + + /** + * Port of host. + */ + private int port; + + /** + * Constructs an InetSocketAddress instance. + * + * @param addr Address of the socket + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegel + */ + public InetSocketAddress(InetAddress addr, int port) + throws IllegalArgumentException + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + this.addr = addr; + this.port = port; + this.hostname = addr.getHostName(); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegal + */ + public InetSocketAddress(int port) throws IllegalArgumentException + { + this((InetAddress) null, port); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * + * @exception IllegalArgumentException If the port number is illegal + */ + public InetSocketAddress(String hostname, int port) + throws IllegalArgumentException + { + if (hostname == null) + throw new IllegalArgumentException("Null host name value"); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + this.port = port; + this.hostname = hostname; + + try + { + this.addr = InetAddress.getByName(hostname); + } + catch (Exception e) // UnknownHostException, SecurityException + { + this.addr = null; + } + } + + /** + * Test if obj is a <code>InetSocketAddress</code> and + * has the same address and port + * + * @param obj The obj to compare this address with. + * + * @return True if obj is equal. + */ + public final boolean equals(Object obj) + { + // InetSocketAddress objects are equal when addr and port are equal. + // The hostname may differ. + if (obj instanceof InetSocketAddress) + { + InetSocketAddress sa = (InetSocketAddress) obj; + + if (addr == null && sa.addr != null) + return false; + else if (addr == null && sa.addr == null) + return hostname.equals(sa.hostname) && sa.port == port; + else + return addr.equals(sa.addr) && sa.port == port; + } + + return false; + } + + /** + * Returns the <code>InetAddress</code> or + * <code>null</code> if its unresolved + * + * @return The IP address of this address. + */ + public final InetAddress getAddress() + { + return addr; + } + + /** + * Returns <code>hostname</code> + * + * @return The hostname of this address. + */ + public final String getHostName() + { + return hostname; + } + + /** + * Returns the <code>port</code> + * + * @return The port of this address. + */ + public final int getPort() + { + return port; + } + + /** + * Returns the hashcode of the <code>InetSocketAddress</code> + * + * @return The hashcode for this address. + */ + public final int hashCode() + { + return port + addr.hashCode(); + } + + /** + * Checks wether the address has been resolved or not + * + * @return True if address is unresolved. + */ + public final boolean isUnresolved() + { + return addr == null; + } + + /** + * Returns the <code>InetSocketAddress</code> as string + * + * @return A string represenation of this address. + */ + public String toString() + { + return (addr == null ? hostname : addr.getHostName()) + ":" + port; + } +} diff --git a/libjava/classpath/java/net/JarURLConnection.java b/libjava/classpath/java/net/JarURLConnection.java new file mode 100644 index 00000000000..3067ecb81e6 --- /dev/null +++ b/libjava/classpath/java/net/JarURLConnection.java @@ -0,0 +1,228 @@ +/* JarURLConnection.java -- Class for manipulating remote jar files + Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + + +/** + * This abstract class represents a common superclass for implementations + * of jar URL's. A jar URL is a special type of URL that allows JAR + * files on remote systems to be accessed. It has the form: + * <p> + * jar:<standard URL pointing to jar filei>!/file/within/jarfile + * <p> for example: + * <p> + * jar:http://www.urbanophile.com/java/foo.jar!/com/urbanophile/bar.class + * <p> + * That example URL points to the file /com/urbanophile/bar.class in the + * remote JAR file http://www.urbanophile.com/java/foo.jar. The HTTP + * protocol is used only as an example. Any supported remote protocol + * can be used. + * <p> + * This class currently works by retrieving the entire jar file into a + * local cache file, then performing standard jar operations on it. + * (At least this is true for the default protocol implementation). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Kresten Krab Thorup (krab@gnu.org) + * @date Aug 10, 1999. + * + * @since 1.2 + */ +public abstract class JarURLConnection extends URLConnection +{ + /** + * This is the actual URL that points the remote jar file. This is parsed + * out of the jar URL by the constructor. + */ + private final URL jarFileURL; + + /** + * The connection to the jar file itself. A JarURLConnection + * can represent an entry in a jar file or an entire jar file. In + * either case this describes just the jar file itself. + */ + protected URLConnection jarFileURLConnection; + + /** + * This is the jar file "entry name" or portion after the "!/" in the + * URL which represents the pathname inside the actual jar file. + */ + private final String entryName; + + /** + * Creates a JarURLConnection from an URL object + * + * @param url The URL object for this connection. + * + * @exception MalformedURLException If url is invalid + * + * @specnote This constructor is protected since JDK 1.4 + */ + protected JarURLConnection(URL url) throws MalformedURLException + { + super(url); + + if (! url.getProtocol().equals("jar")) + throw new MalformedURLException(url + ": Not jar protocol."); + + String spec = url.getFile(); + int bang = spec.indexOf("!/"); + if (bang == -1) + throw new MalformedURLException(url + ": No `!/' in spec."); + + // Extract the url for the jar itself. + jarFileURL = new URL(spec.substring(0, bang)); + + // Get the name of the entry, if any. + entryName = spec.length() == (bang + 2) ? null : spec.substring(bang + 2); + } + + /** + * This method returns the "real" URL where the JarFile is located. + * //****Is this right?***** + * + * @return The remote URL + */ + public URL getJarFileURL() + { + return jarFileURL; + } + + /** + * Returns the "entry name" portion of the jar URL. This is the portion + * after the "!/" in the jar URL that represents the pathname inside the + * actual jar file. + * + * @return The entry name. + */ + public String getEntryName() + { + return entryName; + } + + /** + * Returns the entry in this jar file specified by the URL. + * + * @return The jar entry + * + * @exception IOException If an error occurs + */ + public JarEntry getJarEntry() throws IOException + { + JarFile jarFile = getJarFile(); + + return jarFile != null ? jarFile.getJarEntry(entryName) : null; + } + + /** + * Returns a read-only JarFile object for the remote jar file + * + * @return The JarFile object + * + * @exception IOException If an error occurs + */ + public abstract JarFile getJarFile() throws IOException; + + /** + * Returns an array of Certificate objects for the jar file entry specified + * by this URL or null if there are none + * + * @return A Certificate array + * + * @exception IOException If an error occurs + */ + public Certificate[] getCertificates() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getCertificates() : null; + } + + /** + * Returns the main Attributes for the jar file specified in the URL or + * null if there are none + * + * @return The main Attributes for the JAR file for this connection + * + * @exception IOException If an error occurs + */ + public Attributes getMainAttributes() throws IOException + { + Manifest manifest = getManifest(); + + return manifest != null ? manifest.getMainAttributes() : null; + } + + /** + * Returns the Attributes for the Jar entry specified by the URL or null + * if none + * + * @return The Attributes object for this connection if the URL for it points + * to a JAR file entry, null otherwise + * + * @exception IOException If an error occurs + */ + public Attributes getAttributes() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getAttributes() : null; + } + + /** + * Returns a Manifest object for this jar file, or null if there is no + * manifest. + * + * @return The Manifest for this connection, or null if none + * + * @exception IOException If an error occurs + */ + public Manifest getManifest() throws IOException + { + JarFile file = getJarFile(); + + return file != null ? file.getManifest() : null; + } +} diff --git a/libjava/classpath/java/net/MalformedURLException.java b/libjava/classpath/java/net/MalformedURLException.java new file mode 100644 index 00000000000..27e201886a0 --- /dev/null +++ b/libjava/classpath/java/net/MalformedURLException.java @@ -0,0 +1,74 @@ +/* MalformedURLException.java -- A URL was not in a valid format + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a URL passed to an object was not in a + * valid format. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class MalformedURLException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -182787522200415866L; + + /** + * Create a new instance without a descriptive error message. + */ + public MalformedURLException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public MalformedURLException(String message) + { + super(message); + } +} // class MalformedURLException diff --git a/libjava/classpath/java/net/MimeTypeMapper.java b/libjava/classpath/java/net/MimeTypeMapper.java new file mode 100644 index 00000000000..1747f4d5685 --- /dev/null +++ b/libjava/classpath/java/net/MimeTypeMapper.java @@ -0,0 +1,213 @@ +/* MimeTypeMapper.java -- A class for mapping file names to MIME types + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.util.Hashtable; + + +/** + * This non-public class is used to implement the FileNameMap interface + * which defines a mechanism for mapping filenames to MIME types. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class MimeTypeMapper implements FileNameMap +{ + /** + * This array of strings is used to identify a MIME type based on a file + * extension. This is list is based on the Apache mime.types file. + */ + protected static final String[][] mime_strings = + { + { "application/mac-binhex40", "hqx" }, + { "application/mac-compactpro", "cpt" }, + { "application/msword", "doc" }, + { "application/octet-stream", "bin" }, + { "application/octet-stream", "dms" }, + { "application/octet-stream", "lha" }, + { "application/octet-stream", "lzh" }, + { "application/octet-stream", "exe" }, + { "application/octet-stream", "class" }, + { "application/oda", "oda" }, + { "application/pdf", "pdf" }, + { "application/postscript", "ai" }, + { "application/postscript", "eps" }, + { "application/postscript", "ps" }, + { "application/powerpoint", "ppt" }, + { "application/rtf", "rtf" }, + { "application/x-bcpio", "bcpio" }, + { "application/x-cdlink", "vcd" }, + { "application/x-compress", "Z" }, + { "application/x-cpio", "cpio" }, + { "application/x-csh", "csh" }, + { "application/x-director", "dcr" }, + { "application/x-director", "dir" }, + { "application/x-director", "dxr" }, + { "application/x-dvi", "dvi" }, + { "application/x-gtar", "gtar" }, + { "application/x-gzip", "gz" }, + { "application/x-hdf", "hdf" }, + { "application/x-httpd-cgi", "cgi" }, + { "application/x-koan", "skp" }, + { "application/x-koan", "skd" }, + { "application/x-koan", "skt" }, + { "application/x-koan", "skm" }, + { "application/x-latex", "latex" }, + { "application/x-mif", "mif" }, + { "application/x-netcdf", "nc" }, + { "application/x-netcdf", "cdf" }, + { "application/x-sh", "sh" }, + { "application/x-shar", "shar" }, + { "application/x-stuffit", "sit" }, + { "application/x-sv4cpio", "sv4cpio" }, + { "application/x-sv4crc", "sv4crc" }, + { "application/x-tar", "tar" }, + { "application/x-tcl", "tcl" }, + { "application/x-tex", "tex" }, + { "application/x-texinfo", "texinfo" }, + { "application/x-texinfo", "texi" }, + { "application/x-troff", "t" }, + { "application/x-troff", "tr" }, + { "application/x-troff", "roff" }, + { "application/x-troff-man", "man" }, + { "application/x-troff-me", "me" }, + { "application/x-troff-ms", "ms" }, + { "application/x-ustar", "ustar" }, + { "application/x-wais-source", "src" }, + { "application/zip", "zip" }, + { "audio/basic", "au" }, + { "audio/basic", "snd" }, + { "audio/mpeg", "mpga" }, + { "audio/mpeg", "mp2" }, + { "audio/mpeg", "mp3" }, + { "audio/x-aiff", "aif" }, + { "audio/x-aiff", "aiff" }, + { "audio/x-aiff", "aifc" }, + { "audio/x-pn-realaudio", "ram" }, + { "audio/x-pn-realaudio-plugin", "rpm" }, + { "audio/x-realaudio", "ra" }, + { "audio/x-wav", "wav" }, + { "chemical/x-pdb", "pdb" }, + { "chemical/x-pdb", "xyz" }, + { "image/gif", "gif" }, + { "image/ief", "ief" }, + { "image/jpeg", "jpeg" }, + { "image/jpeg", "jpg" }, + { "image/jpeg", "jpe" }, + { "image/png", "png" }, + { "image/tiff", "tiff" }, + { "image/tiff", "tif" }, + { "image/x-cmu-raster", "ras" }, + { "image/x-portable-anymap", "pnm" }, + { "image/x-portable-bitmap", "pbm" }, + { "image/x-portable-graymap", "pgm" }, + { "image/x-portable-pixmap", "ppm" }, + { "image/x-rgb", "rgb" }, + { "image/x-xbitmap", "xbm" }, + { "image/x-xpixmap", "xpm" }, + { "image/x-xwindowdump", "xwd" }, + { "text/html", "html" }, + { "text/html", "htm" }, + { "text/plain", "txt" }, + { "text/richtext", "rtx" }, + { "text/tab-separated-values", "tsv" }, + { "text/x-setext", "etx" }, + { "text/x-sgml", "sgml" }, + { "text/x-sgml", "sgm" }, + { "video/mpeg", "mpeg" }, + { "video/mpeg", "mpg" }, + { "video/mpeg", "mpe" }, + { "video/quicktime", "qt" }, + { "video/quicktime", "mov" }, + { "video/x-msvideo", "avi" }, + { "video/x-sgi-movie", "movie" }, + { "x-conference/x-cooltalk", "ice" }, + { "x-world/x-vrml", "wrl" }, + { "x-world/x-vrml", "vrml" } + }; + + /** + * The MIME types above are put into this Hashtable for faster lookup. + */ + private static Hashtable mime_types = new Hashtable(150); + + // Static initializer to load MIME types into Hashtable + static + { + for (int i = 0; i < mime_strings.length; i++) + mime_types.put(mime_strings[i][1], mime_strings[i][0]); + } + + /** + * Create a new <code>MimeTypeMapper</code> object. + */ + public MimeTypeMapper() + { + // Do nothing here. + } + + /** + * The method returns the MIME type of the filename passed as an argument. + * The value returned is based on the extension of the filename. The + * default content type returned if this method cannot determine the + * actual content type is "application/octet-stream" + * + * @param filename The name of the file to return the MIME type for + * + * @return The MIME type + */ + public String getContentTypeFor(String filename) + { + int index = filename.lastIndexOf("."); + if (index != -1) + { + if (index == filename.length()) + return "application/octet-stream"; + else + filename = filename.substring(index + 1); + } + + String type = (String) mime_types.get(filename); + if (type == null) + return "application/octet-stream"; + else + return type; + } +} diff --git a/libjava/classpath/java/net/MulticastSocket.java b/libjava/classpath/java/net/MulticastSocket.java new file mode 100644 index 00000000000..03bdf1e77d9 --- /dev/null +++ b/libjava/classpath/java/net/MulticastSocket.java @@ -0,0 +1,486 @@ +/* MulticastSocket.java -- Class for using multicast sockets + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.util.Enumeration; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a multicast UDP socket. A multicast address is a + * class D internet address (one whose most significant bits are 1110). + * A multicast group consists of a multicast address and a well known + * port number. All members of the group listening on that address and + * port will receive all the broadcasts to the group. + * <p> + * Please note that applets are not allowed to use multicast sockets + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (Documentation comments) + * @since 1.1 + * @date May 18, 1999. + */ +public class MulticastSocket extends DatagramSocket +{ + /** + * Create a MulticastSocket that this not bound to any address + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket() throws IOException + { + this(new InetSocketAddress(0)); + } + + /** + * Create a multicast socket bound to the specified port + * + * @param port The port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket(int port) throws IOException + { + this(new InetSocketAddress(port)); + } + + /** + * Create a multicast socket bound to the specified SocketAddress. + * + * @param address The SocketAddress the multicast socket will be bound to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public MulticastSocket(SocketAddress address) throws IOException + { + super((SocketAddress) null); + setReuseAddress(true); + if (address != null) + bind(address); + } + + /** + * Returns the interface being used for multicast packets + * + * @return The multicast interface + * + * @exception SocketException If an error occurs + */ + public InetAddress getInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". This method id + * deprecated. Use <code>getTimeToLive</code> instead. + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by getTimeToLive() + * + * @see MulticastSocket#getTimeToLive() + */ + public byte getTTL() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use getTTL here rather than getTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // getTimeToLive yet. + return getImpl().getTTL(); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public int getTimeToLive() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return getImpl().getTimeToLive(); + } + + /** + * Sets the interface to use for sending multicast packets. + * + * @param addr The new interface to use. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setInterface(InetAddress addr) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, addr); + } + + /** + * Sets the local network interface used to send multicast messages + * + * @param netIf The local network interface used to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#getNetworkInterface() + * + * @since 1.4 + */ + public void setNetworkInterface(NetworkInterface netIf) + throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Enumeration e = netIf.getInetAddresses(); + + if (! e.hasMoreElements()) + throw new SocketException("no network devices found"); + + InetAddress address = (InetAddress) e.nextElement(); + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, address); + } + + /** + * Gets the local network interface which is used to send multicast messages + * + * @return The local network interface to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public NetworkInterface getNetworkInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetAddress address = + (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + NetworkInterface netIf = NetworkInterface.getByInetAddress(address); + + return netIf; + } + + /** + * Disable/Enable local loopback of multicast packets. The option is used by + * the platform's networking code as a hint for setting whether multicast + * data will be looped back to the local socket. + * + * Because this option is a hint, applications that want to verify what + * loopback mode is set to should call #getLoopbackMode + * + * @param disable True to disable loopback mode + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setLoopbackMode(boolean disable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, + Boolean.valueOf(disable)); + } + + /** + * Checks if local loopback mode is enabled + * + * @return true if loopback mode is enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getLoopbackMode() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 1 and 255. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by <code>setTimeToLive</code> + * + * @see MulticastSocket#setTimeToLive(int ttl) + */ + public void setTTL(byte ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use setTTL here rather than setTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // setTimeToLive yet. + getImpl().setTTL(ttl); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 1 and 255. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public void setTimeToLive(int ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (ttl <= 0 || ttl > 255) + throw new IllegalArgumentException("Invalid ttl: " + ttl); + + getImpl().setTimeToLive(ttl); + } + + /** + * Joins the specified multicast group. + * + * @param mcastaddr The address of the group to join + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void joinGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().join(mcastaddr); + } + + /** + * Leaves the specified multicast group + * + * @param mcastaddr The address of the group to leave + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void leaveGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().leave(mcastaddr); + } + + /** + * Joins the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to join + * @param netIf The local network interface to receive the multicast + * messages on or null to defer the interface set by #setInterface or + * #setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("SocketAddress type not supported"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().joinGroup(mcastaddr, netIf); + } + + /** + * Leaves the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to leave + * @param netIf The local networki interface or null to defer to the + * interface set by setInterface or setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().leaveGroup(mcastaddr, netIf); + } + + /** + * Sends a packet of data to a multicast address with a TTL that is + * different from the default TTL on this socket. The default TTL for + * the socket is not changed. + * + * @param packet The packet of data to send + * @param ttl The TTL for this packet + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect or checkMulticast method doesn't allow the operation + * + * @deprecated + */ + public synchronized void send(DatagramPacket packet, byte ttl) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + InetAddress addr = packet.getAddress(); + if (addr.isMulticastAddress()) + s.checkPermission(new SocketPermission(addr.getHostName() + + packet.getPort(), + "accept,connect")); + else + s.checkConnect(addr.getHostAddress(), packet.getPort()); + } + + int oldttl = getImpl().getTimeToLive(); + getImpl().setTimeToLive(((int) ttl) & 0xFF); + getImpl().send(packet); + getImpl().setTimeToLive(oldttl); + } +} diff --git a/libjava/classpath/java/net/NetPermission.java b/libjava/classpath/java/net/NetPermission.java new file mode 100644 index 00000000000..cabe54e065c --- /dev/null +++ b/libjava/classpath/java/net/NetPermission.java @@ -0,0 +1,90 @@ +/* NetPermission.java -- A class for basic miscellaneous network permission + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.security.BasicPermission; + + +/** + * This class is used to model miscellaneous network permissions. It is + * a subclass of <code>BasicPermission</code>. This means that it models a + * "boolean" permission. One that you either have or do not have. Thus + * there is no permitted action list associated with this object. + * + * The following permission names are defined for this class: + * + * <ul> + * <li>setDefaultAuthenticator - Grants the ability to install a facility + * to collect username and password information when requested by a + * web site or proxy server.</li> + * <li>requestPasswordAuthentication - Grants the ability to ask the + * authentication facility for the user's password.</li> + * <li>specifyStreamHandler - Grants the permission to specify the + * stream handler class used when loading from a URL.</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class NetPermission extends BasicPermission +{ + static final long serialVersionUID = -8343910153355041693L; + + /** + * Initializes a new instance of <code>NetPermission</code> with the + * specified name. + * + * @param name The name of this permission. + */ + public NetPermission(String name) + { + super(name); + } + + /** + * Initializes a new instance of <code>NetPermission</code> with the + * specified name and perms. Note that the perms field is irrelevant and is + * ignored. This constructor should never need to be used. + * + * @param name The name of this permission + * @param perms The permitted actions of this permission (ignored) + */ + public NetPermission(String name, String perms) + { + super(name); + } +} diff --git a/libjava/classpath/java/net/NetworkInterface.java b/libjava/classpath/java/net/NetworkInterface.java new file mode 100644 index 00000000000..eccd2dac9fb --- /dev/null +++ b/libjava/classpath/java/net/NetworkInterface.java @@ -0,0 +1,259 @@ +/* NetworkInterface.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.util.Enumeration; +import java.util.Vector; + +/** + * This class models a network interface on the host computer. A network + * interface contains a name (typically associated with a specific + * hardware adapter) and a list of addresses that are bound to it. + * For example, an ethernet interface may be named "eth0" and have the + * address 192.168.1.101 assigned to it. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public final class NetworkInterface +{ + private String name; + private Vector inetAddresses; + + NetworkInterface(String name, InetAddress address) + { + this.name = name; + this.inetAddresses = new Vector(1, 1); + this.inetAddresses.add(address); + } + + NetworkInterface(String name, InetAddress[] addresses) + { + this.name = name; + this.inetAddresses = new Vector(addresses.length, 1); + + for (int i = 0; i < addresses.length; i++) + this.inetAddresses.add(addresses[i]); + } + + /** + * Returns the name of the network interface + * + * @return The name of the interface. + */ + public String getName() + { + return name; + } + + /** + * Returns all available addresses of the network interface + * + * If a @see SecurityManager is available all addresses are checked + * with @see SecurityManager::checkConnect() if they are available. + * Only <code>InetAddresses</code> are returned where the security manager + * doesn't throw an exception. + * + * @return An enumeration of all addresses. + */ + public Enumeration getInetAddresses() + { + SecurityManager s = System.getSecurityManager(); + + if (s == null) + return inetAddresses.elements(); + + Vector tmpInetAddresses = new Vector(1, 1); + + for (Enumeration addresses = inetAddresses.elements(); + addresses.hasMoreElements();) + { + InetAddress addr = (InetAddress) addresses.nextElement(); + try + { + s.checkConnect(addr.getHostAddress(), 58000); + tmpInetAddresses.add(addr); + } + catch (SecurityException e) + { + // Ignore. + } + } + + return tmpInetAddresses.elements(); + } + + /** + * Returns the display name of the interface + * + * @return The display name of the interface + */ + public String getDisplayName() + { + return name; + } + + /** + * Returns an network interface by name + * + * @param name The name of the interface to return + * + * @return a <code>NetworkInterface</code> object representing the interface, + * or null if there is no interface with that name. + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified name is null + */ + public static NetworkInterface getByName(String name) + throws SocketException + { + Vector networkInterfaces = VMNetworkInterface.getInterfaces(); + + for (Enumeration e = networkInterfaces.elements(); e.hasMoreElements();) + { + NetworkInterface tmp = (NetworkInterface) e.nextElement(); + + if (name.equals(tmp.getName())) + return tmp; + } + + // No interface with the given name found. + return null; + } + + /** + * Return a network interface by its address + * + * @param addr The address of the interface to return + * + * @return the interface, or <code>null</code> if none found + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified addess is null + */ + public static NetworkInterface getByInetAddress(InetAddress addr) + throws SocketException + { + Vector networkInterfaces = VMNetworkInterface.getInterfaces(); + + for (Enumeration interfaces = networkInterfaces.elements(); + interfaces.hasMoreElements();) + { + NetworkInterface tmp = (NetworkInterface) interfaces.nextElement(); + + for (Enumeration addresses = tmp.inetAddresses.elements(); + addresses.hasMoreElements();) + { + if (addr.equals((InetAddress) addresses.nextElement())) + return tmp; + } + } + + throw new SocketException("no network interface is bound to such an IP address"); + } + + /** + * Return an <code>Enumeration</code> of all available network interfaces + * + * @return all interfaces + * + * @exception SocketException If an error occurs + */ + public static Enumeration getNetworkInterfaces() throws SocketException + { + Vector networkInterfaces = VMNetworkInterface.getInterfaces(); + + if (networkInterfaces.isEmpty()) + return null; + + return networkInterfaces.elements(); + } + + /** + * Checks if the current instance is equal to obj + * + * @param obj The object to compare with + * + * @return <code>true</code> if equal, <code>false</code> otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof NetworkInterface)) + return false; + + NetworkInterface tmp = (NetworkInterface) obj; + + return (name.equals(tmp.name) && inetAddresses.equals(tmp.inetAddresses)); + } + + /** + * Returns the hashcode of the current instance + * + * @return the hashcode + */ + public int hashCode() + { + // FIXME: hash correctly + return name.hashCode() + inetAddresses.hashCode(); + } + + /** + * Returns a string representation of the interface + * + * @return the string + */ + public String toString() + { + // FIXME: check if this is correct + String result; + String separator = System.getProperty("line.separator"); + + result = + "name: " + getDisplayName() + " (" + getName() + ") addresses:" + + separator; + + for (Enumeration e = inetAddresses.elements(); e.hasMoreElements();) + { + InetAddress address = (InetAddress) e.nextElement(); + result += address.toString() + ";" + separator; + } + + return result; + } +} diff --git a/libjava/classpath/java/net/NoRouteToHostException.java b/libjava/classpath/java/net/NoRouteToHostException.java new file mode 100644 index 00000000000..48c3a8e604e --- /dev/null +++ b/libjava/classpath/java/net/NoRouteToHostException.java @@ -0,0 +1,74 @@ +/* NoRouteToHostException.java -- Cannot connect to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that there is no TCP/IP route to the requested + * host. This is often due to a misconfigured routing table. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoRouteToHostException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1897550894873493790L; + + /** + * Create an instance without a descriptive error message. + */ + public NoRouteToHostException() + { + } + + /** + * Create an instance with a descriptive error message, such as the text + * from strerror(3). + * + * @param message a message describing the error that occurred + */ + public NoRouteToHostException(String message) + { + super(message); + } +} // class NoRouteToHostException diff --git a/libjava/classpath/java/net/PasswordAuthentication.java b/libjava/classpath/java/net/PasswordAuthentication.java new file mode 100644 index 00000000000..1d4ec89611d --- /dev/null +++ b/libjava/classpath/java/net/PasswordAuthentication.java @@ -0,0 +1,92 @@ +/* PasswordAuthentication.java -- Container class for username/password pairs + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This class serves a container for username/password pairs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @since 1.2 + */ +public final class PasswordAuthentication +{ + /** + * The username + */ + private String username; + + /** + * The password + */ + private char[] password; + + /** + * Creates a new <code>PasswordAuthentication</code> object from the + * specified username and password. + * + * @param username The username for this object + * @param password The password for this object + */ + public PasswordAuthentication(String username, char[] password) + { + this.username = username; + this.password = password; + } + + /** + * Returns the username associated with this object + * + * @return The username + */ + public String getUserName() + { + return username; + } + + /** + * Returns the password associated with this object + * + * @return The password + */ + public char[] getPassword() + { + return password; + } +} diff --git a/libjava/classpath/java/net/PortUnreachableException.java b/libjava/classpath/java/net/PortUnreachableException.java new file mode 100644 index 00000000000..49a8c9ea103 --- /dev/null +++ b/libjava/classpath/java/net/PortUnreachableException.java @@ -0,0 +1,72 @@ +/* PortUnreachableException.java -- received an ICMP port unreachable datagram + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception signals that an ICMP port unreachable datagram has been + * received. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class PortUnreachableException extends SocketException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8462541992376507323L; + + /** + * Create a new instance without a descriptive error message. + */ + public PortUnreachableException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public PortUnreachableException(String message) + { + super(message); + } +} // class PortUnreachableException diff --git a/libjava/classpath/java/net/ProtocolException.java b/libjava/classpath/java/net/ProtocolException.java new file mode 100644 index 00000000000..27718a9793d --- /dev/null +++ b/libjava/classpath/java/net/ProtocolException.java @@ -0,0 +1,75 @@ +/* ProtocolException.java -- a low level protocol error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that some sort of low level protocol + * exception occurred. Look in the descriptive message (if any) for + * details on what went wrong. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ProtocolException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6098449442062388080L; + + /** + * Create a new instance without a descriptive error message. + */ + public ProtocolException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public ProtocolException(String message) + { + super(message); + } +} // class ProtocolException diff --git a/libjava/classpath/java/net/STATUS b/libjava/classpath/java/net/STATUS new file mode 100644 index 00000000000..25dff963e06 --- /dev/null +++ b/libjava/classpath/java/net/STATUS @@ -0,0 +1,48 @@ +X ContentHandlerFactory +X FileNameMap +X SocketImplFactory +X URLStreamHandlerFactory +* Authenticator +* ContentHandler ++ DatagramPacket ++ DatagramSocket ++ DatagramSocketImpl ++ HttpURLConnection ++ InetAddress +* JarURLConnection ++ MulticastSocket +* NetPermission +* PasswordAuthentication ++ PlainDatagramSocketImpl ++ PlainSocketImpl (internal) ++ ServerSocket ++ Socket ++ SocketImpl ++ SocketInputStream (internal) ++ SocketOptions (internal) ++ SocketOutputStream (internal) +* SocketPermission ++ URL + URLClassLoader ++ URLConnection +* URLEncoder ++ URLStreamHandler +X BindException +X ConnectException +X MalformedURLException +X NoRouteToHostException +X ProtocolException +X SocketException +X UnknownHostException +X UnknownServiceException + +--------------- ++ Native InetAddress ++ Native SocketImpl ++ Native DatagramSocketImpl ++ Protocol Handler for HTTP + Protocol Handler for FTP ++ ContentHandler for text + ContentHandler for gif + ContentHandler for jpeg + diff --git a/libjava/classpath/java/net/ServerSocket.java b/libjava/classpath/java/net/ServerSocket.java new file mode 100644 index 00000000000..f73c7482aa5 --- /dev/null +++ b/libjava/classpath/java/net/ServerSocket.java @@ -0,0 +1,599 @@ +/* ServerSocket.java -- Class for implementing server side sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ServerSocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models server side sockets. The basic model is that the + * server socket is created and bound to some well known port. It then + * listens for and accepts connections. At that point the client and + * server sockets are ready to communicate with one another utilizing + * whatever application layer protocol they desire. + * + * As with the <code>Socket</code> class, most instance methods of this class + * simply redirect their calls to an implementation class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class ServerSocket +{ + /** + * This is the user defined SocketImplFactory, if one is supplied + */ + private static SocketImplFactory factory; + + /** + * This is the SocketImp object to which most instance methods in this + * class are redirected + */ + private SocketImpl impl; + + /** + * We need to retain the local address even after the socket is closed. + */ + private InetSocketAddress local; + + /* + * This constructor is only used by java.nio. + */ + + // FIXME: Workaround a bug in gcj. + //ServerSocket (PlainSocketImpl impl) throws IOException + ServerSocket(SocketImpl impl) throws IOException + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.impl.create(true); + } + + /* + * This method is only used by java.nio. + */ + + // FIXME: Workaround a bug in gcj. + //PlainSocketImpl getImpl() + SocketImpl getImpl() + { + return impl; + } + + /** + * Constructor that simply sets the implementation. + * + * @exception IOException If an error occurs + * + * @specnote This constructor is public since JDK 1.4 + */ + public ServerSocket() throws IOException + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + + impl.create(true); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to 50. + * + * @param port The port number to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port) throws IOException + { + this(port, 50); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * arg2. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port, int backlog) throws IOException + { + this(port, backlog, null); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * backlog. The third argument specifies a particular local address to + * bind t or null to bind to all local address. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * @param bindAddr The address to bind to, or null to bind to all addresses + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.1 + */ + public ServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException + { + this(); + + // bind/listen socket + bind(new InetSocketAddress(bindAddr, port), backlog); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint) throws IOException + { + bind(endpoint, 50); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint, int backlog) + throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (! (endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("Address type not supported"); + + InetSocketAddress tmp = (InetSocketAddress) endpoint; + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(tmp.getPort()); + + InetAddress addr = tmp.getAddress(); + + // Initialize addr with 0.0.0.0. + if (addr == null) + addr = InetAddress.ANY_IF; + + try + { + impl.bind(addr, tmp.getPort()); + impl.listen(backlog); + local = new InetSocketAddress( + (InetAddress) impl.getOption(SocketOptions.SO_BINDADDR), + impl.getLocalPort()); + } + catch (IOException exception) + { + close(); + throw exception; + } + catch (RuntimeException exception) + { + close(); + throw exception; + } + catch (Error error) + { + close(); + throw error; + } + } + + /** + * This method returns the local address to which this socket is bound + * + * @return The socket's local address + */ + public InetAddress getInetAddress() + { + if (local == null) + return null; + + return local.getAddress(); + } + + /** + * This method returns the local port number to which this socket is bound + * + * @return The socket's port number + */ + public int getLocalPort() + { + if (local == null) + return -1; + + return local.getPort(); + } + + /** + * Returns the local socket address + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + return local; + } + + /** + * Accepts a new connection and returns a connected <code>Socket</code> + * instance representing that connection. This method will block until a + * connection is available. + * + * @return socket object for the just accepted connection + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If a timeout was previously set with + * setSoTimeout and the timeout has been reached + */ + public Socket accept() throws IOException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(impl.getLocalPort()); + + Socket socket = new Socket(); + + try + { + implAccept(socket); + } + catch (IOException e) + { + try + { + socket.close(); + } + catch (IOException e2) + { + // Ignore. + } + + throw e; + } + + return socket; + } + + /** + * This protected method is used to help subclasses override + * <code>ServerSocket.accept()</code>. The passed in socket will be + * connected when this method returns. + * + * @param socket The socket that is used for the accepted connection + * + * @exception IOException If an error occurs + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.1 + */ + protected final void implAccept(Socket socket) throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + impl.accept(socket.impl); + socket.implCreated = true; + } + + /** + * Closes this socket and stops listening for connections + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + if (isClosed()) + return; + + impl.close(); + impl = null; + + if (getChannel() != null) + getChannel().close(); + } + + /** + * Returns the unique <code>ServerSocketChannel</code> object + * associated with this socket, if any. + * + * <p>The socket only has a <code>ServerSocketChannel</code> if its created + * by <code>ServerSocketChannel.open()</code>.</p> + * + * @return the associated socket channel, null if none exists + * + * @since 1.4 + */ + public ServerSocketChannel getChannel() + { + return null; + } + + /** + * Returns true when the socket is bound, otherwise false + * + * @return true if socket is bound, false otherwise + * + * @since 1.4 + */ + public boolean isBound() + { + return local != null; + } + + /** + * Returns true if the socket is closed, otherwise false + * + * @return true if socket is closed, false otherwise + * + * @since 1.4 + */ + public boolean isClosed() + { + return impl == null; + } + + /** + * Sets the value of SO_TIMEOUT. A value of 0 implies that SO_TIMEOUT is + * disabled (ie, operations never time out). This is the number of + * milliseconds a socket operation can block before an + * InterruptedIOException is thrown. + * + * @param timeout The new SO_TIMEOUT value + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + public void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + impl.setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * Retrieves the current value of the SO_TIMEOUT setting. A value of 0 + * implies that SO_TIMEOUT is disabled (ie, operations never time out). + * This is the number of milliseconds a socket operation can block before + * an InterruptedIOException is thrown. + * + * @return The value of SO_TIMEOUT + * + * @exception IOException If an error occurs + * + * @since 1.1 + */ + public int getSoTimeout() throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object timeout = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (! (timeout instanceof Integer)) + throw new IOException("Internal Error"); + + return ((Integer) timeout).intValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param on true if SO_REUSEADDR should be enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return true if SO_REUSEADDR is set, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object reuseaddr = impl.getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.4 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + impl.setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.4 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object buf = impl.getOption(SocketOptions.SO_RCVBUF); + + if (! (buf instanceof Integer)) + throw new SocketException("Internal Error: Unexpected type"); + + return ((Integer) buf).intValue(); + } + + /** + * Returns the value of this socket as a <code>String</code>. + * + * @return This socket represented as a <code>String</code>. + */ + public String toString() + { + if (! isBound()) + return "ServerSocket[unbound]"; + + return ("ServerSocket[addr=" + getInetAddress() + ",port=" + + impl.getPort() + ",localport=" + impl.getLocalPort() + "]"); + } + + /** + * Sets the <code>SocketImplFactory</code> for all + * <code>ServerSocket</code>'s. This may only be done + * once per virtual machine. Subsequent attempts will generate an + * exception. Note that a <code>SecurityManager</code> check is made prior + * to setting the factory. If insufficient privileges exist to set the + * factory, an exception will be thrown + * + * @param fac the factory to set + * + * @exception SecurityException If this operation is not allowed by the + * <code>SecurityManager</code>. + * @exception SocketException If the factory object is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketFactory(SocketImplFactory fac) + throws IOException + { + factory = fac; + } +} diff --git a/libjava/classpath/java/net/Socket.java b/libjava/classpath/java/net/Socket.java new file mode 100644 index 00000000000..9432a6be1d8 --- /dev/null +++ b/libjava/classpath/java/net/Socket.java @@ -0,0 +1,1284 @@ +/* Socket.java -- Client socket implementation + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models a client site socket. A socket is a TCP/IP endpoint + * for network communications conceptually similar to a file handle. + * <p> + * This class does not actually do any work. Instead, it redirects all of + * its calls to a socket implementation object which implements the + * <code>SocketImpl</code> interface. The implementation class is + * instantiated by factory class that implements the + * <code>SocketImplFactory interface</code>. A default + * factory is provided, however the factory may be set by a call to + * the <code>setSocketImplFactory</code> method. Note that this may only be + * done once per virtual machine. If a subsequent attempt is made to set the + * factory, a <code>SocketException</code> will be thrown. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class Socket +{ + /** + * This is the user SocketImplFactory for this class. If this variable is + * null, a default factory is used. + */ + static SocketImplFactory factory; + + /** + * The implementation object to which calls are redirected + */ + // package-private because ServerSocket.implAccept() needs to access it. + SocketImpl impl; + + /** + * True if socket implementation was created by calling their + * create() method. + */ + // package-private because ServerSocket.implAccept() needs to access it. + boolean implCreated; + + /** + * True if the socket is bound. + */ + private boolean bound; + + /** + * True if input is shutdown. + */ + private boolean inputShutdown; + + /** + * True if output is shutdown. + */ + private boolean outputShutdown; + + /** + * Initializes a new instance of <code>Socket</code> object without + * connecting to a remote host. This useful for subclasses of socket that + * might want this behavior. + * + * @specnote This constructor is public since JDK 1.4 + * @since 1.1 + */ + public Socket() + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + } + + /** + * Initializes a new instance of <code>Socket</code> object without + * connecting to a remote host. This is useful for subclasses of socket + * that might want this behavior. + * <p> + * Additionally, this socket will be created using the supplied + * implementation class instead the default class or one returned by a + * factory. If this value is <code>null</code>, the default Socket + * implementation is used. + * + * @param impl The <code>SocketImpl</code> to use for this + * <code>Socket</code> + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + protected Socket(SocketImpl impl) throws SocketException + { + if (impl == null) + this.impl = new PlainSocketImpl(); + else + this.impl = impl; + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * hostname and port specified as arguments. + * + * @param host The name of the host to connect to + * @param port The port number to connect to + * + * @exception UnknownHostException If the hostname cannot be resolved to a + * network address. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(String host, int port) + throws UnknownHostException, IOException + { + this(InetAddress.getByName(host), port, null, 0, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments. + * + * @param address The address to connect to + * @param port The port number to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(InetAddress address, int port) throws IOException + { + this(address, port, null, 0, true); + } + + /** + * Initializes a new instance of <code>Socket</code> that connects to the + * named host on the specified port and binds to the specified local address + * and port. + * + * @param host The name of the remote host to connect to. + * @param port The remote port to connect to. + * @param localAddr The local address to bind to. + * @param localPort The local port to bind to. + * + * @exception SecurityException If the <code>SecurityManager</code> + * exists and does not allow a connection to the specified host/port or + * binding to the specified local host/port. + * @exception IOException If a connection error occurs. + * + * @since 1.1 + */ + public Socket(String host, int port, InetAddress localAddr, int localPort) + throws IOException + { + this(InetAddress.getByName(host), port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments, plus binds to the + * specified local address and port. + * + * @param address The remote address to connect to + * @param port The remote port to connect to + * @param localAddr The local address to connect to + * @param localPort The local port to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @since 1.1 + */ + public Socket(InetAddress address, int port, InetAddress localAddr, + int localPort) throws IOException + { + this(address, port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * hostname and port specified as arguments. If the stream argument is set + * to <code>true</code>, then a stream socket is created. If it is + * <code>false</code>, a datagram socket is created. + * + * @param host The name of the host to connect to + * @param port The port to connect to + * @param stream <code>true</code> for a stream socket, <code>false</code> + * for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the <code>DatagramSocket</code> class to create + * datagram oriented sockets. + */ + public Socket(String host, int port, boolean stream) + throws IOException + { + this(InetAddress.getByName(host), port, null, 0, stream); + } + + /** + * Initializes a new instance of <code>Socket</code> and connects to the + * address and port number specified as arguments. If the stream param is + * <code>true</code>, a stream socket will be created, otherwise a datagram + * socket is created. + * + * @param host The address to connect to + * @param port The port number to connect to + * @param stream <code>true</code> to create a stream socket, + * <code>false</code> to create a datagram socket. + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the <code>DatagramSocket</code> class to create + * datagram oriented sockets. + */ + public Socket(InetAddress host, int port, boolean stream) + throws IOException + { + this(host, port, null, 0, stream); + } + + /** + * This constructor is where the real work takes place. Connect to the + * specified address and port. Use default local values if not specified, + * otherwise use the local host and port passed in. Create as stream or + * datagram based on "stream" argument. + * <p> + * + * @param raddr The remote address to connect to + * @param rport The remote port to connect to + * @param laddr The local address to connect to + * @param lport The local port to connect to + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + private Socket(InetAddress raddr, int rport, InetAddress laddr, int lport, + boolean stream) throws IOException + { + this(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(raddr.getHostName(), rport); + + // bind socket + SocketAddress bindaddr = + laddr == null ? null : new InetSocketAddress(laddr, lport); + bind(bindaddr); + + // connect socket + connect(new InetSocketAddress(raddr, rport)); + + // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port, + // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as + // that default. JDK 1.2 doc infers not to do a bind. + } + + private SocketImpl getImpl() throws SocketException + { + try + { + if (! implCreated) + { + impl.create(true); + implCreated = true; + } + } + catch (IOException e) + { + throw new SocketException(e.getMessage()); + } + + return impl; + } + + /** + * Binds the socket to the givent local address/port + * + * @param bindpoint The address/port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * @exception IllegalArgumentException If the address type is not supported + * + * @since 1.4 + */ + public void bind(SocketAddress bindpoint) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // XXX: JDK 1.4.1 API documentation says that if bindpoint is null the + // socket will be bound to an ephemeral port and a valid local address. + if (bindpoint == null) + bindpoint = new InetSocketAddress(InetAddress.ANY_IF, 0); + + if (! (bindpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException(); + + InetSocketAddress tmp = (InetSocketAddress) bindpoint; + + // bind to address/port + try + { + getImpl().bind(tmp.getAddress(), tmp.getPort()); + bound = true; + } + catch (IOException exception) + { + close(); + throw exception; + } + catch (RuntimeException exception) + { + close(); + throw exception; + } + catch (Error error) + { + close(); + throw error; + } + } + + /** + * Connects the socket with a remote address. + * + * @param endpoint The address to connect to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the addess type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint) throws IOException + { + connect(endpoint, 0); + } + + /** + * Connects the socket with a remote address. A timeout of zero is + * interpreted as an infinite timeout. The connection will then block + * until established or an error occurs. + * + * @param endpoint The address to connect to + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the address type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If the timeout is reached + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint, int timeout) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + if (! isBound()) + bind(null); + + try + { + getImpl().connect(endpoint, timeout); + } + catch (IOException exception) + { + close(); + throw exception; + } + catch (RuntimeException exception) + { + close(); + throw exception; + } + catch (Error error) + { + close(); + throw error; + } + } + + /** + * Returns the address of the remote end of the socket. If this socket + * is not connected, then <code>null</code> is returned. + * + * @return The remote address this socket is connected to + */ + public InetAddress getInetAddress() + { + if (! isConnected()) + return null; + + try + { + return getImpl().getInetAddress(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return null; + } + + /** + * Returns the local address to which this socket is bound. If this socket + * is not connected, then a wildcard address, for which + * @see isAnyLocalAddress() is <code>true</code>, is returned. + * + * @return The local address + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return InetAddress.ANY_IF; + + InetAddress addr = null; + + try + { + addr = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + } + catch (SocketException e) + { + // (hopefully) shouldn't happen + // throw new java.lang.InternalError + // ("Error in PlainSocketImpl.getOption"); + return null; + } + + // FIXME: According to libgcj, checkConnect() is supposed to be called + // before performing this operation. Problems: 1) We don't have the + // addr until after we do it, so we do a post check. 2). The docs I + // see don't require this in the Socket case, only DatagramSocket, but + // we'll assume they mean both. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(addr.getHostName(), getLocalPort()); + + return addr; + } + + /** + * Returns the port number of the remote end of the socket connection. If + * this socket is not connected, then 0 is returned. + * + * @return The remote port this socket is connected to + */ + public int getPort() + { + if (! isConnected()) + return 0; + + try + { + return getImpl().getPort(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return 0; + } + + /** + * Returns the local port number to which this socket is bound. If this + * socket is not connected, then -1 is returned. + * + * @return The local port + */ + public int getLocalPort() + { + if (! isBound()) + return -1; + + try + { + if (getImpl() != null) + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + } + + return -1; + } + + /** + * Returns local socket address. + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + InetAddress addr = getLocalAddress(); + + try + { + return new InetSocketAddress(addr, getImpl().getLocalPort()); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + } + + /** + * Returns the remote socket address. + * + * @return the remote socket address, null of not connected + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + try + { + return new InetSocketAddress(getImpl().getInetAddress(), + getImpl().getPort()); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + return null; + } + } + + /** + * Returns an InputStream for reading from this socket. + * + * @return The InputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public InputStream getInputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getInputStream(); + } + + /** + * Returns an OutputStream for writing to this socket. + * + * @return The OutputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public OutputStream getOutputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getOutputStream(); + } + + /** + * Sets the TCP_NODELAY option on the socket. + * + * @param on true to enable, false to disable + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public void setTcpNoDelay(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); + } + + /** + * Tests whether or not the TCP_NODELAY option is set on the socket. + * Returns true if enabled, false if disabled. When on it disables the + * Nagle algorithm which means that packets are always send immediatly and + * never merged together to reduce network trafic. + * + * @return Whether or not TCP_NODELAY is set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public boolean getTcpNoDelay() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object on = getImpl().getOption(SocketOptions.TCP_NODELAY); + + if (on instanceof Boolean) + return (((Boolean) on).booleanValue()); + else + throw new SocketException("Internal Error"); + } + + /** + * Sets the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. The linger interval is specified in hundreths of a second + * (platform specific?) + * + * @param on true to enable SO_LINGER, false to disable + * @param linger The SO_LINGER timeout in hundreths of a second or -1 if + * SO_LINGER not set. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If linger is negative + * + * @since 1.1 + */ + public void setSoLinger(boolean on, int linger) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (on) + { + if (linger < 0) + throw new IllegalArgumentException("SO_LINGER must be >= 0"); + + if (linger > 65535) + linger = 65535; + + getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger)); + } + else + getImpl().setOption(SocketOptions.SO_LINGER, Boolean.valueOf(false)); + } + + /** + * Returns the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. This method either returns the timeouts (in hundredths of + * of a second (platform specific?)) if SO_LINGER is set, or -1 if + * SO_LINGER is not set. + * + * @return The SO_LINGER timeout in hundreths of a second or -1 + * if SO_LINGER not set + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public int getSoLinger() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object linger = getImpl().getOption(SocketOptions.SO_LINGER); + + if (linger instanceof Integer) + return (((Integer) linger).intValue()); + else + return -1; + } + + /** + * Sends urgent data through the socket + * + * @param data The data to send. + * Only the lowest eight bits of data are sent + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + public void sendUrgentData(int data) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().sendUrgentData(data); + } + + /** + * Enables/disables the SO_OOBINLINE option + * + * @param on True if SO_OOBLINE should be enabled + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setOOBInline(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on)); + } + + /** + * Returns the current setting of the SO_OOBINLINE option for this socket + * + * @return True if SO_OOBINLINE is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getOOBInline() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_OOBINLINE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Sets the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in milliseconds. + * + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + } + + /** + * Returns the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in thousandths of a second (implementation specific?). + * + * @return The length of the timeout in thousandth's of a second or 0 + * if not set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object timeout = getImpl().getOption(SocketOptions.SO_TIMEOUT); + if (timeout instanceof Integer) + return (((Integer) timeout).intValue()); + else + return 0; + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_SNDBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, new Integer(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs or socket not connected + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, new Integer(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the socket level socket option + * SO_KEEPALIVE. + * + * @param on True if SO_KEEPALIVE should be enabled + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public void setKeepAlive(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on)); + } + + /** + * This method returns the value of the socket level socket option + * SO_KEEPALIVE. + * + * @return The setting + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public boolean getKeepAlive() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_KEEPALIVE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Closes the socket. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + if (isClosed()) + return; + + getImpl().close(); + impl = null; + bound = false; + + if (getChannel() != null) + getChannel().close(); + } + + /** + * Converts this <code>Socket</code> to a <code>String</code>. + * + * @return The <code>String</code> representation of this <code>Socket</code> + */ + public String toString() + { + try + { + if (isConnected()) + return ("Socket[addr=" + getImpl().getInetAddress() + ",port=" + + getImpl().getPort() + ",localport=" + + getImpl().getLocalPort() + "]"); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return "Socket[unconnected]"; + } + + /** + * Sets the <code>SocketImplFactory</code>. This may be done only once per + * virtual machine. Subsequent attempts will generate a + * <code>SocketException</code>. Note that a <code>SecurityManager</code> + * check is made prior to setting the factory. If + * insufficient privileges exist to set the factory, then an + * <code>IOException</code> will be thrown. + * + * @param fac the factory to set + * + * @exception SecurityException If the <code>SecurityManager</code> does + * not allow this operation. + * @exception SocketException If the SocketImplFactory is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketImplFactory(SocketImplFactory fac) + throws IOException + { + // See if already set + if (factory != null) + throw new SocketException("SocketImplFactory already defined"); + + // Check permissions + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + if (fac == null) + throw new SocketException("SocketImplFactory cannot be null"); + + factory = fac; + } + + /** + * Closes the input side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownInput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownInput(); + inputShutdown = true; + } + + /** + * Closes the output side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownOutput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownOutput(); + outputShutdown = true; + } + + /** + * Returns the socket channel associated with this socket. + * + * @return the associated socket channel, + * null if no associated channel exists + * + * @since 1.4 + */ + public SocketChannel getChannel() + { + return null; + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return True if SO_REUSEADDR is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object reuseaddr = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param reuseAddress true if SO_REUSEADDR should be enabled, + * false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean reuseAddress) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, + Boolean.valueOf(reuseAddress)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @exception SocketException If an error occurs + * + * @see Socket#setTrafficClass(int tc) + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object obj = getImpl().getOption(SocketOptions.IP_TOS); + + if (obj instanceof Integer) + return ((Integer) obj).intValue(); + else + throw new SocketException("Unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see Socket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, new Integer(tc)); + } + + /** + * Checks if the socket is connected + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + try + { + if (getImpl() == null) + return false; + + return getImpl().getInetAddress() != null; + } + catch (SocketException e) + { + return false; + } + } + + /** + * Checks if the socket is already bound. + * + * @return True if socket is bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + return bound; + } + + /** + * Checks if the socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + return impl == null; + } + + /** + * Checks if the socket's input stream is shutdown + * + * @return True if input is shut down. + * + * @since 1.4 + */ + public boolean isInputShutdown() + { + return inputShutdown; + } + + /** + * Checks if the socket's output stream is shutdown + * + * @return True if output is shut down. + * + * @since 1.4 + */ + public boolean isOutputShutdown() + { + return outputShutdown; + } +} diff --git a/libjava/classpath/java/net/SocketAddress.java b/libjava/classpath/java/net/SocketAddress.java new file mode 100644 index 00000000000..48ab010097f --- /dev/null +++ b/libjava/classpath/java/net/SocketAddress.java @@ -0,0 +1,63 @@ +/* SocketAddress.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.Serializable; + + +/** + * Abstract base class for InetSocketAddress. + * InetSocketAddress is to my knowledge the only derived + * class. [Ronald] + * + * @since 1.4 + */ +public abstract class SocketAddress implements Serializable +{ + /** + * Compatible with JDK 1.4+ + */ + static final long serialVersionUID = 5215720748342549866L; + + /** + * Initializes the socket address. + */ + public SocketAddress() + { + } +} diff --git a/libjava/classpath/java/net/SocketException.java b/libjava/classpath/java/net/SocketException.java new file mode 100644 index 00000000000..37b2f6fba43 --- /dev/null +++ b/libjava/classpath/java/net/SocketException.java @@ -0,0 +1,75 @@ +/* SocketException.java -- An exception occurred while performing a socket op + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a generic error occurred related to an + * operation on a socket. Check the descriptive message (if any) for + * details on the nature of this error + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class SocketException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5935874303556886934L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketException(String message) + { + super(message); + } +} // class SocketException diff --git a/libjava/classpath/java/net/SocketImpl.java b/libjava/classpath/java/net/SocketImpl.java new file mode 100644 index 00000000000..77f470be332 --- /dev/null +++ b/libjava/classpath/java/net/SocketImpl.java @@ -0,0 +1,321 @@ +/* SocketImpl.java -- Abstract socket implementation class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Believed complete and correct. + */ + +/** + * This abstract class serves as the parent class for socket implementations. + * The implementation class serves an intermediary to native routines that + * perform system specific socket operations. + * <p> + * A default implementation is provided by the system, but this can be + * changed via installing a <code>SocketImplFactory</code> (through a call + * to the static method <code>Socket.setSocketImplFactory</code>). A + * subclass of <code>Socket</code> can also pass in a <code>SocketImpl</code> + * to the <code>Socket(SocketImpl)</code> constructor to use an + * implementation different from the system default without installing + * a factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class SocketImpl implements SocketOptions +{ + /** + * The address of the remote end of the socket connection + */ + protected InetAddress address; + + /** + * A FileDescriptor object representing this socket connection. + */ + protected FileDescriptor fd; + + /** + * The port number the socket is bound to locally + */ + protected int localport = -1; + + /** + * The port number of the remote end of the socket connection + */ + protected int port; + + /** + * Default, no-argument constructor for use by subclasses. + */ + public SocketImpl() + { + } + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + */ + protected abstract void create(boolean stream) throws IOException; + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param host The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(String host, int port) + throws IOException; + + /** + * Connects to the remote address and port specified as arguments. + * + * @param host The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(InetAddress host, int port) + throws IOException; + + /** + * Connects to the socket to the host specified in address. This + * method blocks until successful connected or the timeout occurs. + * A timeout of zero means no timout. + * + * @param address Data of remote host + * @param timeout time to wait to stop connecting + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void connect(SocketAddress address, int timeout) + throws IOException; + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. + * <p> + * Note that it is unspecified how to bind to all interfaces on the localhost + * (INADDR_ANY). + * + * @param host The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ + protected abstract void bind(InetAddress host, int port) + throws IOException; + + /** + * Starts listening for connections on a socket. The backlog parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + */ + protected abstract void listen(int backlog) throws IOException; + + /** + * Accepts a connection on this socket. + * + * @param s The implementation object for the accepted connection. + * + * @exception IOException If an error occurs + */ + protected abstract void accept(SocketImpl s) throws IOException; + + /** + * Returns an <code>InputStream</code> object for reading from this socket. + * + * @return An <code>InputStream</code> for reading from this socket. + * + * @exception IOException If an error occurs + */ + protected abstract InputStream getInputStream() throws IOException; + + /** + * Returns an <code>OutputStream</code> object for writing to this socket + * + * @return An <code>OutputStream</code> for writing to this socket. + * + * @exception IOException If an error occurs. + */ + protected abstract OutputStream getOutputStream() throws IOException; + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ + protected abstract int available() throws IOException; + + /** + * Closes the socket. This will normally cause any resources, such as the + * InputStream, OutputStream and associated file descriptors to be freed. + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ + protected abstract void close() throws IOException; + + /** + * Returns the FileDescriptor objects for this socket. + * + * @return A FileDescriptor for this socket. + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the remote address this socket is connected to + * + * @return The remote address + */ + protected InetAddress getInetAddress() + { + return address; + } + + /** + * Returns the remote port this socket is connected to + * + * @return The remote port + */ + protected int getPort() + { + return port; + } + + /** + * Returns true or false when this socket supports sending urgent data + * or not. + * + * @return true if the socket implementation supports sending urgent data, + * false otherwise + * + * @since 1.4 + */ + protected boolean supportsUrgentData() + { + // This method has to be overwritten by socket classes that support + // sending urgend data. + return false; + } + + /** + * Sends one byte of urgent data to the socket. + * + * @param data The byte to send, the low eight bits of it + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void sendUrgentData(int data) throws IOException; + + /** + * Returns the local port this socket is bound to + * + * @return The local port + */ + protected int getLocalPort() + { + return localport; + } + + /** + * Returns a <code>String</code> representing the remote host and port of + * this socket. + * + * @return A <code>String</code> for this socket. + */ + public String toString() + { + return "[addr=" + + ((address == null) ? "0.0.0.0/0.0.0.0" : address.toString()) + + ",port=" + port + ",localport=" + localport + "]"; + } + + /** + * Shut down the input side of this socket. Subsequent reads will + * return end-of-file. + * + * @exception IOException if an error occurs + */ + protected void shutdownInput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } + + /** + * Shut down the output side of this socket. Subsequent writes will + * fail with an IOException. + * + * @exception IOException if an error occurs + */ + protected void shutdownOutput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } +} diff --git a/libjava/classpath/java/net/SocketImplFactory.java b/libjava/classpath/java/net/SocketImplFactory.java new file mode 100644 index 00000000000..b7cb10ca65f --- /dev/null +++ b/libjava/classpath/java/net/SocketImplFactory.java @@ -0,0 +1,59 @@ +/* SocketImplFactory.java -- Interface to create a SocketImpl object + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a <code>SocketImpl</code> + * object. This should not be needed by ordinary applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public interface SocketImplFactory +{ + /** + * This method returns an instance of the <code>SocketImpl</code> object + * + * @return A <code>SocketImpl</code> object + */ + SocketImpl createSocketImpl(); +} // interface SocketImplFactory diff --git a/libjava/classpath/java/net/SocketOptions.java b/libjava/classpath/java/net/SocketOptions.java new file mode 100644 index 00000000000..659bf750c3b --- /dev/null +++ b/libjava/classpath/java/net/SocketOptions.java @@ -0,0 +1,166 @@ +/* SocketOptions.java -- Implements options for sockets (duh!) + Copyright (C) 1998, 1999, 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface is used by <code>SocketImpl</code> and + * <code>DatagramSocketImpl</code> to implement options + * on sockets. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status should be completely JDK 1.4 compatible + */ +public interface SocketOptions +{ + /** + * Option id for the SO_KEEPALIVE value + * @since 1.3 + */ + int SO_KEEPALIVE = 0x8; + + /** + * Option id for the SO_LINGER value + */ + int SO_LINGER = 0x80; // 128 + + /** + * Option id for the SO_TIMEOUT value + */ + int SO_TIMEOUT = 0x1006; // 4102 + + /** + * Retrieve the local address to which the socket is bound. + */ + int SO_BINDADDR = 0x0F; // 15 + + /** + * Option id for the send buffer size + * @since 1.2 + */ + int SO_SNDBUF = 0x1001; // 4097 + + /** + * Option id for the receive buffer size + * @since 1.2 + */ + int SO_RCVBUF = 0x1002; // 4098 + + /** + * Sets the SO_REUSEADDR parameter on a socket + */ + int SO_REUSEADDR = 0x04; // 4 + + /** + * Sets SO_BROADCAST for a socket + * @since 1.4 + */ + int SO_BROADCAST = 0x20; // 32 + + /** + * Sets SO_OOBINLINE for a socket + * @since 1.4 + */ + int SO_OOBINLINE = 0x1003; // 4099 + + /** + * Option id for the TCP_NODELAY value + */ + int TCP_NODELAY = 0x01; // 1 + + /** + * Options id for the IP_MULTICAST_IF value + */ + int IP_MULTICAST_IF = 0x10; // 16 + + /** + * same as above + * @since 1.4 + */ + int IP_MULTICAST_IF2 = 0x1F; // 31 + + /** + * This option enables or disables local loopback of multicast datagrams. + * @since 1.4 + */ + int IP_MULTICAST_LOOP = 0x12; // 18 + + /** + * This option sets the type-of-service or traffic class field in the + * IP header for a TCP or UDP socket. + * @since 1.4 + */ + int IP_TOS = 0x03; // 3 + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * <code>Integer</code>. For options that are set to on or off, the + * value passed will be a <code>Boolean</code>. The <code>optionId</code> + * parameter is one of the defined constants in this interface. + * + * @param optionId The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ + void setOption(int optionId, Object val) throws SocketException; + + /** + * Returns the current setting of the specified option. The + * <code>Object</code> returned will be an <code>Integer</code> for options + * that have integer values. For options that are set to on or off, a + * <code>Boolean</code> will be returned. The <code>optionId</code> + * parameter is one of the defined constants in this interface. + * + * @param optionId The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ + Object getOption(int optionId) throws SocketException; +} // interface SocketOptions diff --git a/libjava/classpath/java/net/SocketPermission.java b/libjava/classpath/java/net/SocketPermission.java new file mode 100644 index 00000000000..8ccd01baec5 --- /dev/null +++ b/libjava/classpath/java/net/SocketPermission.java @@ -0,0 +1,408 @@ +/* SocketPermission.java -- Class modeling permissions for socket operations + Copyright (C) 1998, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.Serializable; +import java.security.Permission; +import java.security.PermissionCollection; + + +/** + * This class models a specific set of permssions for connecting to a + * host. There are two elements to this, the host/port combination and + * the permission list. + * <p> + * The host/port combination is specified as followed + * <p> + * <pre> + * hostname[:[-]port[-[port]]] + * </pre> + * <p> + * The hostname portion can be either a hostname or IP address. If it is + * a hostname, a wildcard is allowed in hostnames. This wildcard is a "*" + * and matches one or more characters. Only one "*" may appear in the + * host and it must be the leftmost character. For example, + * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain. + * <p> + * The port portion can be either a single value, or a range of values + * treated as inclusive. The first or the last port value in the range + * can be omitted in which case either the minimum or maximum legal + * value for a port (respectively) is used by default. Here are some + * examples: + * <p><ul> + * <li>8080 - Represents port 8080 only</li> + * <li>2000-3000 - Represents ports 2000 through 3000 inclusive</li> + * <li>-4000 - Represents ports 0 through 4000 inclusive</li> + * <li>1024- - Represents ports 1024 through 65535 inclusive</li> + * </ul><p> + * The permission list is a comma separated list of individual permissions. + * These individual permissions are: + * <p> + * <pre> + * accept + * connect + * listen + * resolve + * </pre> + * <p> + * The "listen" permission is only relevant if the host is localhost. If + * any permission at all is specified, then resolve permission is implied to + * exist. + * <p> + * Here are a variety of examples of how to create SocketPermission's + * <p><pre> + * SocketPermission("www.urbanophile.com", "connect"); + * Can connect to any port on www.urbanophile.com + * SocketPermission("www.urbanophile.com:80", "connect,accept"); + * Can connect to or accept connections from www.urbanophile.com on port 80 + * SocketPermission("localhost:1024-", "listen,accept,connect"); + * Can connect to, accept from, an listen on any local port number 1024 + * and up. + * SocketPermission("*.edu", "connect"); + * Can connect to any host in the edu domain + * SocketPermission("197.197.20.1", "accept"); + * Can accept connections from 197.197.20.1 + * </pre><p> + * + * This class also supports IPv6 addresses. These should be specified + * in either RFC 2732 format or in full uncompressed form. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SocketPermission extends Permission implements Serializable +{ + static final long serialVersionUID = -7204263841984476862L; + +// FIXME: Needs serialization work, including readObject/writeObject methods. + + /** + * A hostname/port combination as described above + */ + private transient String hostport; + + /** + * A comma separated list of actions for which we have permission + */ + private String actions; + + /** + * Initializes a new instance of <code>SocketPermission</code> with the + * specified host/port combination and actions string. + * + * @param hostport The hostname/port number combination + * @param actions The actions string + */ + public SocketPermission(String hostport, String actions) + { + super(hostport); + + this.hostport = hostport; + this.actions = actions; + } + + /** + * Tests this object for equality against another. This will be true if + * and only if the passed object is an instance of + * <code>SocketPermission</code> and both its hostname/port combination + * and permissions string are identical. + * + * @param obj The object to test against for equality + * + * @return <code>true</code> if object is equal to this object, + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof SocketPermission)) + return false; + + if (((SocketPermission) obj).hostport.equals(hostport)) + if (((SocketPermission) obj).actions.equals(actions)) + return true; + + return false; + } + + /** + * Returns a hash code value for this object. Overrides the + * <code>Permission.hashCode()</code>. + * + * @return A hash code + */ + public int hashCode() + { + int hash = 100; + if (hostport != null) + hash += hostport.hashCode(); + if (actions != null) + hash += actions.hashCode(); + return hash; + } + + /** + * Returns the list of permission actions in this object in canonical + * order. The canonical order is "connect,listen,accept,resolve" + * + * @return The permitted action string. + */ + public String getActions() + { + boolean found = false; + StringBuffer sb = new StringBuffer(""); + + if (actions.indexOf("connect") != -1) + { + sb.append("connect"); + found = true; + } + + if (actions.indexOf("listen") != -1) + if (found) + sb.append(",listen"); + else + { + sb.append("listen"); + found = true; + } + + if (actions.indexOf("accept") != -1) + if (found) + sb.append(",accept"); + else + { + sb.append("accept"); + found = true; + } + + if (found) + sb.append(",resolve"); + else if (actions.indexOf("resolve") != -1) + sb.append("resolve"); + + return sb.toString(); + } + + /** + * Returns a new <code>PermissionCollection</code> object that can hold + * <code>SocketPermission</code>'s. + * + * @return A new <code>PermissionCollection</code>. + */ + public PermissionCollection newPermissionCollection() + { + // FIXME: Implement + + return null; + } + + /** + * Returns true if the permission object passed it is implied by the + * this permission. This will be true if: + * + * <ul> + * <li>The argument is of type <code>SocketPermission</code></li> + * <li>The actions list of the argument are in this object's actions</li> + * <li>The port range of the argument is within this objects port range</li> + * <li>The hostname is equal to or a subset of this objects hostname</li> + * </ul> + * + * <p>The argument's hostname will be a subset of this object's hostname if:</p> + * + * <ul> + * <li>The argument's hostname or IP address is equal to this object's.</li> + * <li>The argument's canonical hostname is equal to this object's.</li> + * <li>The argument's canonical name matches this domains hostname with + * wildcards</li> + * </ul> + * + * @param perm The <code>Permission</code> to check against + * + * @return <code>true</code> if the <code>Permission</code> is implied by + * this object, <code>false</code> otherwise. + */ + public boolean implies(Permission perm) + { + SocketPermission p; + + // First make sure we are the right object type + if (perm instanceof SocketPermission) + p = (SocketPermission) perm; + else + return false; + + // Next check the actions + String ourlist = getActions(); + String theirlist = p.getActions(); + + if (! ourlist.startsWith(theirlist)) + return false; + + // Now check ports + int ourfirstport = 0; + + // Now check ports + int ourlastport = 0; + + // Now check ports + int theirfirstport = 0; + + // Now check ports + int theirlastport = 0; + + // Get ours + if (hostport.indexOf(":") == -1) + { + ourfirstport = 0; + ourlastport = 65535; + } + else + { + // FIXME: Needs bulletproofing. + // This will dump if hostport if all sorts of bad data was passed to + // the constructor + String range = hostport.substring(hostport.indexOf(":") + 1); + if (range.startsWith("-")) + ourfirstport = 0; + else if (range.indexOf("-") == -1) + ourfirstport = Integer.parseInt(range); + else + ourfirstport = + Integer.parseInt(range.substring(0, range.indexOf("-"))); + + if (range.endsWith("-")) + ourlastport = 65535; + else if (range.indexOf("-") == -1) + ourlastport = Integer.parseInt(range); + else + ourlastport = + Integer.parseInt(range.substring(range.indexOf("-") + 1, + range.length())); + } + + // Get theirs + if (p.hostport.indexOf(":") == -1) + { + theirfirstport = 0; + ourlastport = 65535; + } + else + { + // This will dump if hostport if all sorts of bad data was passed to + // the constructor + String range = p.hostport.substring(hostport.indexOf(":") + 1); + if (range.startsWith("-")) + theirfirstport = 0; + else if (range.indexOf("-") == -1) + theirfirstport = Integer.parseInt(range); + else + theirfirstport = + Integer.parseInt(range.substring(0, range.indexOf("-"))); + + if (range.endsWith("-")) + theirlastport = 65535; + else if (range.indexOf("-") == -1) + theirlastport = Integer.parseInt(range); + else + theirlastport = + Integer.parseInt(range.substring(range.indexOf("-") + 1, + range.length())); + } + + // Now check them + if ((theirfirstport < ourfirstport) || (theirlastport > ourlastport)) + return false; + + // Finally we can check the hosts + String ourhost; + + // Finally we can check the hosts + String theirhost; + + // Get ours + if (hostport.indexOf(":") == -1) + ourhost = hostport; + else + ourhost = hostport.substring(0, hostport.indexOf(":")); + + // Get theirs + if (p.hostport.indexOf(":") == -1) + theirhost = p.hostport; + else + theirhost = p.hostport.substring(0, p.hostport.indexOf(":")); + + // Are they equal? + if (ourhost.equals(theirhost)) + return true; + + // Try the canonical names + String ourcanonical = null; + + // Try the canonical names + String theircanonical = null; + try + { + ourcanonical = InetAddress.getByName(ourhost).getHostName(); + theircanonical = InetAddress.getByName(theirhost).getHostName(); + } + catch (UnknownHostException e) + { + // Who didn't resolve? Just assume current address is canonical enough + // Is this ok to do? + if (ourcanonical == null) + ourcanonical = ourhost; + if (theircanonical == null) + theircanonical = theirhost; + } + + if (ourcanonical.equals(theircanonical)) + return true; + + // Well, last chance. Try for a wildcard + if (ourhost.indexOf("*.") != -1) + { + String wild_domain = ourhost.substring(ourhost.indexOf("*" + 1)); + if (theircanonical.endsWith(wild_domain)) + return true; + } + + // Didn't make it + return false; + } +} diff --git a/libjava/classpath/java/net/SocketTimeoutException.java b/libjava/classpath/java/net/SocketTimeoutException.java new file mode 100644 index 00000000000..21b0dcd86c2 --- /dev/null +++ b/libjava/classpath/java/net/SocketTimeoutException.java @@ -0,0 +1,73 @@ +/* SocketTimeoutException.java -- the socket timed out + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.InterruptedIOException; + + +/** + * This exception signals that a socket read or accept timed out. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class SocketTimeoutException extends InterruptedIOException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -8846654841826352300L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketTimeoutException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketTimeoutException(String message) + { + super(message); + } +} // class SocketTimeoutException diff --git a/libjava/classpath/java/net/TODO b/libjava/classpath/java/net/TODO new file mode 100644 index 00000000000..e48969d3ae6 --- /dev/null +++ b/libjava/classpath/java/net/TODO @@ -0,0 +1,24 @@ +-- DNS cache purging. + +-- Implement ContentHandler chaining (non-JDK feature) + +-- Implement MIME type by file determination chaining using external + disk files. (non-JDK feature) + +-- Implement determining MIME type from an InputStream + +-- Datagram peek()'s + +-- Finalize methods for sockets + +-- HTTP - caching (supported by JDK?) + +-- HTTP - all protocol support beyond basic GET functionality + +-- Fix call to Date(String) in URLConnection.getHeaderFieldDate() when + I figure out why DateFormat isn't working. + +-- Finish off all JDK 1.2 permissions stuff + +-- Write URLClassLoader + diff --git a/libjava/classpath/java/net/URI.java b/libjava/classpath/java/net/URI.java new file mode 100644 index 00000000000..aeceeeec555 --- /dev/null +++ b/libjava/classpath/java/net/URI.java @@ -0,0 +1,1438 @@ +/* URI.java -- An URI class + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <p> + * A URI instance represents that defined by + * <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC3986</a>, + * with some deviations. + * </p> + * <p> + * At its highest level, a URI consists of: + * </p> + * <code>[<em>scheme</em><strong>:</strong>]<em>scheme-specific-part</em> + * [<strong>#</strong><em>fragment</em>]</code> + * </p> + * <p> + * where <strong>#</strong> and <strong>:</strong> are literal characters, + * and those parts enclosed in square brackets are optional. + * </p> + * <p> + * There are two main types of URI. An <em>opaque</em> URI is one + * which just consists of the above three parts, and is not further + * defined. An example of such a URI would be <em>mailto:</em> URI. + * In contrast, <em>hierarchical</em> URIs give further definition + * to the scheme-specific part, so as represent some part of a hierarchical + * structure. + * </p> + * <p> + * <code>[<strong>//</strong><em>authority</em>][<em>path</em>] + * [<strong>?</strong><em>query</em>]</code> + * </p> + * <p> + * with <strong>/</strong> and <strong>?</strong> being literal characters. + * When server-based, the authority section is further subdivided into: + * </p> + * <p> + * <code>[<em>user-info</em><strong>@</strong>]<em>host</em> + * [<strong>:</strong><em>port</em>]</code> + * </p> + * <p> + * with <strong>@</strong> and <strong>:</strong> as literal characters. + * Authority sections that are not server-based are said to be registry-based. + * </p> + * <p> + * Hierarchical URIs can be either relative or absolute. Absolute URIs + * always start with a `<strong>/</strong>', while relative URIs don't + * specify a scheme. Opaque URIs are always absolute. + * </p> + * <p> + * Each part of the URI may have one of three states: undefined, empty + * or containing some content. The former two of these are represented + * by <code>null</code> and the empty string in Java, respectively. + * The scheme-specific part may never be undefined. It also follows from + * this that the path sub-part may also not be undefined, so as to ensure + * the former. + * </p> + * <h2>Character Escaping and Quoting</h2> + * <p> + * The characters that can be used within a valid URI are restricted. + * There are two main classes of characters which can't be used as is + * within the URI: + * </p> + * <ol> + * <li><strong>Characters outside the US-ASCII character set</strong>. + * These have to be <strong>escaped</strong> in order to create + * an RFC-compliant URI; this means replacing the character with the + * appropriate hexadecimal value, preceded by a `%'.</li> + * <li><strong>Illegal characters</strong> (e.g. space characters, + * control characters) are quoted, which results in them being encoded + * in the same way as non-US-ASCII characters.</li> + * </ol> + * <p> + * The set of valid characters differs depending on the section of the URI: + * </p> + * <ul> + * <li><strong>Scheme</strong>: Must be an alphanumeric, `-', `.' or '+'.</li> + * <li><strong>Authority</strong>:Composed of the username, host, port, `@' + * and `:'.</li> + * <li><strong>Username</strong>: Allows unreserved or percent-encoded + * characters, sub-delimiters and `:'.</li> + * <li><strong>Host</strong>: Allows unreserved or percent-encoded + * characters, sub-delimiters and square brackets (`[' and `]') for IPv6 + * addresses.</li> + * <li><strong>Port</strong>: Digits only.</li> + * <li><strong>Path</strong>: Allows the path characters and `/'. + * <li><strong>Query</strong>: Allows the path characters, `?' and '/'. + * <li><strong>Fragment</strong>: Allows the path characters, `?' and '/'. + * </ul> + * <p> + * These definitions reference the following sets of characters: + * </p> + * <ul> + * <li><strong>Unreserved characters</strong>: The alphanumerics plus + * `-', `.', `_', and `~'.</li> + * <li><strong>Sub-delimiters</strong>: `!', `$', `&', `(', `)', `*', + * `+', `,', `;', `=' and the single-quote itself.</li> + * <li><strong>Path characters</strong>: Unreserved and percent-encoded + * characters and the sub-delimiters along with `@' and `:'.</li> + * </ul> + * <p> + * The constructors and accessor methods allow the use and retrieval of + * URI components which contain non-US-ASCII characters directly. + * They are only escaped when the <code>toASCIIString()</code> method + * is used. In contrast, illegal characters are always quoted, with the + * exception of the return values of the non-raw accessors. + * </p> + * + * @author Ito Kazumitsu (ito.kazumitsu@hitachi-cable.co.jp) + * @author Dalibor Topic (robilad@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public final class URI + implements Comparable, Serializable +{ + /** + * For serialization compatability. + */ + static final long serialVersionUID = -6052424284110960213L; + + /** + * Regular expression for parsing URIs. + * + * Taken from RFC 2396, Appendix B. + * This expression doesn't parse IPv6 addresses. + */ + private static final String URI_REGEXP = + "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?"; + + /** + * Regular expression for parsing the authority segment. + */ + private static final String AUTHORITY_REGEXP = + "(([^?#]*)@)?([^?#:]*)(:([0-9]*))?"; + + /** + * Valid characters (taken from rfc2396/3986) + */ + private static final String RFC2396_DIGIT = "0123456789"; + private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz"; + private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String RFC2396_ALPHA = + RFC2396_LOWALPHA + RFC2396_UPALPHA; + private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA; + private static final String RFC3986_UNRESERVED = RFC2396_ALPHANUM + "-._~"; + private static final String RFC3986_SUBDELIMS = "!$&'()*+,;="; + private static final String RFC3986_REG_NAME = + RFC3986_UNRESERVED + RFC3986_SUBDELIMS + "%"; + private static final String RFC3986_PCHAR = RFC3986_UNRESERVED + + RFC3986_SUBDELIMS + ":@%"; + private static final String RFC3986_SEGMENT = RFC3986_PCHAR; + private static final String RFC3986_PATH_SEGMENTS = RFC3986_SEGMENT + "/"; + private static final String RFC3986_SSP = RFC3986_PCHAR + "?/"; + private static final String RFC3986_HOST = RFC3986_REG_NAME + "[]"; + private static final String RFC3986_USERINFO = RFC3986_REG_NAME + ":"; + + /** + * Index of scheme component in parsed URI. + */ + private static final int SCHEME_GROUP = 2; + + /** + * Index of scheme-specific-part in parsed URI. + */ + private static final int SCHEME_SPEC_PART_GROUP = 3; + + /** + * Index of authority component in parsed URI. + */ + private static final int AUTHORITY_GROUP = 5; + + /** + * Index of path component in parsed URI. + */ + private static final int PATH_GROUP = 6; + + /** + * Index of query component in parsed URI. + */ + private static final int QUERY_GROUP = 8; + + /** + * Index of fragment component in parsed URI. + */ + private static final int FRAGMENT_GROUP = 10; + + /** + * Index of userinfo component in parsed authority section. + */ + private static final int AUTHORITY_USERINFO_GROUP = 2; + + /** + * Index of host component in parsed authority section. + */ + private static final int AUTHORITY_HOST_GROUP = 3; + + /** + * Index of port component in parsed authority section. + */ + private static final int AUTHORITY_PORT_GROUP = 5; + + /** + * The compiled version of the URI regular expression. + */ + private static final Pattern URI_PATTERN; + + /** + * The compiled version of the authority regular expression. + */ + private static final Pattern AUTHORITY_PATTERN; + + /** + * The set of valid hexadecimal characters. + */ + private static final String HEX = "0123456789ABCDEF"; + + private transient String scheme; + private transient String rawSchemeSpecificPart; + private transient String schemeSpecificPart; + private transient String rawAuthority; + private transient String authority; + private transient String rawUserInfo; + private transient String userInfo; + private transient String rawHost; + private transient String host; + private transient int port = -1; + private transient String rawPath; + private transient String path; + private transient String rawQuery; + private transient String query; + private transient String rawFragment; + private transient String fragment; + private String string; + + /** + * Static initializer to pre-compile the regular expressions. + */ + static + { + URI_PATTERN = Pattern.compile(URI_REGEXP); + AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEXP); + } + + private void readObject(ObjectInputStream is) + throws ClassNotFoundException, IOException + { + this.string = (String) is.readObject(); + try + { + parseURI(this.string); + } + catch (URISyntaxException x) + { + // Should not happen. + throw new RuntimeException(x); + } + } + + private void writeObject(ObjectOutputStream os) throws IOException + { + if (string == null) + string = toString(); + os.writeObject(string); + } + + /** + * <p> + * Returns the string content of the specified group of the supplied + * matcher. The returned value is modified according to the following: + * </p> + * <ul> + * <li>If the resulting string has a length greater than 0, then + * that string is returned.</li> + * <li>If a string of zero length, is matched, then the content + * of the preceding group is considered. If this is also an empty + * string, then <code>null</code> is returned to indicate an undefined + * value. Otherwise, the value is truly the empty string and this is + * the returned value.</li> + * </ul> + * <p> + * This method is used for matching against all parts of the URI + * that may be either undefined or empty (i.e. all those but the + * scheme-specific part and the path). In each case, the preceding + * group is the content of the original group, along with some + * additional distinguishing feature. For example, the preceding + * group for the query includes the preceding question mark, + * while that of the fragment includes the hash symbol. The presence + * of these features enables disambiguation between the two cases + * of a completely unspecified value and a simple non-existant value. + * The scheme differs in that it will never return an empty string; + * the delimiter follows the scheme rather than preceding it, so + * it becomes part of the following section. The same is true + * of the user information. + * </p> + * + * @param match the matcher, which contains the results of the URI + * matched against the URI regular expression. + * @return either the matched content, <code>null</code> for undefined + * values, or an empty string for a URI part with empty content. + */ + private static String getURIGroup(Matcher match, int group) + { + String matched = match.group(group); + return matched.length() == 0 + ? ((match.group(group - 1).length() == 0) ? null : "") : matched; + } + + /** + * Sets fields of this URI by parsing the given string. + * + * @param str The string to parse + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + private void parseURI(String str) throws URISyntaxException + { + Matcher matcher = URI_PATTERN.matcher(str); + + if (matcher.matches()) + { + scheme = getURIGroup(matcher, SCHEME_GROUP); + rawSchemeSpecificPart = matcher.group(SCHEME_SPEC_PART_GROUP); + schemeSpecificPart = unquote(rawSchemeSpecificPart); + if (!isOpaque()) + { + rawAuthority = getURIGroup(matcher, AUTHORITY_GROUP); + rawPath = matcher.group(PATH_GROUP); + rawQuery = getURIGroup(matcher, QUERY_GROUP); + } + rawFragment = getURIGroup(matcher, FRAGMENT_GROUP); + } + else + throw new URISyntaxException(str, + "doesn't match URI regular expression"); + parseServerAuthority(); + + // We must eagerly unquote the parts, because this is the only time + // we may throw an exception. + authority = unquote(rawAuthority); + userInfo = unquote(rawUserInfo); + host = unquote(rawHost); + path = unquote(rawPath); + query = unquote(rawQuery); + fragment = unquote(rawFragment); + } + + /** + * Unquote "%" + hex quotes characters + * + * @param str The string to unquote or null. + * + * @return The unquoted string or null if str was null. + * + * @exception URISyntaxException If the given string contains invalid + * escape sequences. + */ + private static String unquote(String str) throws URISyntaxException + { + if (str == null) + return null; + byte[] buf = new byte[str.length()]; + int pos = 0; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (c == '%') + { + if (i + 2 >= str.length()) + throw new URISyntaxException(str, "Invalid quoted character"); + int hi = Character.digit(str.charAt(++i), 16); + int lo = Character.digit(str.charAt(++i), 16); + if (lo < 0 || hi < 0) + throw new URISyntaxException(str, "Invalid quoted character"); + buf[pos++] = (byte) (hi * 16 + lo); + } + else + buf[pos++] = (byte) c; + } + try + { + return new String(buf, 0, pos, "utf-8"); + } + catch (java.io.UnsupportedEncodingException x2) + { + throw (Error) new InternalError().initCause(x2); + } + } + + /** + * Quote characters illegal in URIs in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quote(String str) + { + return quote(str, RFC3986_SSP); + } + + /** + * Quote characters illegal in URI authorities in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteAuthority(String str) + { + // Technically, we should be using RFC2396_AUTHORITY, but + // it contains no additional characters. + return quote(str, RFC3986_REG_NAME); + } + + /** + * Quotes the characters in the supplied string that are not part of + * the specified set of legal characters. + * + * @param str the string to quote + * @param legalCharacters the set of legal characters + * + * @return the quoted string. + */ + private static String quote(String str, String legalCharacters) + { + StringBuffer sb = new StringBuffer(str.length()); + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (legalCharacters.indexOf(c) == -1) + { + if (c <= 127) + { + sb.append('%'); + sb.append(HEX.charAt(c / 16)); + sb.append(HEX.charAt(c % 16)); + } + } + else + sb.append(c); + } + return sb.toString(); + } + + /** + * Quote characters illegal in URI hosts in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteHost(String str) + { + return quote(str, RFC3986_HOST); + } + + /** + * Quote characters illegal in URI paths in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quotePath(String str) + { + // Technically, we should be using RFC2396_PATH, but + // it contains no additional characters. + return quote(str, RFC3986_PATH_SEGMENTS); + } + + /** + * Quote characters illegal in URI user infos in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteUserInfo(String str) + { + return quote(str, RFC3986_USERINFO); + } + + /** + * Creates an URI from the given string + * + * @param str The string to create the URI from + * + * @exception URISyntaxException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public URI(String str) throws URISyntaxException + { + this.string = str; + parseURI(str); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param userInfo The username and authorization info + * @param host The hostname + * @param port The port number + * @param path The path + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String userInfo, String host, int port, + String path, String query, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (userInfo == null && host == null && port == -1 ? "" : "//") + + (userInfo == null ? "" : quoteUserInfo(userInfo) + "@") + + (host == null ? "" : quoteHost(host)) + + (port == -1 ? "" : ":" + String.valueOf(port)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param authority The authority + * @param path The apth + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String authority, String path, String query, + String fragment) throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (authority == null ? "" : "//" + quoteAuthority(authority)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param host The hostname + * @param path The path + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String host, String path, String fragment) + throws URISyntaxException + { + this(scheme, null, host, -1, path, null, fragment); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param ssp The scheme specific part + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String ssp, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (ssp == null ? "" : quote(ssp)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given string + * + * @param str The string to create the URI from + * + * @exception IllegalArgumentException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public static URI create(String str) + { + try + { + return new URI(str); + } + catch (URISyntaxException e) + { + throw (IllegalArgumentException) new IllegalArgumentException() + .initCause(e); + } + } + + /** + * Attempts to parse this URI's authority component, if defined, + * into user-information, host, and port components. The purpose + * of this method was to disambiguate between some authority sections, + * which form invalid server-based authories, but valid registry + * based authorities. In the updated RFC 3986, the authority section + * is defined differently, with registry-based authorities part of + * the host section. Thus, this method is now simply an explicit + * way of parsing any authority section. + * + * @return the URI, with the authority section parsed into user + * information, host and port components. + * @throws URISyntaxException if the given string violates RFC 2396 + */ + public URI parseServerAuthority() throws URISyntaxException + { + if (rawAuthority != null) + { + Matcher matcher = AUTHORITY_PATTERN.matcher(rawAuthority); + + if (matcher.matches()) + { + rawUserInfo = getURIGroup(matcher, AUTHORITY_USERINFO_GROUP); + rawHost = getURIGroup(matcher, AUTHORITY_HOST_GROUP); + + String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP); + + if (portStr != null) + try + { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) + { + URISyntaxException use = + new URISyntaxException + (string, "doesn't match URI regular expression"); + use.initCause(e); + throw use; + } + } + else + throw new URISyntaxException(string, + "doesn't match URI regular expression"); + } + return this; + } + + /** + * <p> + * Returns a normalized version of the URI. If the URI is opaque, + * or its path is already in normal form, then this URI is simply + * returned. Otherwise, the following transformation of the path + * element takes place: + * </p> + * <ol> + * <li>All `.' segments are removed.</li> + * <li>Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.</li> + * <li>A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.</li> + * </ol> + * <p> + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + * </p> + * + * @return the normalized URI. + */ + public URI normalize() + { + if (isOpaque() || path.indexOf("/./") == -1 && path.indexOf("/../") == -1) + return this; + try + { + return new URI(scheme, authority, normalizePath(path), query, + fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Normalized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * <p> + * Normalize the given path. The following transformation takes place: + * </p> + * <ol> + * <li>All `.' segments are removed.</li> + * <li>Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.</li> + * <li>A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.</li> + * </ol> + * <p> + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + * </p> + * + * @param relativePath the relative path to be normalized. + * @return the normalized path. + */ + private String normalizePath(String relativePath) + { + /* + This follows the algorithm in section 5.2.4. of RFC3986, + but doesn't modify the input buffer. + */ + StringBuffer input = new StringBuffer(relativePath); + StringBuffer output = new StringBuffer(); + int start = 0; + while (start < input.length()) + { + /* A */ + if (input.indexOf("../",start) == start) + { + start += 3; + continue; + } + if (input.indexOf("./",start) == start) + { + start += 2; + continue; + } + /* B */ + if (input.indexOf("/./",start) == start) + { + start += 2; + continue; + } + if (input.indexOf("/.",start) == start + && input.charAt(start + 2) != '.') + { + start += 1; + input.setCharAt(start,'/'); + continue; + } + /* C */ + if (input.indexOf("/../",start) == start) + { + start += 3; + removeLastSegment(output); + continue; + } + if (input.indexOf("/..",start) == start) + { + start += 2; + input.setCharAt(start,'/'); + removeLastSegment(output); + continue; + } + /* D */ + if (start == input.length() - 1 && input.indexOf(".",start) == start) + { + input.delete(0,1); + continue; + } + if (start == input.length() - 2 && input.indexOf("..",start) == start) + { + input.delete(0,2); + continue; + } + /* E */ + int indexOfSlash = input.indexOf("/",start); + while (indexOfSlash == start) + { + output.append("/"); + ++start; + indexOfSlash = input.indexOf("/",start); + } + if (indexOfSlash == -1) + indexOfSlash = input.length(); + output.append(input.substring(start, indexOfSlash)); + start = indexOfSlash; + } + return output.toString(); + } + + /** + * Removes the last segment of the path from the specified buffer. + * + * @param buffer the buffer containing the path. + */ + private void removeLastSegment(StringBuffer buffer) + { + int lastSlash = buffer.lastIndexOf("/"); + if (lastSlash == -1) + buffer.setLength(0); + else + buffer.setLength(lastSlash); + } + + /** + * Resolves the given URI against this URI + * + * @param uri The URI to resolve against this URI + * + * @return The resulting URI, or null when it couldn't be resolved + * for some reason. + * + * @throws NullPointerException if uri is null + */ + public URI resolve(URI uri) + { + if (uri.isAbsolute()) + return uri; + if (uri.isOpaque()) + return uri; + + String scheme = uri.getScheme(); + String schemeSpecificPart = uri.getSchemeSpecificPart(); + String authority = uri.getAuthority(); + String path = uri.getPath(); + String query = uri.getQuery(); + String fragment = uri.getFragment(); + + try + { + if (fragment != null && path != null && path.equals("") + && scheme == null && authority == null && query == null) + return new URI(this.scheme, this.schemeSpecificPart, fragment); + + if (authority == null) + { + authority = this.authority; + if (path == null) + path = ""; + if (! (path.startsWith("/"))) + { + StringBuffer basepath = new StringBuffer(this.path); + int i = this.path.lastIndexOf('/'); + + if (i >= 0) + basepath.delete(i + 1, basepath.length()); + + basepath.append(path); + path = normalizePath(basepath.toString()); + } + } + return new URI(this.scheme, authority, path, query, fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Resolved URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Resolves the given URI string against this URI + * + * @param str The URI as string to resolve against this URI + * + * @return The resulting URI + * + * @throws IllegalArgumentException If the given URI string + * violates RFC 2396 + * @throws NullPointerException If uri is null + */ + public URI resolve(String str) throws IllegalArgumentException + { + return resolve(create(str)); + } + + /** + * <p> + * Relativizes the given URI against this URI. The following + * algorithm is used: + * </p> + * <ul> + * <li>If either URI is opaque, the given URI is returned.</li> + * <li>If the schemes of the URIs differ, the given URI is returned.</li> + * <li>If the authority components of the URIs differ, then the given + * URI is returned.</li> + * <li>If the path of this URI is not a prefix of the supplied URI, + * then the given URI is returned.</li> + * <li>If all the above conditions hold, a new URI is created using the + * query and fragment components of the given URI, along with a path + * computed by removing the path of this URI from the start of the path + * of the supplied URI.</li> + * </ul> + * + * @param uri the URI to relativize agsint this URI + * @return the resulting URI + * @throws NullPointerException if the uri is null + */ + public URI relativize(URI uri) + { + if (isOpaque() || uri.isOpaque()) + return uri; + if (scheme == null && uri.getScheme() != null) + return uri; + if (scheme != null && !(scheme.equals(uri.getScheme()))) + return uri; + if (rawAuthority == null && uri.getRawAuthority() != null) + return uri; + if (rawAuthority != null && !(rawAuthority.equals(uri.getRawAuthority()))) + return uri; + if (!(uri.getRawPath().startsWith(rawPath))) + return uri; + try + { + return new URI(null, null, + uri.getRawPath().substring(rawPath.length()), + uri.getRawQuery(), uri.getRawFragment()); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Relativized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Creates an URL from an URI + * + * @throws MalformedURLException If a protocol handler for the URL could + * not be found, or if some other error occurred while constructing the URL + * @throws IllegalArgumentException If the URI is not absolute + */ + public URL toURL() throws IllegalArgumentException, MalformedURLException + { + if (isAbsolute()) + return new URL(this.toString()); + + throw new IllegalArgumentException("not absolute"); + } + + /** + * Returns the scheme of the URI + */ + public String getScheme() + { + return scheme; + } + + /** + * Tells whether this URI is absolute or not + */ + public boolean isAbsolute() + { + return scheme != null; + } + + /** + * Tell whether this URI is opaque or not + */ + public boolean isOpaque() + { + return ((scheme != null) && ! (schemeSpecificPart.startsWith("/"))); + } + + /** + * Returns the raw scheme specific part of this URI. + * The scheme-specific part is never undefined, though it may be empty + */ + public String getRawSchemeSpecificPart() + { + return rawSchemeSpecificPart; + } + + /** + * Returns the decoded scheme specific part of this URI. + */ + public String getSchemeSpecificPart() + { + return schemeSpecificPart; + } + + /** + * Returns the raw authority part of this URI + */ + public String getRawAuthority() + { + return rawAuthority; + } + + /** + * Returns the decoded authority part of this URI + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the raw user info part of this URI + */ + public String getRawUserInfo() + { + return rawUserInfo; + } + + /** + * Returns the decoded user info part of this URI + */ + public String getUserInfo() + { + return userInfo; + } + + /** + * Returns the hostname of the URI + */ + public String getHost() + { + return host; + } + + /** + * Returns the port number of the URI + */ + public int getPort() + { + return port; + } + + /** + * Returns the raw path part of this URI + */ + public String getRawPath() + { + return rawPath; + } + + /** + * Returns the path of the URI + */ + public String getPath() + { + return path; + } + + /** + * Returns the raw query part of this URI + */ + public String getRawQuery() + { + return rawQuery; + } + + /** + * Returns the query of the URI + */ + public String getQuery() + { + return query; + } + + /** + * Return the raw fragment part of this URI + */ + public String getRawFragment() + { + return rawFragment; + } + + /** + * Returns the fragment of the URI + */ + public String getFragment() + { + return fragment; + } + + /** + * <p> + * Compares the URI with the given object for equality. If the + * object is not a <code>URI</code>, then the method returns false. + * Otherwise, the following criteria are observed: + * </p> + * <ul> + * <li>The scheme of the URIs must either be null (undefined) in both cases, + * or equal, ignorant of case.</li> + * <li>The raw fragment of the URIs must either be null (undefined) in both + * cases, or equal, ignorant of case.</li> + * <li>Both URIs must be of the same type (opaque or hierarchial)</li> + * <li><strong>For opaque URIs:</strong></li> + * <ul> + * <li>The raw scheme-specific parts must be equal.</li> + * </ul> + * <li>For hierarchical URIs:</li> + * <ul> + * <li>The raw paths must be equal, ignorant of case.</li> + * <li>The raw queries are either both undefined or both equal, ignorant + * of case.</li> + * <li>The raw authority sections are either both undefined or:</li> + * <li><strong>For registry-based authorities:</strong></li> + * <ul><li>they are equal.</li></ul> + * <li><strong>For server-based authorities:</strong></li> + * <ul> + * <li>the hosts are equal, ignoring case</li> + * <li>the ports are equal</li> + * <li>the user information components are equal</li> + * </ul> + * </ul> + * </ul> + * + * @param obj the obj to compare the URI with. + * @return <code>true</code> if the objects are equal, according to + * the specification above. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof URI)) + return false; + URI uriObj = (URI) obj; + if (scheme == null) + { + if (uriObj.getScheme() != null) + return false; + } + else + if (!(scheme.equalsIgnoreCase(uriObj.getScheme()))) + return false; + if (rawFragment == null) + { + if (uriObj.getRawFragment() != null) + return false; + } + else + if (!(rawFragment.equalsIgnoreCase(uriObj.getRawFragment()))) + return false; + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uriObj.isOpaque(); + if (opaqueThis && opaqueObj) + return rawSchemeSpecificPart.equals(uriObj.getRawSchemeSpecificPart()); + else if (!opaqueThis && !opaqueObj) + { + boolean common = rawPath.equalsIgnoreCase(uriObj.getRawPath()) + && ((rawQuery == null && uriObj.getRawQuery() == null) + || rawQuery.equalsIgnoreCase(uriObj.getRawQuery())); + if (rawAuthority == null && uriObj.getRawAuthority() == null) + return common; + if (host == null) + return common + && rawAuthority.equalsIgnoreCase(uriObj.getRawAuthority()); + return common + && host.equalsIgnoreCase(uriObj.getHost()) + && port == uriObj.getPort() + && (rawUserInfo == null ? + uriObj.getRawUserInfo() == null : + rawUserInfo.equalsIgnoreCase(uriObj.getRawUserInfo())); + } + else + return false; + } + + /** + * Computes the hashcode of the URI + */ + public int hashCode() + { + return (getScheme() == null ? 0 : 13 * getScheme().hashCode()) + + 17 * getRawSchemeSpecificPart().hashCode() + + (getRawFragment() == null ? 0 : 21 + getRawFragment().hashCode()); + } + + /** + * Compare the URI with another object that must also be a URI. + * Undefined components are taken to be less than any other component. + * The following criteria are observed: + * </p> + * <ul> + * <li>Two URIs with different schemes are compared according to their + * scheme, regardless of case.</li> + * <li>A hierarchical URI is less than an opaque URI with the same + * scheme.</li> + * <li><strong>For opaque URIs:</strong></li> + * <ul> + * <li>URIs with differing scheme-specific parts are ordered according + * to the ordering of the scheme-specific part.</li> + * <li>URIs with the same scheme-specific part are ordered by the + * raw fragment.</li> + * </ul> + * <li>For hierarchical URIs:</li> + * <ul> + * <li>URIs are ordered according to their raw authority sections, + * if they are unequal.</li> + * <li><strong>For registry-based authorities:</strong></li> + * <ul><li>they are ordered according to the ordering of the authority + * component.</li></ul> + * <li><strong>For server-based authorities:</strong></li> + * <ul> + * <li>URIs are ordered according to the raw user information.</li> + * <li>URIs with the same user information are ordered by the host, + * ignoring case.</li> + * <lI>URIs with the same host are ordered by the port.</li> + * </ul> + * <li>URIs with the same authority section are ordered by the raw path.</li> + * <li>URIs with the same path are ordered by their raw query.</li> + * <li>URIs with the same query are ordered by their raw fragments.</li> + * </ul> + * </ul> + * + * @param obj This object to compare this URI with + * @return a negative integer, zero or a positive integer depending + * on whether this URI is less than, equal to or greater + * than that supplied, respectively. + * @throws ClassCastException if the given object is not a URI + */ + public int compareTo(Object obj) + throws ClassCastException + { + URI uri = (URI) obj; + if (scheme == null && uri.getScheme() != null) + return -1; + if (scheme != null) + { + int sCompare = scheme.compareToIgnoreCase(uri.getScheme()); + if (sCompare != 0) + return sCompare; + } + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uri.isOpaque(); + if (opaqueThis && !opaqueObj) + return 1; + if (!opaqueThis && opaqueObj) + return -1; + if (opaqueThis) + { + int ssCompare = + rawSchemeSpecificPart.compareTo(uri.getRawSchemeSpecificPart()); + if (ssCompare == 0) + return compareFragments(uri); + else + return ssCompare; + } + if (rawAuthority == null && uri.getRawAuthority() != null) + return -1; + if (rawAuthority != null) + { + int aCompare = rawAuthority.compareTo(uri.getRawAuthority()); + if (aCompare != 0) + { + if (host == null) + return aCompare; + if (rawUserInfo == null && uri.getRawUserInfo() != null) + return -1; + int uCompare = rawUserInfo.compareTo(uri.getRawUserInfo()); + if (uCompare != 0) + return uCompare; + if (host == null && uri.getHost() != null) + return -1; + int hCompare = host.compareTo(uri.getHost()); + if (hCompare != 0) + return hCompare; + return new Integer(port).compareTo(new Integer(uri.getPort())); + } + } + if (rawPath == null && uri.getRawPath() != null) + return -1; + if (rawPath != null) + { + int pCompare = rawPath.compareTo(uri.getRawPath()); + if (pCompare != 0) + return pCompare; + } + if (rawQuery == null && uri.getRawQuery() != null) + return -1; + if (rawQuery != null) + { + int qCompare = rawQuery.compareTo(uri.getRawQuery()); + if (qCompare != 0) + return qCompare; + } + return compareFragments(uri); + } + + /** + * Compares the fragment of this URI with that of the supplied URI. + * + * @param uri the URI to compare with this one. + * @return a negative integer, zero or a positive integer depending + * on whether this uri's fragment is less than, equal to + * or greater than the fragment of the uri supplied, respectively. + */ + private int compareFragments(URI uri) + { + if (rawFragment == null && uri.getRawFragment() != null) + return -1; + else if (rawFragment == null) + return 0; + else + return rawFragment.compareTo(uri.getRawFragment()); + } + + /** + * Returns the URI as a String. If the URI was created using a constructor, + * then this will be the same as the original input string. + * + * @return a string representation of the URI. + */ + public String toString() + { + return (scheme == null ? "" : scheme + ":") + + rawSchemeSpecificPart + + (rawFragment == null ? "" : "#" + rawFragment); + } + + /** + * Returns the URI as US-ASCII string. This is the same as the result + * from <code>toString()</code> for URIs that don't contain any non-US-ASCII + * characters. Otherwise, the non-US-ASCII characters are replaced + * by their percent-encoded representations. + * + * @return a string representation of the URI, containing only US-ASCII + * characters. + */ + public String toASCIIString() + { + String strRep = toString(); + boolean inNonAsciiBlock = false; + StringBuffer buffer = new StringBuffer(); + StringBuffer encBuffer = null; + for (int i = 0; i < strRep.length(); i++) + { + char c = strRep.charAt(i); + if (c <= 127) + { + if (inNonAsciiBlock) + { + buffer.append(escapeCharacters(encBuffer.toString())); + inNonAsciiBlock = false; + } + buffer.append(c); + } + else + { + if (!inNonAsciiBlock) + { + encBuffer = new StringBuffer(); + inNonAsciiBlock = true; + } + encBuffer.append(c); + } + } + return buffer.toString(); + } + + /** + * Converts the non-ASCII characters in the supplied string + * to their equivalent percent-encoded representations. + * That is, they are replaced by "%" followed by their hexadecimal value. + * + * @param str a string including non-ASCII characters. + * @return the string with the non-ASCII characters converted to their + * percent-encoded representations. + */ + private static String escapeCharacters(String str) + { + try + { + StringBuffer sb = new StringBuffer(); + // this is far from optimal, but it works + byte[] utf8 = str.getBytes("utf-8"); + for (int j = 0; j < utf8.length; j++) + { + sb.append('%'); + sb.append(HEX.charAt((utf8[j] & 0xff) / 16)); + sb.append(HEX.charAt((utf8[j] & 0xff) % 16)); + } + return sb.toString(); + } + catch (java.io.UnsupportedEncodingException x) + { + throw (Error) new InternalError("Escaping error").initCause(x); + } + } + +} diff --git a/libjava/classpath/java/net/URISyntaxException.java b/libjava/classpath/java/net/URISyntaxException.java new file mode 100644 index 00000000000..27a70bdb717 --- /dev/null +++ b/libjava/classpath/java/net/URISyntaxException.java @@ -0,0 +1,144 @@ +/* URISyntaxException.java -- a string could not be parsed as a URI + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception is thrown when a String cannot be parsed as a URI. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see URI + * @since 1.4 + * @status updated to 1.4 + */ +public class URISyntaxException extends Exception +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 2137979680897488891L; + + /** + * The failed input. + * + * @serial the bad URI + */ + private final String input; + + /** + * The index of failure. + * + * @serial the location of the problem + */ + private final int index; + + /** + * Create an exception from the invalid string, with the index set to -1. + * + * @param input the bad URI + * @param msg the descriptive error message + * @throws NullPointerException if input or msg are null + */ + public URISyntaxException(String input, String msg) + { + this(input, msg, -1); + } + + /** + * Create an exception from the invalid string, with the index of the + * point of failure. + * + * @param input the bad URI + * @param msg the descriptive error message + * @param index the index of the parse error, or -1 + * @throws NullPointerException if input or msg are null + * @throws IllegalArgumentException if index < -1 + */ + public URISyntaxException(String input, String msg, int index) + { + // The toString() hack checks for null. + super(msg.toString()); + this.input = input.toString(); + this.index = index; + if (index < -1) + throw new IllegalArgumentException(); + } + + /** + * Returns the bad input string. + * + * @return the bad URI, guaranteed non-null + */ + public String getInput() + { + return input; + } + + /** + * Returns the reason for the failure. + * + * @return the message, guaranteed non-null + */ + public String getReason() + { + return super.getMessage(); + } + + /** + * Returns the index of the failure, or -1. + * + * @return the index of failure + */ + public int getIndex() + { + return index; + } + + /** + * Returns a message describing the parse error, as if by + * <code>getReason() + (getIndex() >= 0 ? " at index " + getIndex() : "") + * + ": " + getInput()</code>. + * + * @return the message string + */ + public String getMessage() + { + return (super.getMessage() + (index >= 0 ? " at index " + index : "") + + ": " + input); + } +} diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java new file mode 100644 index 00000000000..627dbc391e9 --- /dev/null +++ b/libjava/classpath/java/net/URL.java @@ -0,0 +1,956 @@ +/* URL.java -- Uniform Resource Locator Class + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.URLParseError; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.StringTokenizer; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This final class represents an Internet Uniform Resource Locator (URL). + * For details on the syntax of URL's and what they can be used for, + * refer to RFC 1738, available from <a + * href="http://ds.internic.net/rfcs/rfc1738.txt"> + * http://ds.internic.net/rfcs/rfc1738.txt</a> + * <p> + * There are a great many protocols supported by URL's such as "http", + * "ftp", and "file". This object can handle any arbitrary URL for which + * a URLStreamHandler object can be written. Default protocol handlers + * are provided for the "http" and "ftp" protocols. Additional protocols + * handler implementations may be provided in the future. In any case, + * an application or applet can install its own protocol handlers that + * can be "chained" with other protocol hanlders in the system to extend + * the base functionality provided with this class. (Note, however, that + * unsigned applets cannot access properties by default or install their + * own protocol handlers). + * <p> + * This chaining is done via the system property java.protocol.handler.pkgs + * If this property is set, it is assumed to be a "|" separated list of + * package names in which to attempt locating protocol handlers. The + * protocol handler is searched for by appending the string + * ".<protocol>.Handler" to each packed in the list until a hander is + * found. If a protocol handler is not found in this list of packages, or if + * the property does not exist, then the default protocol handler of + * "gnu.java.net.<protocol>.Handler" is tried. If this is + * unsuccessful, a MalformedURLException is thrown. + * <p> + * All of the constructor methods of URL attempt to load a protocol + * handler and so any needed protocol handlers must be installed when + * the URL is constructed. + * <p> + * Here is an example of how URL searches for protocol handlers. Assume + * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the + * URL is "news://comp.lang.java.programmer". URL would looking the + * following places for protocol handlers: + * <p><pre> + * com.foo.news.Handler + * com.bar.news.Handler + * gnu.java.net.news.Handler + * </pre><p> + * If the protocol handler is not found in any of those locations, a + * MalformedURLException would be thrown. + * <p> + * Please note that a protocol handler must be a subclass of + * URLStreamHandler. + * <p> + * Normally, this class caches protocol handlers. Once it finds a handler + * for a particular protocol, it never tries to look up a new handler + * again. However, if the system property + * gnu.java.net.nocache_protocol_handlers is set, then this + * caching behavior is disabled. This property is specific to this + * implementation. Sun's JDK may or may not do protocol caching, but it + * almost certainly does not examine this property. + * <p> + * Please also note that an application can install its own factory for + * loading protocol handlers (see setURLStreamHandlerFactory). If this is + * done, then the above information is superseded and the behavior of this + * class in loading protocol handlers is dependent on that factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URLStreamHandler + */ +public final class URL implements Serializable +{ + private static final String DEFAULT_SEARCH_PATH = + "gnu.java.net.protocol|gnu.inet"; + + // Cached System ClassLoader + private static ClassLoader systemClassLoader; + + /** + * The name of the protocol for this URL. + * The protocol is always stored in lower case. + */ + private String protocol; + + /** + * The "authority" portion of the URL. + */ + private String authority; + + /** + * The hostname or IP address of this protocol. + * This includes a possible user. For example <code>joe@some.host.net</code>. + */ + private String host; + + /** + * The user information necessary to establish the connection. + */ + private String userInfo; + + /** + * The port number of this protocol or -1 if the port number used is + * the default for this protocol. + */ + private int port = -1; // Initialize for constructor using context. + + /** + * The "file" portion of the URL. It is defined as <code>path[?query]</code>. + */ + private String file; + + /** + * The anchor portion of the URL. + */ + private String ref; + + /** + * This is the hashCode for this URL + */ + private int hashCode; + + /** + * The protocol handler in use for this URL + */ + transient URLStreamHandler ph; + + /** + * If an application installs its own protocol handler factory, this is + * where we keep track of it. + */ + private static URLStreamHandlerFactory factory; + private static final long serialVersionUID = -7627629688361524110L; + + /** + * This a table where we cache protocol handlers to avoid the overhead + * of looking them up each time. + */ + private static HashMap ph_cache = new HashMap(); + + /** + * Whether or not to cache protocol handlers. + */ + private static boolean cache_handlers; + + static + { + String s = System.getProperty("gnu.java.net.nocache_protocol_handlers"); + + if (s == null) + cache_handlers = true; + else + cache_handlers = false; + } + + /** + * Constructs a URL and loads a protocol handler for the values passed as + * arguments. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's + * default port + * @param file The "file" portion of the URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, int port, String file) + throws MalformedURLException + { + this(protocol, host, port, file, null); + } + + /** + * Constructs a URL and loads a protocol handler for the values passed in + * as arugments. Uses the default port for the protocol. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address for this URL + * @param file The "file" portion of this URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, String file) + throws MalformedURLException + { + this(protocol, host, -1, file, null); + } + + /** + * This method initializes a new instance of <code>URL</code> with the + * specified protocol, host, port, and file. Additionally, this method + * allows the caller to specify a protocol handler to use instead of + * the default. If this handler is specified, the caller must have + * the "specifyStreamHandler" permission (see <code>NetPermission</code>) + * or a <code>SecurityException</code> will be thrown. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's default + * port + * @param file The "file" portion of the URL. + * @param ph The protocol handler to use with this URL. + * + * @exception MalformedURLException If no protocol handler can be loaded + * for the specified protocol. + * @exception SecurityException If the <code>SecurityManager</code> exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(String protocol, String host, int port, String file, + URLStreamHandler ph) throws MalformedURLException + { + if (protocol == null) + throw new MalformedURLException("null protocol"); + protocol = protocol.toLowerCase(); + this.protocol = protocol; + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + this.host = host; + this.port = port; + this.authority = (host != null) ? host : ""; + if (port >= 0 && host != null) + this.authority += ":" + port; + + int hashAt = file.indexOf('#'); + if (hashAt < 0) + { + this.file = file; + this.ref = null; + } + else + { + this.file = file.substring(0, hashAt); + this.ref = file.substring(hashAt + 1); + } + hashCode = hashCode(); // Used for serialization. + } + + /** + * Initializes a URL from a complete string specification such as + * "http://www.urbanophile.com/arenn/". First the protocol name is parsed + * out of the string. Then a handler is located for that protocol and + * the parseURL() method of that protocol handler is used to parse the + * remaining fields. + * + * @param spec The complete String representation of a URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + */ + public URL(String spec) throws MalformedURLException + { + this((URL) null, spec != null ? spec : "", (URLStreamHandler) null); + } + + /** + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any + * fields not present the URL are inheritied from the context URL. + * This allows relative URL's to be easily constructed. If the + * context argument is null, then a complete URL must be specified + * in the URL string. If the protocol parsed out of the URL is + * different from the context URL's protocol, then then URL String + * is also expected to be a complete URL. + * + * @param context The context on which to parse the specification + * @param spec The string to parse an URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * for the URL cannot be parsed + */ + public URL(URL context, String spec) throws MalformedURLException + { + this(context, spec, (URLStreamHandler) null); + } + + /** + * Creates an URL from given arguments + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any fields + * not present the URL are inheritied from the context URL. This allows + * relative URL's to be easily constructed. If the context argument is + * null, then a complete URL must be specified in the URL string. + * If the protocol parsed out of the URL is different + * from the context URL's protocol, then then URL String is also + * expected to be a complete URL. + * <p> + * Additionally, this method allows the caller to specify a protocol handler + * to use instead of the default. If this handler is specified, the caller + * must have the "specifyStreamHandler" permission + * (see <code>NetPermission</code>) or a <code>SecurityException</code> + * will be thrown. + * + * @param context The context in which to parse the specification + * @param spec The string to parse as an URL + * @param ph The stream handler for the URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + * @exception SecurityException If the <code>SecurityManager</code> exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(URL context, String spec, URLStreamHandler ph) + throws MalformedURLException + { + /* A protocol is defined by the doc as the substring before a ':' + * as long as the ':' occurs before any '/'. + * + * If context is null, then spec must be an absolute URL. + * + * The relative URL need not specify all the components of a URL. + * If the protocol, host name, or port number is missing, the value + * is inherited from the context. A bare file component is appended + * to the context's file. The optional anchor is not inherited. + */ + + // If this is an absolute URL, then ignore context completely. + // An absolute URL must have chars prior to "://" but cannot have a colon + // right after the "://". The second colon is for an optional port value + // and implies that the host from the context is used if available. + int colon; + int slash = spec.indexOf('/'); + if ((colon = spec.indexOf("://", 1)) > 0 + && ((colon < slash || slash < 0)) + && ! spec.regionMatches(colon, "://:", 0, 4)) + context = null; + + if ((colon = spec.indexOf(':')) > 0 + && (colon < slash || slash < 0)) + { + // Protocol specified in spec string. + protocol = spec.substring(0, colon).toLowerCase(); + if (context != null && context.protocol.equals(protocol)) + { + // The 1.2 doc specifically says these are copied to the new URL. + host = context.host; + port = context.port; + file = context.file; + userInfo = context.userInfo; + if (file == null || file.length() == 0) + file = "/"; + authority = context.authority; + } + } + else if (context != null) + { + // Protocol NOT specified in spec string. + // Use context fields (except ref) as a foundation for relative URLs. + colon = -1; + protocol = context.protocol; + host = context.host; + port = context.port; + file = context.file; + userInfo = context.userInfo; + if (file == null || file.length() == 0) + file = "/"; + authority = context.authority; + } + else // Protocol NOT specified in spec. and no context available. + throw new MalformedURLException("Absolute URL required with null" + + " context: " + spec); + + protocol = protocol.trim(); + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + // JDK 1.2 doc for parseURL specifically states that any '#' ref + // is to be excluded by passing the 'limit' as the indexOf the '#' + // if one exists, otherwise pass the end of the string. + int hashAt = spec.indexOf('#', colon + 1); + + try + { + this.ph.parseURL(this, spec, colon + 1, + hashAt < 0 ? spec.length() : hashAt); + } + catch (URLParseError e) + { + throw new MalformedURLException(e.getMessage()); + } + + if (hashAt >= 0) + ref = spec.substring(hashAt + 1); + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Test another URL for equality with this one. This will be true only if + * the argument is non-null and all of the fields in the URL's match + * exactly (ie, protocol, host, port, file, and ref). Overrides + * Object.equals(), implemented by calling the equals method of the handler. + * + * @param obj The URL to compare with + * + * @return true if the URL is equal, false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof URL)) + return false; + + return ph.equals(this, (URL) obj); + } + + /** + * Returns the contents of this URL as an object by first opening a + * connection, then calling the getContent() method against the connection + * + * @return A content object for this URL + * @exception IOException If opening the connection or getting the + * content fails. + * + * @since 1.3 + */ + public Object getContent() throws IOException + { + return openConnection().getContent(); + } + + /** + * Gets the contents of this URL + * + * @param classes The allow classes for the content object. + * + * @return a context object for this URL. + * + * @exception IOException If an error occurs + */ + public Object getContent(Class[] classes) throws IOException + { + // FIXME: implement this + return getContent(); + } + + /** + * Returns the file portion of the URL. + * Defined as <code>path[?query]</code>. + * Returns the empty string if there is no file portion. + * + * @return The filename specified in this URL, or an empty string if empty. + */ + public String getFile() + { + return file == null ? "" : file; + } + + /** + * Returns the path of the URL. This is the part of the file before any '?' + * character. + * + * @return The path specified in this URL, or null if empty. + * + * @since 1.3 + */ + public String getPath() + { + // The spec says we need to return an empty string, but some + // applications depends on receiving null when the path is empty. + if (file == null) + return null; + int quest = file.indexOf('?'); + return quest < 0 ? getFile() : file.substring(0, quest); + } + + /** + * Returns the authority of the URL + * + * @return The authority specified in this URL. + * + * @since 1.3 + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the host of the URL + * + * @return The host specified in this URL. + */ + public String getHost() + { + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? host : host.substring(at + 1, host.length()); + } + + /** + * Returns the port number of this URL or -1 if the default port number is + * being used. + * + * @return The port number + * + * @see #getDefaultPort() + */ + public int getPort() + { + return port; + } + + /** + * Returns the default port of the URL. If the StreamHandler for the URL + * protocol does not define a default port it returns -1. + * + * @return The default port of the current protocol. + */ + public int getDefaultPort() + { + return ph.getDefaultPort(); + } + + /** + * Returns the protocol of the URL + * + * @return The specified protocol. + */ + public String getProtocol() + { + return protocol; + } + + /** + * Returns the ref (sometimes called the "# reference" or "anchor") portion + * of the URL. + * + * @return The ref + */ + public String getRef() + { + return ref; + } + + /** + * Returns the user information of the URL. This is the part of the host + * name before the '@'. + * + * @return the user at a particular host or null when no user defined. + */ + public String getUserInfo() + { + if (userInfo != null) + return userInfo; + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? null : host.substring(0, at); + } + + /** + * Returns the query of the URL. This is the part of the file before the + * '?'. + * + * @return the query part of the file, or null when there is no query part. + */ + public String getQuery() + { + int quest = (file == null) ? -1 : file.indexOf('?'); + return quest < 0 ? null : file.substring(quest + 1, file.length()); + } + + /** + * Returns a hashcode computed by the URLStreamHandler of this URL + * + * @return The hashcode for this URL. + */ + public int hashCode() + { + if (hashCode != 0) + return hashCode; // Use cached value if available. + else + return ph.hashCode(this); + } + + /** + * Returns a URLConnection object that represents a connection to the remote + * object referred to by the URL. The URLConnection is created by calling the + * openConnection() method of the protocol handler + * + * @return A URLConnection for this URL + * + * @exception IOException If an error occurs + */ + public URLConnection openConnection() throws IOException + { + return ph.openConnection(this); + } + + /** + * Opens a connection to this URL and returns an InputStream for reading + * from that connection + * + * @return An <code>InputStream</code> for this URL. + * + * @exception IOException If an error occurs + */ + public InputStream openStream() throws IOException + { + return openConnection().getInputStream(); + } + + /** + * Tests whether or not another URL refers to the same "file" as this one. + * This will be true if and only if the passed object is not null, is a + * URL, and matches all fields but the ref (ie, protocol, host, port, + * and file); + * + * @param url The URL object to test with + * + * @return true if URL matches this URL's file, false otherwise + */ + public boolean sameFile(URL url) + { + return ph.sameFile(this, url); + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. This might be called + * by the <code>parseURL()</code> method in that class. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL + * @param host The hostname or IP address for this URL + * @param port The port number of this URL + * @param file The "file" portion of this URL. + * @param ref The anchor portion of this URL. + */ + protected void set(String protocol, String host, int port, String file, + String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.authority = ""; + this.port = port; + this.host = host; + this.file = file; + this.ref = ref; + + if (host != null) + this.authority += host; + if (port >= 0) + this.authority += ":" + port; + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL. + * @param host The hostname or IP address for this URL. + * @param port The port number of this URL. + * @param authority The authority of this URL. + * @param userInfo The user and password (if needed) of this URL. + * @param path The "path" portion of this URL. + * @param query The query of this URL. + * @param ref The anchor portion of this URL. + * + * @since 1.3 + */ + protected void set(String protocol, String host, int port, String authority, + String userInfo, String path, String query, String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.host = host; + this.userInfo = userInfo; + this.port = port; + this.authority = authority; + if (query == null) + this.file = path; + else + this.file = path + "?" + query; + this.ref = ref; + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the URLStreamHandlerFactory for this class. This factory is + * responsible for returning the appropriate protocol handler for + * a given URL. + * + * @param fac The URLStreamHandlerFactory class to use + * + * @exception Error If the factory is alread set. + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) + { + if (factory != null) + throw new Error("URLStreamHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + factory = fac; + } + + /** + * Returns a String representing this URL. The String returned is + * created by calling the protocol handler's toExternalForm() method. + * + * @return A string for this URL + */ + public String toExternalForm() + { + // Identical to toString(). + return ph.toExternalForm(this); + } + + /** + * Returns a String representing this URL. Identical to toExternalForm(). + * The value returned is created by the protocol handler's + * toExternalForm method. Overrides Object.toString() + * + * @return A string for this URL + */ + public String toString() + { + // Identical to toExternalForm(). + return ph.toExternalForm(this); + } + + /** + * This internal method is used in two different constructors to load + * a protocol handler for this URL. + * + * @param protocol The protocol to load a handler for + * + * @return A URLStreamHandler for this protocol, or null when not found. + */ + private static synchronized URLStreamHandler getURLStreamHandler(String protocol) + { + URLStreamHandler ph = null; + + // First, see if a protocol handler is in our cache. + if (cache_handlers) + { + if ((ph = (URLStreamHandler) ph_cache.get(protocol)) != null) + return ph; + } + + // If a non-default factory has been set, use it to find the protocol. + if (factory != null) + { + ph = factory.createURLStreamHandler(protocol); + } + + // Non-default factory may have returned null or a factory wasn't set. + // Use the default search algorithm to find a handler for this protocol. + if (ph == null) + { + // Get the list of packages to check and append our default handler + // to it, along with the JDK specified default as a last resort. + // Except in very unusual environments the JDK specified one shouldn't + // ever be needed (or available). + String ph_search_path = + System.getProperty("java.protocol.handler.pkgs"); + + // Tack our default package on at the ends. + if (ph_search_path != null) + ph_search_path += "|" + DEFAULT_SEARCH_PATH; + else + ph_search_path = DEFAULT_SEARCH_PATH; + + // Finally loop through our search path looking for a match. + StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|"); + + // Cache the systemClassLoader + if (systemClassLoader == null) + { + systemClassLoader = (ClassLoader) AccessController.doPrivileged + (new PrivilegedAction() { + public Object run() + { + return ClassLoader.getSystemClassLoader(); + } + }); + } + + do + { + try + { + // Try to get a class from the system/application + // classloader, initialize it, make an instance + // and try to cast it to a URLStreamHandler. + String clsName = + (pkgPrefix.nextToken() + "." + protocol + ".Handler"); + Class c = Class.forName(clsName, true, systemClassLoader); + ph = (URLStreamHandler) c.newInstance(); + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Ignored. + } + } + while (ph == null && pkgPrefix.hasMoreTokens()); + } + + // Update the hashtable with the new protocol handler. + if (ph != null && cache_handlers) + ph_cache.put(protocol, ph); + else + ph = null; + + return ph; + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + this.ph = getURLStreamHandler(protocol); + if (this.ph == null) + throw new IOException("Handler for protocol " + protocol + " not found"); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + } + + /** + * Returns the equivalent <code>URI</code> object for this <code>URL</code>. + * This is the same as calling <code>new URI(this.toString())</code>. + * RFC2396-compliant URLs are guaranteed a successful conversion to + * a <code>URI</code> instance. However, there are some values which + * form valid URLs, but which do not also form RFC2396-compliant URIs. + * + * @throws URISyntaxException if this URL is not RFC2396-compliant, + * and thus can not be successfully converted to a URI. + */ + public URI toURI() + throws URISyntaxException + { + return new URI(toString()); + } + +} diff --git a/libjava/classpath/java/net/URLClassLoader.java b/libjava/classpath/java/net/URLClassLoader.java new file mode 100644 index 00000000000..8ebc3e05dd7 --- /dev/null +++ b/libjava/classpath/java/net/URLClassLoader.java @@ -0,0 +1,1171 @@ +/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.PrivilegedAction; +import java.security.SecureClassLoader; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + + +/** + * A secure class loader that can load classes and resources from + * multiple locations. Given an array of <code>URL</code>s this class + * loader will retrieve classes and resources by fetching them from + * possible remote locations. Each <code>URL</code> is searched in + * order in which it was added. If the file portion of the + * <code>URL</code> ends with a '/' character then it is interpreted + * as a base directory, otherwise it is interpreted as a jar file from + * which the classes/resources are resolved. + * + * <p>New instances can be created by two static + * <code>newInstance()</code> methods or by three public + * contructors. Both ways give the option to supply an initial array + * of <code>URL</code>s and (optionally) a parent classloader (that is + * different from the standard system class loader).</p> + * + * <p>Normally creating a <code>URLClassLoader</code> throws a + * <code>SecurityException</code> if a <code>SecurityManager</code> is + * installed and the <code>checkCreateClassLoader()</code> method does + * not return true. But the <code>newInstance()</code> methods may be + * used by any code as long as it has permission to acces the given + * <code>URL</code>s. <code>URLClassLoaders</code> created by the + * <code>newInstance()</code> methods also explicitly call the + * <code>checkPackageAccess()</code> method of + * <code>SecurityManager</code> if one is installed before trying to + * load a class. Note that only subclasses of + * <code>URLClassLoader</code> can add new URLs after the + * URLClassLoader had been created. But it is always possible to get + * an array of all URLs that the class loader uses to resolve classes + * and resources by way of the <code>getURLs()</code> method.</p> + * + * <p>Open issues: + * <ul> + * + * <li>Should the URLClassLoader actually add the locations found in + * the manifest or is this the responsibility of some other + * loader/(sub)class? (see <a + * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html"> + * Extension Mechanism Architecture - Bundles Extensions</a>)</li> + * + * <li>How does <code>definePackage()</code> and sealing work + * precisely?</li> + * + * <li>We save and use the security context (when a created by + * <code>newInstance()</code> but do we have to use it in more + * places?</li> + * + * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li> + * + * </ul> + * </p> + * + * @since 1.2 + * + * @author Mark Wielaard (mark@klomp.org) + * @author Wu Gansha (gansha.wu@intel.com) + */ +public class URLClassLoader extends SecureClassLoader +{ + // Class Variables + + /** + * A global cache to store mappings between URLLoader and URL, + * so we can avoid do all the homework each time the same URL + * comes. + * XXX - Keeps these loaders forever which prevents garbage collection. + */ + private static HashMap urlloaders = new HashMap(); + + /** + * A cache to store mappings between handler factory and its + * private protocol handler cache (also a HashMap), so we can avoid + * create handlers each time the same protocol comes. + */ + private static HashMap factoryCache = new HashMap(5); + + // Instance variables + + /** Locations to load classes from */ + private final Vector urls = new Vector(); + + /** + * Store pre-parsed information for each url into this vector: each + * element is a URL loader. A jar file has its own class-path + * attribute which adds to the URLs that will be searched, but this + * does not add to the list of urls. + */ + private final Vector urlinfos = new Vector(); + + /** Factory used to get the protocol handlers of the URLs */ + private final URLStreamHandlerFactory factory; + + /** + * The security context when created from <code>newInstance()</code> + * or null when created through a normal constructor or when no + * <code>SecurityManager</code> was installed. + */ + private final AccessControlContext securityContext; + + // Helper classes + + /** + * A <code>URLLoader</code> contains all logic to load resources from a + * given base <code>URL</code>. + */ + abstract static class URLLoader + { + /** + * Our classloader to get info from if needed. + */ + final URLClassLoader classloader; + + /** + * The base URL from which all resources are loaded. + */ + final URL baseURL; + + /** + * A <code>CodeSource</code> without any associated certificates. + * It is common for classes to not have certificates associated + * with them. If they come from the same <code>URLLoader</code> + * then it is safe to share the associated <code>CodeSource</code> + * between them since <code>CodeSource</code> is immutable. + */ + final CodeSource noCertCodeSource; + + URLLoader(URLClassLoader classloader, URL baseURL) + { + this(classloader, baseURL, baseURL); + } + + URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL) + { + this.classloader = classloader; + this.baseURL = baseURL; + this.noCertCodeSource = new CodeSource(overrideURL, null); + } + + /** + * Returns a <code>Resource</code> loaded by this + * <code>URLLoader</code>, or <code>null</code> when no + * <code>Resource</code> with the given name exists. + */ + abstract Resource getResource(String s); + + /** + * Returns the <code>Manifest</code> associated with the + * <code>Resource</code>s loaded by this <code>URLLoader</code> or + * <code>null</code> there is no such <code>Manifest</code>. + */ + Manifest getManifest() + { + return null; + } + + Vector getClassPath() + { + return null; + } + } + + /** + * A <code>Resource</code> represents a resource in some + * <code>URLLoader</code>. It also contains all information (e.g., + * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and + * <code>InputStream</code>) that is necessary for loading resources + * and creating classes from a <code>URL</code>. + */ + abstract static class Resource + { + final URLLoader loader; + final String name; + + Resource(URLLoader loader, String name) + { + this.loader = loader; + this.name = name; + } + + /** + * Returns the non-null <code>CodeSource</code> associated with + * this resource. + */ + CodeSource getCodeSource() + { + Certificate[] certs = getCertificates(); + if (certs == null) + return loader.noCertCodeSource; + else + return new CodeSource(loader.baseURL, certs); + } + + /** + * Returns <code>Certificates</code> associated with this + * resource, or null when there are none. + */ + Certificate[] getCertificates() + { + return null; + } + + /** + * Return a <code>URL</code> that can be used to access this resource. + */ + abstract URL getURL(); + + /** + * Returns the size of this <code>Resource</code> in bytes or + * <code>-1</code> when unknown. + */ + abstract int getLength(); + + /** + * Returns the non-null <code>InputStream</code> through which + * this resource can be loaded. + */ + abstract InputStream getInputStream() throws IOException; + } + + /** + * A <code>JarURLLoader</code> is a type of <code>URLLoader</code> + * only loading from jar url. + */ + static final class JarURLLoader extends URLLoader + { + final JarFile jarfile; // The jar file for this url + final URL baseJarURL; // Base jar: url for all resources loaded from jar + + Vector classPath; // The "Class-Path" attribute of this Jar's manifest + + public JarURLLoader(URLClassLoader classloader, URL baseURL) + { + super(classloader, baseURL); + + // Cache url prefix for all resources in this jar url. + String external = baseURL.toExternalForm(); + StringBuffer sb = new StringBuffer(external.length() + 6); + sb.append("jar:"); + sb.append(external); + sb.append("!/"); + String jarURL = sb.toString(); + + this.classPath = null; + URL baseJarURL = null; + JarFile jarfile = null; + try + { + baseJarURL = + new URL(null, jarURL, classloader.getURLStreamHandler("jar")); + + jarfile = + ((JarURLConnection) baseJarURL.openConnection()).getJarFile(); + + Manifest manifest; + Attributes attributes; + String classPathString; + + if ((manifest = jarfile.getManifest()) != null + && (attributes = manifest.getMainAttributes()) != null + && ((classPathString + = attributes.getValue(Attributes.Name.CLASS_PATH)) + != null)) + { + this.classPath = new Vector(); + + StringTokenizer st = new StringTokenizer(classPathString, " "); + while (st.hasMoreElements ()) + { + String e = st.nextToken (); + try + { + URL url = new URL(baseURL, e); + this.classPath.add(url); + } + catch (java.net.MalformedURLException xx) + { + // Give up + } + } + } + } + catch (IOException ioe) + { + /* ignored */ + } + + this.baseJarURL = baseJarURL; + this.jarfile = jarfile; + } + + /** get resource with the name "name" in the jar url */ + Resource getResource(String name) + { + if (jarfile == null) + return null; + + if (name.startsWith("/")) + name = name.substring(1); + + JarEntry je = jarfile.getJarEntry(name); + if (je != null) + return new JarURLResource(this, name, je); + else + return null; + } + + Manifest getManifest() + { + try + { + return (jarfile == null) ? null : jarfile.getManifest(); + } + catch (IOException ioe) + { + return null; + } + } + + Vector getClassPath() + { + return classPath; + } + } + + static final class JarURLResource extends Resource + { + private final JarEntry entry; + + JarURLResource(JarURLLoader loader, String name, JarEntry entry) + { + super(loader, name); + this.entry = entry; + } + + InputStream getInputStream() throws IOException + { + return ((JarURLLoader) loader).jarfile.getInputStream(entry); + } + + int getLength() + { + return (int) entry.getSize(); + } + + Certificate[] getCertificates() + { + // We have to get the entry from the jar file again, because the + // certificates will not be available until the entire entry has + // been read. + return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name)) + .getCertificates(); + } + + URL getURL() + { + try + { + return new URL(((JarURLLoader) loader).baseJarURL, name, + loader.classloader.getURLStreamHandler("jar")); + } + catch (MalformedURLException e) + { + InternalError ie = new InternalError(); + ie.initCause(e); + throw ie; + } + } + } + + /** + * Loader for remote directories. + */ + static final class RemoteURLLoader extends URLLoader + { + private final String protocol; + + RemoteURLLoader(URLClassLoader classloader, URL url) + { + super(classloader, url); + protocol = url.getProtocol(); + } + + /** + * Get a remote resource. + * Returns null if no such resource exists. + */ + Resource getResource(String name) + { + try + { + URL url = + new URL(baseURL, name, classloader.getURLStreamHandler(protocol)); + URLConnection connection = url.openConnection(); + + // Open the connection and check the stream + // just to be sure it exists. + int length = connection.getContentLength(); + InputStream stream = connection.getInputStream(); + + // We can do some extra checking if it is a http request + if (connection instanceof HttpURLConnection) + { + int response = + ((HttpURLConnection) connection).getResponseCode(); + if (response / 100 != 2) + return null; + } + + if (stream != null) + return new RemoteResource(this, name, url, stream, length); + else + return null; + } + catch (IOException ioe) + { + return null; + } + } + } + + /** + * A resource from some remote location. + */ + static final class RemoteResource extends Resource + { + private final URL url; + private final InputStream stream; + private final int length; + + RemoteResource(RemoteURLLoader loader, String name, URL url, + InputStream stream, int length) + { + super(loader, name); + this.url = url; + this.stream = stream; + this.length = length; + } + + InputStream getInputStream() throws IOException + { + return stream; + } + + public int getLength() + { + return length; + } + + public URL getURL() + { + return url; + } + } + + /** + * A <code>FileURLLoader</code> is a type of <code>URLLoader</code> + * only loading from file url. + */ + static final class FileURLLoader extends URLLoader + { + File dir; //the file for this file url + + FileURLLoader(URLClassLoader classloader, URL url) + { + super(classloader, url); + dir = new File(baseURL.getFile()); + } + + /** get resource with the name "name" in the file url */ + Resource getResource(String name) + { + File file = new File(dir, name); + if (file.exists() && !file.isDirectory()) + return new FileResource(this, name, file); + return null; + } + } + + static final class FileResource extends Resource + { + final File file; + + FileResource(FileURLLoader loader, String name, File file) + { + super(loader, name); + this.file = file; + } + + InputStream getInputStream() throws IOException + { + return new FileInputStream(file); + } + + public int getLength() + { + return (int) file.length(); + } + + public URL getURL() + { + try + { + return new URL(loader.baseURL, name, + loader.classloader.getURLStreamHandler("file")); + } + catch (MalformedURLException e) + { + InternalError ie = new InternalError(); + ie.initCause(e); + throw ie; + } + } + } + + // Constructors + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (<code>SecureClassLoader</code>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the default parent ClassLoader). + * + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls) throws SecurityException + { + super(); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + /** + * Creates a <code>URLClassLoader</code> that gets classes from the supplied + * <code>URL</code>s. + * To determine if this classloader may be created the constructor of + * the super class (<code>SecureClassLoader</code>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent) + throws SecurityException + { + super(parent); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + // Package-private to avoid a trampoline constructor. + /** + * Package-private constructor used by the static + * <code>newInstance(URL[])</code> method. Creates an + * <code>URLClassLoader</code> with the given parent but without any + * <code>URL</code>s yet. This is used to bypass the normal security + * check for creating classloaders, but remembers the security + * context which will be used when defining classes. The + * <code>URL</code>s to load from must be added by the + * <code>newInstance()</code> method in the security context of the + * caller. + * + * @param securityContext the security context of the unprivileged code. + */ + URLClassLoader(ClassLoader parent, AccessControlContext securityContext) + { + super(parent); + this.factory = null; + this.securityContext = securityContext; + } + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (<CODE>SecureClassLoader</CODE>) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the + * protocol handlers of the supplied URLs. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @param factory Used to get the protocol handler for the URLs. + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent, + URLStreamHandlerFactory factory) + throws SecurityException + { + super(parent); + this.securityContext = null; + this.factory = factory; + addURLs(urls); + + // If this factory is still not in factoryCache, add it, + // since we only support three protocols so far, 5 is enough + // for cache initial size + synchronized (factoryCache) + { + if (factory != null && factoryCache.get(factory) == null) + factoryCache.put(factory, new HashMap(5)); + } + } + + // Methods + + /** + * Adds a new location to the end of the internal URL store. + * @param newUrl the location to add + */ + protected void addURL(URL newUrl) + { + urls.add(newUrl); + addURLImpl(newUrl); + } + + private void addURLImpl(URL newUrl) + { + synchronized (urlloaders) + { + if (newUrl == null) + return; // Silently ignore... + + // Reset the toString() value. + thisString = null; + + // Check global cache to see if there're already url loader + // for this url. + URLLoader loader = (URLLoader) urlloaders.get(newUrl); + if (loader == null) + { + String file = newUrl.getFile(); + String protocol = newUrl.getProtocol(); + + // Check that it is not a directory + if (! (file.endsWith("/") || file.endsWith(File.separator))) + loader = new JarURLLoader(this, newUrl); + else if ("file".equals(protocol)) + loader = new FileURLLoader(this, newUrl); + else + loader = new RemoteURLLoader(this, newUrl); + + // Cache it. + urlloaders.put(newUrl, loader); + } + + urlinfos.add(loader); + + Vector extraUrls = loader.getClassPath(); + if (extraUrls != null) + { + Iterator it = extraUrls.iterator(); + while (it.hasNext()) + { + URL url = (URL)it.next(); + URLLoader extraLoader = (URLLoader) urlloaders.get(url); + if (! urlinfos.contains (extraLoader)) + addURLImpl(url); + } + } + + } + } + + /** + * Adds an array of new locations to the end of the internal URL store. + * @param newUrls the locations to add + */ + private void addURLs(URL[] newUrls) + { + for (int i = 0; i < newUrls.length; i++) + addURL(newUrls[i]); + } + + /** + * Defines a Package based on the given name and the supplied manifest + * information. The manifest indicates the tile, version and + * vendor information of the specification and implementation and wheter the + * package is sealed. If the Manifest indicates that the package is sealed + * then the Package will be sealed with respect to the supplied URL. + * + * @exception IllegalArgumentException If this package name already exists + * in this class loader + * @param name The name of the package + * @param manifest The manifest describing the specification, + * implementation and sealing details of the package + * @param url the code source url to seal the package + * @return the defined Package + */ + protected Package definePackage(String name, Manifest manifest, URL url) + throws IllegalArgumentException + { + Attributes attr = manifest.getMainAttributes(); + String specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); + String specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); + String specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); + String implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); + String implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); + String implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); + + // Look if the Manifest indicates that this package is sealed + // XXX - most likely not completely correct! + // Shouldn't we also check the sealed attribute of the complete jar? + // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled + // But how do we get that jar manifest here? + String sealed = attr.getValue(Attributes.Name.SEALED); + if ("false".equals(sealed)) + // make sure that the URL is null so the package is not sealed + url = null; + + return definePackage(name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor, url); + } + + /** + * Finds (the first) class by name from one of the locations. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param className the classname to find + * @exception ClassNotFoundException when the class could not be found or + * loaded + * @return a Class object representing the found class + */ + protected Class findClass(final String className) + throws ClassNotFoundException + { + // Just try to find the resource by the (almost) same name + String resourceName = className.replace('.', '/') + ".class"; + Resource resource = findURLResource(resourceName); + if (resource == null) + throw new ClassNotFoundException(className + " not found in " + this); + + // Try to read the class data, create the CodeSource, Package and + // construct the class (and watch out for those nasty IOExceptions) + try + { + byte[] data; + InputStream in = resource.getInputStream(); + try + { + int length = resource.getLength(); + if (length != -1) + { + // We know the length of the data. + // Just try to read it in all at once + data = new byte[length]; + int pos = 0; + while (length - pos > 0) + { + int len = in.read(data, pos, length - pos); + if (len == -1) + throw new EOFException("Not enough data reading from: " + + in); + pos += len; + } + } + else + { + // We don't know the data length. + // Have to read it in chunks. + ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + byte[] b = new byte[4096]; + int l = 0; + while (l != -1) + { + l = in.read(b); + if (l != -1) + out.write(b, 0, l); + } + data = out.toByteArray(); + } + } + finally + { + in.close(); + } + final byte[] classData = data; + + // Now get the CodeSource + final CodeSource source = resource.getCodeSource(); + + // Find out package name + String packageName = null; + int lastDot = className.lastIndexOf('.'); + if (lastDot != -1) + packageName = className.substring(0, lastDot); + + if (packageName != null && getPackage(packageName) == null) + { + // define the package + Manifest manifest = resource.loader.getManifest(); + if (manifest == null) + definePackage(packageName, null, null, null, null, null, null, + null); + else + definePackage(packageName, manifest, resource.loader.baseURL); + } + + // And finally construct the class! + SecurityManager sm = System.getSecurityManager(); + Class result = null; + if (sm != null && securityContext != null) + { + result = (Class)AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + return defineClass(className, classData, + 0, classData.length, + source); + } + }, securityContext); + } + else + result = defineClass(className, classData, 0, classData.length, source); + + super.setSigners(result, resource.getCertificates()); + return result; + } + catch (IOException ioe) + { + ClassNotFoundException cnfe; + cnfe = new ClassNotFoundException(className + " not found in " + this); + cnfe.initCause(ioe); + throw cnfe; + } + } + + // Cached String representation of this URLClassLoader + private String thisString; + + /** + * Returns a String representation of this URLClassLoader giving the + * actual Class name, the URLs that are searched and the parent + * ClassLoader. + */ + public String toString() + { + synchronized (urlloaders) + { + if (thisString == null) + { + StringBuffer sb = new StringBuffer(); + sb.append(this.getClass().getName()); + sb.append("{urls=[" ); + URL[] thisURLs = getURLs(); + for (int i = 0; i < thisURLs.length; i++) + { + sb.append(thisURLs[i]); + if (i < thisURLs.length - 1) + sb.append(','); + } + sb.append(']'); + sb.append(", parent="); + sb.append(getParent()); + sb.append('}'); + thisString = sb.toString(); + } + return thisString; + } + } + + /** + * Finds the first occurrence of a resource that can be found. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param resourceName the resource name to look for + * @return the URLResource for the resource if found, null otherwise + */ + private Resource findURLResource(String resourceName) + { + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + if (loader == null) + continue; + + Resource resource = loader.getResource(resourceName); + if (resource != null) + return resource; + } + return null; + } + + /** + * Finds the first occurrence of a resource that can be found. + * + * @param resourceName the resource name to look for + * @return the URL if found, null otherwise + */ + public URL findResource(String resourceName) + { + Resource resource = findURLResource(resourceName); + if (resource != null) + return resource.getURL(); + + // Resource not found + return null; + } + + /** + * If the URLStreamHandlerFactory has been set this return the appropriate + * URLStreamHandler for the given protocol, if not set returns null. + * + * @param protocol the protocol for which we need a URLStreamHandler + * @return the appropriate URLStreamHandler or null + */ + URLStreamHandler getURLStreamHandler(String protocol) + { + if (factory == null) + return null; + + URLStreamHandler handler; + synchronized (factoryCache) + { + // Check if there're handler for the same protocol in cache. + HashMap cache = (HashMap) factoryCache.get(factory); + handler = (URLStreamHandler) cache.get(protocol); + if (handler == null) + { + // Add it to cache. + handler = factory.createURLStreamHandler(protocol); + cache.put(protocol, handler); + } + } + return handler; + } + + /** + * Finds all the resources with a particular name from all the locations. + * + * @exception IOException when an error occurs accessing one of the + * locations + * @param resourceName the name of the resource to lookup + * @return a (possible empty) enumeration of URLs where the resource can be + * found + */ + public Enumeration findResources(String resourceName) + throws IOException + { + Vector resources = new Vector(); + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + Resource resource = loader.getResource(resourceName); + if (resource != null) + resources.add(resource.getURL()); + } + return resources.elements(); + } + + /** + * Returns the permissions needed to access a particular code + * source. These permissions includes those returned by + * <code>SecureClassLoader.getPermissions()</code> and the actual + * permissions to access the objects referenced by the URL of the + * code source. The extra permissions added depend on the protocol + * and file portion of the URL in the code source. If the URL has + * the "file" protocol ends with a '/' character then it must be a + * directory and a file Permission to read everything in that + * directory and all subdirectories is added. If the URL had the + * "file" protocol and doesn't end with a '/' character then it must + * be a normal file and a file permission to read that file is + * added. If the <code>URL</code> has any other protocol then a + * socket permission to connect and accept connections from the host + * portion of the URL is added. + * + * @param source The codesource that needs the permissions to be accessed + * @return the collection of permissions needed to access the code resource + * @see java.security.SecureClassLoader#getPermissions() + */ + protected PermissionCollection getPermissions(CodeSource source) + { + // XXX - This implementation does exactly as the Javadoc describes. + // But maybe we should/could use URLConnection.getPermissions()? + // First get the permissions that would normally be granted + PermissionCollection permissions = super.getPermissions(source); + + // Now add any extra permissions depending on the URL location. + URL url = source.getLocation(); + String protocol = url.getProtocol(); + if (protocol.equals("file")) + { + String file = url.getFile(); + + // If the file end in / it must be an directory. + if (file.endsWith("/") || file.endsWith(File.separator)) + { + // Grant permission to read everything in that directory and + // all subdirectories. + permissions.add(new FilePermission(file + "-", "read")); + } + else + { + // It is a 'normal' file. + // Grant permission to access that file. + permissions.add(new FilePermission(file, "read")); + } + } + else + { + // Grant permission to connect to and accept connections from host + String host = url.getHost(); + if (host != null) + permissions.add(new SocketPermission(host, "connect,accept")); + } + + return permissions; + } + + /** + * Returns all the locations that this class loader currently uses the + * resolve classes and resource. This includes both the initially supplied + * URLs as any URLs added later by the loader. + * @return All the currently used URLs + */ + public URL[] getURLs() + { + return (URL[]) urls.toArray(new URL[urls.size()]); + } + + /** + * Creates a new instance of a <code>URLClassLoader</code> that gets + * classes from the supplied <code>URL</code>s. This class loader + * will have as parent the standard system class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given <code>URL</code>s + */ + public static URLClassLoader newInstance(URL[] urls) + throws SecurityException + { + return newInstance(urls, null); + } + + /** + * Creates a new instance of a <code>URLClassLoader</code> that gets + * classes from the supplied <code>URL</code>s and with the supplied + * loader as parent class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * @param parent the parent class loader + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given <code>URL</code>s + */ + public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) + return new URLClassLoader(urls, parent); + else + { + final Object securityContext = sm.getSecurityContext(); + + // XXX - What to do with anything else then an AccessControlContext? + if (! (securityContext instanceof AccessControlContext)) + throw new SecurityException("securityContext must be AccessControlContext: " + + securityContext); + + URLClassLoader loader = + (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return new URLClassLoader(parent, + (AccessControlContext) securityContext); + } + }); + loader.addURLs(urls); + return loader; + } + } +} diff --git a/libjava/classpath/java/net/URLConnection.java b/libjava/classpath/java/net/URLConnection.java new file mode 100644 index 00000000000..0a12d588d9a --- /dev/null +++ b/libjava/classpath/java/net/URLConnection.java @@ -0,0 +1,1021 @@ +/* URLConnection.java -- Abstract superclass for reading from URL's + Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AllPermission; +import java.security.Permission; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: One guessContentTypeFrom... methods not implemented. + * getContent method assumes content type from response; see comment there. + */ +/** + * This class models a connection that retrieves the information pointed + * to by a URL object. This is typically a connection to a remote node + * on the network, but could be a simple disk read. + * <p> + * A URLConnection object is normally created by calling the openConnection() + * method of a URL object. This method is somewhat misnamed because it does + * not actually open the connection. Instead, it return an unconnected + * instance of this object. The caller then has the opportunity to set + * various connection options prior to calling the actual connect() method. + * <p> + * After the connection has been opened, there are a number of methods in + * this class that access various attributes of the data, typically + * represented by headers sent in advance of the actual data itself. + * <p> + * Also of note are the getInputStream and getContent() methods which allow + * the caller to retrieve the actual data from the connection. Note that + * for some types of connections, writing is also allowed. The setDoOutput() + * method must be called prior to connecing in order to enable this, then + * the getOutputStream method called after the connection in order to + * obtain a stream to write the output to. + * <p> + * The getContent() method is of particular note. This method returns an + * Object that encapsulates the data returned. There is no way do determine + * the type of object that will be returned in advance. This is determined + * by the actual content handlers as described in the description of that + * method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class URLConnection +{ + /** + * This is an object that maps filenames to MIME types. The interface + * to do this is implemented by this class, so just create an empty + * instance and store it here. + */ + private static FileNameMap fileNameMap; + + /** + * This is the ContentHandlerFactory set by the caller, if any + */ + private static ContentHandlerFactory factory; + + /** + * This is the default value that will be used to determine whether or + * not user interaction should be allowed. + */ + private static boolean defaultAllowUserInteraction; + + /** + * This is the default flag indicating whether or not to use caches to + * store the data returned from a server + */ + private static boolean defaultUseCaches = true; + + /** + * This variable determines whether or not interaction is allowed with + * the user. For example, to prompt for a username and password. + */ + protected boolean allowUserInteraction; + + /** + * Indicates whether or not a connection has been established to the + * destination specified in the URL + */ + protected boolean connected; + + /** + * Indicates whether or not input can be read from this URL + */ + protected boolean doInput = true; + + /** + * Indicates whether or not output can be sent to this URL + */ + protected boolean doOutput; + + /** + * If this flag is set, the protocol is allowed to cache data whenever + * it can (caching is not guaranteed). If it is not set, the protocol + * must a get a fresh copy of the data. + * <p> + * This field is set by the setUseCaches method and returned by the + * getUseCaches method. + * + * Its default value is that determined by the last invocation of + * setDefaultUseCaches + */ + protected boolean useCaches; + + /** + * If this value is non-zero, then the connection will only attempt to + * fetch the document pointed to by the URL if the document has been + * modified more recently than the date set in this variable. That date + * should be specified as the number of seconds since 1/1/1970 GMT. + */ + protected long ifModifiedSince; + + /** + * This is the URL associated with this connection + */ + protected URL url; + private static SimpleDateFormat[] dateFormats; + private static boolean dateformats_initialized; + + /* Cached ParsePosition, used when parsing dates. */ + private ParsePosition position; + + /** + * Creates a URL connection to a given URL. A real connection is not made. + * Use #connect to do this. + * + * @param url The Object to create the URL connection to + * + * @see URLConnection#connect() + */ + protected URLConnection(URL url) + { + // Set up all our instance variables + this.url = url; + allowUserInteraction = defaultAllowUserInteraction; + useCaches = defaultUseCaches; + } + + /** + * Establishes the actual connection to the URL associated with this + * connection object + * + * @exception IOException if an error occurs + */ + public abstract void connect() throws IOException; + + /** + * Returns the URL object associated with this connection + * + * @return The URL for this connection. + */ + public URL getURL() + { + return url; + } + + /** + * Returns the value of the content-length header field or -1 if the value + * is not known or not present. + * + * @return The content-length field + */ + public int getContentLength() + { + return getHeaderFieldInt("content-length", -1); + } + + /** + * Returns the the content-type of the data pointed to by the URL. This + * method first tries looking for a content-type header. If that is not + * present, it attempts to use the file name to determine the content's + * MIME type. If that is unsuccessful, the method returns null. The caller + * may then still attempt to determine the MIME type by a call to + * guessContentTypeFromStream() + * + * @return The content MIME type + */ + public String getContentType() + { + return getHeaderField("content-type"); + } + + /** + * Returns the value of the content-encoding field or null if it is not + * known or not present. + * + * @return The content-encoding field + */ + public String getContentEncoding() + { + return getHeaderField("content-encoding"); + } + + /** + * Returns the value of the expires header or 0 if not known or present. + * If populated, the return value is number of seconds since midnight + * on 1/1/1970 GMT. + * + * @return The expiration time. + */ + public long getExpiration() + { + return getHeaderFieldDate("expires", 0L); + } + + /** + * Returns the date of the document pointed to by the URL as reported in + * the date field of the header or 0 if the value is not present or not + * known. If populated, the return value is number of seconds since + * midnight on 1/1/1970 GMT. + * + * @return The document date + */ + public long getDate() + { + return getHeaderFieldDate("date", 0L); + } + + /** + * Returns the value of the last-modified header field or 0 if not known known + * or not present. If populated, the return value is the number of seconds + * since midnight on 1/1/1970. + * + * @return The last modified time + */ + public long getLastModified() + { + return getHeaderFieldDate("last-modified", 0L); + } + + /** + * Return a String representing the header value at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * getHeaderFieldKey(int) method allows access to the corresponding key + * for this header field + * + * @param index The index into the header field list to retrieve the value for + * + * @return The header value or null if index is past the end of the headers + */ + public String getHeaderField(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns a String representing the value of the header field having + * the named key. Returns null if the header field does not exist. + * + * @param name The key of the header field + * + * @return The value of the header field as a String + */ + public String getHeaderField(String name) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns a map of all sent header fields + * + * @return all header fields + * + * @since 1.4 + */ + public Map getHeaderFields() + { + // Subclasses for specific protocols override this. + return Collections.EMPTY_MAP; + } + + /** + * Returns the value of the named header field as an int. If the field + * is not present or cannot be parsed as an integer, the default value + * will be returned. + * + * @param name The header field key to lookup + * @param defaultValue The defaule value if the header field is not found + * or can't be parsed. + * + * @return The value of the header field or the default value if the field + * is missing or malformed + */ + public int getHeaderFieldInt(String name, int defaultValue) + { + String value = getHeaderField(name); + + if (value == null) + return defaultValue; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + /** + * Returns the value of the named header field as a date. This date will + * be the number of seconds since midnight 1/1/1970 GMT or the default + * value if the field is not present or cannot be converted to a date. + * + * @param name The name of the header field + * @param defaultValue The default date if the header field is not found + * or can't be converted. + * + * @return Returns the date value of the header filed or the default value + * if the field is missing or malformed + */ + public long getHeaderFieldDate(String name, long defaultValue) + { + if (! dateformats_initialized) + initializeDateFormats(); + + if (position == null) + position = new ParsePosition(0); + + long result = defaultValue; + String str = getHeaderField(name); + + if (str != null) + { + for (int i = 0; i < dateFormats.length; i++) + { + SimpleDateFormat df = dateFormats[i]; + position.setIndex(0); + position.setErrorIndex(0); + Date date = df.parse(str, position); + if (date != null) + return date.getTime(); + } + } + + return result; + } + + /** + * Returns a String representing the header key at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * getHeaderField(int) method allows access to the corresponding value for + * this tag. + * + * @param index The index into the header field list to retrieve the key for. + * + * @return The header field key or null if index is past the end + * of the headers. + */ + public String getHeaderFieldKey(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * This method returns the content of the document pointed to by the + * URL as an Object. The type of object depends on the MIME type of + * the object and particular content hander loaded. Most text type + * content handlers will return a subclass of + * <code>InputStream</code>. Images usually return a class that + * implements <code>ImageProducer</code>. There is not guarantee + * what type of object will be returned, however. + * + * <p>This class first determines the MIME type of the content, then + * creates a ContentHandler object to process the input. If the + * <code>ContentHandlerFactory</code> is set, then that object is + * called to load a content handler, otherwise a class called + * gnu.java.net.content.<content_type> is tried. If this + * handler does not exist, the method will simple return the + * <code>InputStream</code> returned by + * <code>getInputStream()</code>. Note that the default + * implementation of <code>getInputStream()</code> throws a + * <code>UnknownServiceException</code> so subclasses are encouraged + * to override this method.</p> + * + * @return the content + * + * @exception IOException If an error with the connection occurs. + * @exception UnknownServiceException If the protocol does not support the + * content type at all. + */ + public Object getContent() throws IOException + { + if (!connected) + connect(); + + // FIXME: Doc indicates that other criteria should be applied as + // heuristics to determine the true content type, e.g. see + // guessContentTypeFromName() and guessContentTypeFromStream methods + // as well as FileNameMap class & fileNameMap field & get/set methods. + String type = getContentType(); + ContentHandler ch = getContentHandler(type); + + if (ch != null) + return ch.getContent(this); + + return getInputStream(); + } + + /** + * Retrieves the content of this URLConnection + * + * @param classes The allowed classes for the content + * + * @return the content + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support the + * content type + */ + public Object getContent(Class[] classes) throws IOException + { + // FIXME: implement this + return getContent(); + } + + /** + * This method returns a <code>Permission</code> object representing the + * permissions required to access this URL. This method returns + * <code>java.security.AllPermission</code> by default. Subclasses should + * override it to return a more specific permission. For example, an + * HTTP URL should return an instance of <code>SocketPermission</code> + * for the appropriate host and port. + * <p> + * Note that because of items such as HTTP redirects, the permission + * object returned might be different before and after connecting. + * + * @return A Permission object + * + * @exception IOException If the computation of the permission requires + * network or file I/O and an exception occurs while computing it + */ + public Permission getPermission() throws IOException + { + // Subclasses may override this. + return new AllPermission(); + } + + /** + * Returns an InputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An InputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support input + */ + public InputStream getInputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support input."); + } + + /** + * Returns an OutputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An OutputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support output + */ + public OutputStream getOutputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support output."); + } + + /** + * The methods prints the value of this object as a String by calling the + * toString() method of its associated URL. Overrides Object.toString() + * + * @return A String representation of this object + */ + public String toString() + { + return this.getClass().getName() + ":" + url.toString(); + } + + /** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @param input <code>true</code> if input is to be done, + * <code>false</code> otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoInput(boolean input) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doInput = input; + } + + /** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @return true if input is to be done, false otherwise + */ + public boolean getDoInput() + { + return doInput; + } + + /** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. The default value is false, so this method can + * be used to override the default + * + * @param output ture if output is to be done, false otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoOutput(boolean output) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doOutput = output; + } + + /** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. This defaults to false. + * + * @return true if output is to be done, false otherwise + */ + public boolean getDoOutput() + { + return doOutput; + } + + /** + * Sets a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @param allow true if user interaction should be allowed, false otherwise. + * + * @exception IllegalStateException If already connected + */ + public void setAllowUserInteraction(boolean allow) + { + allowUserInteraction = allow; + } + + /** + * Returns a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @return true if user interaction is allowed, false otherwise + */ + public boolean getAllowUserInteraction() + { + return allowUserInteraction; + } + + /** + * Sets the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @param allow true to allow user interaction, false otherwise + */ + public static void setDefaultAllowUserInteraction(boolean allow) + { + defaultAllowUserInteraction = allow; + } + + /** + * Returns the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @return true if user interaction is allowed, false otherwise + */ + public static boolean getDefaultAllowUserInteraction() + { + return defaultAllowUserInteraction; + } + + /** + * Sets a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @param usecaches The new value + * + * @exception IllegalStateException If already connected + */ + public void setUseCaches(boolean usecaches) + { + if (connected) + throw new IllegalStateException("Already connected"); + + useCaches = usecaches; + } + + /** + * Returns a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @return true if caching should be used if possible, false otherwise + */ + public boolean getUseCaches() + { + return useCaches; + } + + /** + * Sets the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * passed should be 0 if this feature is to be disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise. + * + * @param ifmodifiedsince The new value in milliseconds + * since January 1, 1970 GMT + * + * @exception IllegalStateException If already connected + */ + public void setIfModifiedSince(long ifmodifiedsince) + { + if (connected) + throw new IllegalStateException("Already connected"); + + ifModifiedSince = ifmodifiedsince; + } + + /** + * Returns the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * returned will be 0 if this feature is disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise + * + * @return The ifModifiedSince value + */ + public long getIfModifiedSince() + { + return ifModifiedSince; + } + + /** + * Returns the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @return true if caches will be used, false otherwise + */ + public boolean getDefaultUseCaches() + { + return defaultUseCaches; + } + + /** + * Sets the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @param use true to use caches if possible by default, false otherwise + */ + public void setDefaultUseCaches(boolean use) + { + defaultUseCaches = use; + } + + /** + * Sets the value of the named request property + * + * @param key The name of the property + * @param value The value of the property + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String key) + * @see URLConnection#addRequestProperty(String key, String value) + * + * @since 1.4 + */ + public void setRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support setting + // header fields in the request. + } + + /** + * Adds a new request property by a key/value pair. + * This method does not overwrite existing properties with the same key. + * + * @param key Key of the property to add + * @param value Value of the Property to add + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String key) + * @see URLConnection#setRequestProperty(String key, String value) + * + * @since 1.4 + */ + public void addRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support adding + // header fields in the request. + } + + /** + * Returns the value of the named request property. + * + * @param key The name of the property + * + * @return Value of the property + * + * @exception IllegalStateException If already connected + * + * @see URLConnection#setRequestProperty(String key, String value) + * @see URLConnection#addRequestProperty(String key, String value) + */ + public String getRequestProperty(String key) + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return null; + } + + /** + * Returns an unmodifiable Map containing the request properties. + * + * @return The map of properties + * + * @exception IllegalStateException If already connected + * + * @since 1.4 + */ + public Map getRequestProperties() + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return Collections.EMPTY_MAP; + } + + /** + * Sets the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property name the default is being set for + * @param value The value to set the default to + * + * @deprecated 1.3 The method setRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#setRequestProperty(String key, String value) + */ + public static void setDefaultRequestProperty(String key, String value) + { + // This method does nothing since JDK 1.3. + } + + /** + * Returns the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property to return the default value of + * + * @return The value of the default property or null if not available + * + * @deprecated 1.3 The method getRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#getRequestProperty(String key) + */ + public static String getDefaultRequestProperty(String key) + { + // This method does nothing since JDK 1.3. + return null; + } + + /** + * Set's the ContentHandlerFactory for an application. This can be called + * once and only once. If it is called again, then an Error is thrown. + * Unlike for other set factory methods, this one does not do a security + * check prior to setting the factory. + * + * @param factory The ContentHandlerFactory for this application + * + * @exception Error If the factory has already been defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory) + { + if (URLConnection.factory != null) + throw new Error("ContentHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + URLConnection.factory = factory; + } + + /** + * Returns the MIME type of a file based on the name of the file. This + * works by searching for the file's extension in a list of file extensions + * and returning the MIME type associated with it. If no type is found, + * then a MIME type of "application/octet-stream" will be returned. + * + * @param filename The filename to determine the MIME type for + * + * @return The MIME type String + * + * @specnote public since JDK 1.4 + */ + public static String guessContentTypeFromName(String filename) + { + return getFileNameMap().getContentTypeFor(filename.toLowerCase()); + } + + /** + * Returns the MIME type of a stream based on the first few characters + * at the beginning of the stream. This routine can be used to determine + * the MIME type if a server is believed to be returning an incorrect + * MIME type. This method returns "application/octet-stream" if it + * cannot determine the MIME type. + * <p> + * NOTE: Overriding MIME types sent from the server can be obnoxious + * to user's. See Internet Exploder 4 if you don't believe me. + * + * @param is The InputStream to determine the MIME type from + * + * @return The MIME type + * + * @exception IOException If an error occurs + */ + public static String guessContentTypeFromStream(InputStream is) + throws IOException + { + return "application/octet-stream"; + } + + /** + * This method returns the <code>FileNameMap</code> object being used + * to decode MIME types by file extension. + * + * @return The <code>FileNameMap</code>. + * + * @since 1.2 + */ + public static synchronized FileNameMap getFileNameMap() + { + // Delayed initialization. + if (fileNameMap == null) + fileNameMap = new MimeTypeMapper(); + + return fileNameMap; + } + + /** + * This method set the <code>FileNameMap</code> object being used + * to decode MIME types by file extension. + * + * @param map The <code>FileNameMap</code>. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + * + * @since 1.2 + */ + public static synchronized void setFileNameMap(FileNameMap map) + { + // Throw an exception if an extant security manager precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + fileNameMap = map; + } + + private ContentHandler getContentHandler(String contentType) + { + // No content type so just handle it as the default. + if (contentType == null || contentType.equals("")) + return null; + + ContentHandler handler = null; + + // If a non-default factory has been set, use it. + if (factory != null) + handler = factory.createContentHandler(contentType); + + // Then try our default class. + try + { + String typeClass = contentType.replace('/', '.'); + + // Deal with "Content-Type: text/html; charset=ISO-8859-1". + int parameterBegin = typeClass.indexOf(';'); + if (parameterBegin >= 1) + typeClass = typeClass.substring(0, parameterBegin); + + Class cls = Class.forName("gnu.java.net.content." + typeClass); + Object obj = cls.newInstance(); + + if (obj instanceof ContentHandler) + { + handler = (ContentHandler) obj; + return handler; + } + } + catch (ClassNotFoundException e) + { + // Ignore. + } + catch (InstantiationException e) + { + // Ignore. + } + catch (IllegalAccessException e) + { + // Ignore. + } + + return handler; + } + + // We don't put these in a static initializer, because it creates problems + // with initializer co-dependency: SimpleDateFormat's constructors eventually + // depend on URLConnection (via the java.text.*Symbols classes). + private static synchronized void initializeDateFormats() + { + if (dateformats_initialized) + return; + + Locale locale = new Locale("En", "Us", "Unix"); + dateFormats = new SimpleDateFormat[3]; + dateFormats[0] = + new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale); + dateFormats[1] = + new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale); + dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale); + dateformats_initialized = true; + } +} diff --git a/libjava/classpath/java/net/URLDecoder.java b/libjava/classpath/java/net/URLDecoder.java new file mode 100644 index 00000000000..ca40c386a52 --- /dev/null +++ b/libjava/classpath/java/net/URLDecoder.java @@ -0,0 +1,180 @@ +/* URLDecoder.java -- Class to decode URL's from encoded form. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.UnsupportedEncodingException; + + +/** + * This utility class contains static methods that converts a + * string encoded in the x-www-form-urlencoded format to the original + * text. The x-www-form-urlencoded format replaces certain disallowed + * characters with encoded equivalents. All upper case and lower case + * letters in the US alphabet remain as is, the space character (' ') + * is replaced with '+' sign, and all other characters are converted to a + * "%XX" format where XX is the hexadecimal representation of that character + * in a given character encoding (default is "UTF-8"). + * <p> + * This method is very useful for decoding strings sent to CGI scripts + * + * Written using on-line Java Platform 1.2/1.4 API Specification. + * Status: Believed complete and correct. + * + * @since 1.2 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (documentation comments) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLDecoder +{ + /** + * Public contructor. Note that this class has only static methods. + */ + public URLDecoder() + { + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the default encoding "UTF-8" to decode the hex encoded + * unsafe characters. + * + * @param s the String to convert + * + * @return the converted String + * + * @deprecated + */ + public static String decode(String s) + { + try + { + return decode(s, "UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since UTF-8 encoding should always be supported + return s; + } + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the given character encoding to decode the hex encoded + * unsafe characters. + * + * This implementation will decode the string even if it contains + * unsafe characters (characters that should have been encoded) or if the + * two characters following a % do not represent a hex encoded byte. + * In those cases the unsafe character or the % character will be added + * verbatim to the decoded result. + * + * @param s the String to convert + * @param encoding the character encoding to use the decode the hex encoded + * unsafe characters + * + * @return the converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String decode(String s, String encoding) + throws UnsupportedEncodingException + { + // First convert all '+' characters to spaces. + String str = s.replace('+', ' '); + + // Then go through the whole string looking for byte encoded characters + int i; + int start = 0; + byte[] bytes = null; + int length = str.length(); + StringBuffer result = new StringBuffer(length); + while ((i = str.indexOf('%', start)) >= 0) + { + // Add all non-encoded characters to the result buffer + result.append(str.substring(start, i)); + start = i; + + // Get all consecutive encoded bytes + while ((i + 2 < length) && (str.charAt(i) == '%')) + i += 3; + + // Decode all these bytes + if ((bytes == null) || (bytes.length < ((i - start) / 3))) + bytes = new byte[((i - start) / 3)]; + + int index = 0; + try + { + while (start < i) + { + String sub = str.substring(start + 1, start + 3); + bytes[index] = (byte) Integer.parseInt(sub, 16); + index++; + start += 3; + } + } + catch (NumberFormatException nfe) + { + // One of the hex encoded strings was bad + } + + // Add the bytes as characters according to the given encoding + result.append(new String(bytes, 0, index, encoding)); + + // Make sure we skip to just after a % sign + // There might not have been enough encoded characters after the % + // or the hex chars were not actually hex chars (NumberFormatException) + if (start < length && s.charAt(start) == '%') + { + result.append('%'); + start++; + } + } + + // Add any characters left + if (start < str.length()) + result.append(str.substring(start)); + + return result.toString(); + } +} // class URLDecoder diff --git a/libjava/classpath/java/net/URLEncoder.java b/libjava/classpath/java/net/URLEncoder.java new file mode 100644 index 00000000000..dacc3848e37 --- /dev/null +++ b/libjava/classpath/java/net/URLEncoder.java @@ -0,0 +1,184 @@ +/* URLEncoder.java -- Class to convert strings to a properly encoded URL + Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.UnsupportedEncodingException; + + +/* + * Written using on-line Java Platform 1.2/1.4 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This utility class contains static methods that converts a + * string into a fully encoded URL string in x-www-form-urlencoded + * format. This format replaces certain disallowed characters with + * encoded equivalents. All upper case and lower case letters in the + * US alphabet remain as is, the space character (' ') is replaced with + * '+' sign, and all other characters are converted to a "%XX" format + * where XX is the hexadecimal representation of that character in a + * certain encoding (by default, the platform encoding, though the + * standard is "UTF-8"). + * <p> + * This method is very useful for encoding strings to be sent to CGI scripts + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLEncoder +{ + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the default encoding. The standard encoding is + * "UTF-8", and the two-argument form of this method should be used + * instead. + * + * @param s The String to convert + * + * @return The converted String + * + * @deprecated + */ + public static String encode(String s) + { + try + { + // We default to 8859_1 for compatibility with the same + // default elsewhere in the library. + return encode(s, System.getProperty("file.encoding", "8859_1")); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since default should always be supported + return s; + } + } + + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the character encoding to hex-encode the unsafe characters. + * + * @param s The String to convert + * @param encoding The encoding to use for unsafe characters + * + * @return The converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String encode(String s, String encoding) + throws UnsupportedEncodingException + { + int length = s.length(); + int start = 0; + int i = 0; + + StringBuffer result = new StringBuffer(length); + while (true) + { + while (i < length && isSafe(s.charAt(i))) + i++; + + // Safe character can just be added + result.append(s.substring(start, i)); + + // Are we done? + if (i >= length) + return result.toString(); + else if (s.charAt(i) == ' ') + { + result.append('+'); // Replace space char with plus symbol. + i++; + } + else + { + // Get all unsafe characters + start = i; + char c; + while (i < length && (c = s.charAt(i)) != ' ' && ! isSafe(c)) + i++; + + // Convert them to %XY encoded strings + String unsafe = s.substring(start, i); + byte[] bytes = unsafe.getBytes(encoding); + for (int j = 0; j < bytes.length; j++) + { + result.append('%'); + int val = bytes[j]; + result.append(hex.charAt((val & 0xf0) >> 4)); + result.append(hex.charAt(val & 0x0f)); + } + } + start = i; + } + } + + /** + * Private static method that returns true if the given char is either + * a uppercase or lowercase letter from 'a' till 'z', or a digit froim + * '0' till '9', or one of the characters '-', '_', '.' or '*'. Such + * 'safe' character don't have to be url encoded. + */ + private static boolean isSafe(char c) + { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' + || c == '*'); + } + + /** + * Private constructor that does nothing. Included to avoid a default + * public constructor being created by the compiler. + */ + private URLEncoder() + { + } + + /** + * Used to convert to hex. We don't use Integer.toHexString, since + * it converts to lower case (and the Sun docs pretty clearly + * specify upper case here), and because it doesn't provide a + * leading 0. + */ + private static final String hex = "0123456789ABCDEF"; +} diff --git a/libjava/classpath/java/net/URLStreamHandler.java b/libjava/classpath/java/net/URLStreamHandler.java new file mode 100644 index 00000000000..57ce2dfa290 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandler.java @@ -0,0 +1,532 @@ +/* URLStreamHandler.java -- Abstract superclass for all protocol handlers + Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.File; +import java.io.IOException; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class is the superclass of all URL protocol handlers. The URL + * class loads the appropriate protocol handler to establish a connection + * to a (possibly) remote service (eg, "http", "ftp") and to do protocol + * specific parsing of URL's. Refer to the URL class documentation for + * details on how that class locates and loads protocol handlers. + * <p> + * A protocol handler implementation should override the openConnection() + * method, and optionally override the parseURL() and toExternalForm() + * methods if necessary. (The default implementations will parse/write all + * URL's in the same form as http URL's). A protocol specific subclass + * of URLConnection will most likely need to be created as well. + * <p> + * Note that the instance methods in this class are called as if they + * were static methods. That is, a URL object to act on is passed with + * every call rather than the caller assuming the URL is stored in an + * instance variable of the "this" object. + * <p> + * The methods in this class are protected and accessible only to subclasses. + * URLStreamConnection objects are intended for use by the URL class only, + * not by other classes (unless those classes are implementing protocols). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URL + */ +public abstract class URLStreamHandler +{ + /** + * Creates a URLStreamHander + */ + public URLStreamHandler() + { + } + + /** + * Returns a URLConnection for the passed in URL. Note that this should + * not actually create the connection to the (possibly) remote host, but + * rather simply return a URLConnection object. The connect() method of + * URL connection is used to establish the actual connection, possibly + * after the caller sets up various connection options. + * + * @param url The URL to get a connection object for + * + * @return A URLConnection object for the given URL + * + * @exception IOException If an error occurs + */ + protected abstract URLConnection openConnection(URL url) + throws IOException; + + /** + * This method parses the string passed in as a URL and set's the + * instance data fields in the URL object passed in to the various values + * parsed out of the string. The start parameter is the position to start + * scanning the string. This is usually the position after the ":" which + * terminates the protocol name. The end parameter is the position to + * stop scanning. This will be either the end of the String, or the + * position of the "#" character, which separates the "file" portion of + * the URL from the "anchor" portion. + * <p> + * This method assumes URL's are formatted like http protocol URL's, so + * subclasses that implement protocols with URL's the follow a different + * syntax should override this method. The lone exception is that if + * the protocol name set in the URL is "file", this method will accept + * an empty hostname (i.e., "file:///"), which is legal for that protocol + * + * @param url The URL object in which to store the results + * @param spec The String-ized URL to parse + * @param start The position in the string to start scanning from + * @param end The position in the string to stop scanning + */ + protected void parseURL(URL url, String spec, int start, int end) + { + String host = url.getHost(); + int port = url.getPort(); + String file = url.getFile(); + String ref = url.getRef(); + String userInfo = url.getUserInfo(); + String authority = url.getAuthority(); + String query = null; + + // On Windows we need to change \ to / for file URLs + char separator = File.separatorChar; + if (url.getProtocol().equals("file") && separator != '/') + { + file = file.replace(separator, '/'); + spec = spec.replace(separator, '/'); + } + + if (spec.regionMatches(start, "//", 0, 2)) + { + String genuineHost; + int hostEnd; + int colon; + int at_host; + + start += 2; + int slash = spec.indexOf('/', start); + if (slash >= 0) + hostEnd = slash; + else + hostEnd = end; + + authority = host = spec.substring(start, hostEnd); + + // We first need a genuine host name (with userinfo). + // So we check for '@': if it's present check the port in the + // section after '@' in the other case check it in the full string. + // P.S.: We don't care having '@' at the beginning of the string. + if ((at_host = host.indexOf('@')) >= 0) + { + genuineHost = host.substring(at_host); + userInfo = host.substring(0, at_host); + } + else + genuineHost = host; + + // Look for optional port number. It is valid for the non-port + // part of the host name to be null (e.g. a URL "http://:80"). + // TBD: JDK 1.2 in this case sets host to null rather than ""; + // this is undocumented and likely an unintended side effect in 1.2 + // so we'll be simple here and stick with "". Note that + // "http://" or "http:///" produce a "" host in JDK 1.2. + if ((colon = genuineHost.indexOf(':')) >= 0) + { + try + { + port = Integer.parseInt(genuineHost.substring(colon + 1)); + } + catch (NumberFormatException e) + { + // Ignore invalid port values; port is already set to u's + // port. + } + + // Now we must cut the port number in the original string. + if (at_host >= 0) + host = host.substring(0, at_host + colon); + else + host = host.substring(0, colon); + } + file = null; + start = hostEnd; + } + else if (host == null) + host = ""; + + if (file == null || file.length() == 0 + || (start < end && spec.charAt(start) == '/')) + { + // No file context available; just spec for file. + // Or this is an absolute path name; ignore any file context. + file = spec.substring(start, end); + ref = null; + } + else if (start < end) + { + // Context is available, but only override it if there is a new file. + int lastSlash = file.lastIndexOf('/'); + if (lastSlash < 0) + file = spec.substring(start, end); + else + file = (file.substring(0, lastSlash) + + '/' + spec.substring(start, end)); + + // For URLs constructed relative to a context, we + // need to canonicalise the file path. + file = canonicalizeFilename(file); + + ref = null; + } + + if (ref == null) + { + // Normally there should be no '#' in the file part, + // but we are nice. + int hash = file.indexOf('#'); + if (hash != -1) + { + ref = file.substring(hash + 1, file.length()); + file = file.substring(0, hash); + } + } + + // We care about the query tag only if there is no reference at all. + if (ref == null) + { + int queryTag = file.indexOf('?'); + if (queryTag != -1) + { + query = file.substring(queryTag + 1); + file = file.substring(0, queryTag); + } + } + + // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on + // the file part. It seems like overhead, but supposedly there is some + // benefit in windows based systems (it also lowercased the string). + setURL(url, url.getProtocol(), host, port, authority, userInfo, file, query, ref); + } + + /* + * Canonicalize a filename. + */ + private static String canonicalizeFilename(String file) + { + // XXX - GNU Classpath has an implementation that might be more appropriate + // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm) + int index; + + // Replace "/./" with "/". This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/./")) >= 0) + file = file.substring(0, index) + file.substring(index + 2); + + // Process "/../" correctly. This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/../")) >= 0) + { + // Strip of the previous directory - if it exists. + int previous = file.lastIndexOf('/', index - 1); + if (previous >= 0) + file = file.substring(0, previous) + file.substring(index + 3); + else + break; + } + return file; + } + + /** + * Compares two URLs, excluding the fragment component + * + * @param url1 The first url + * @param url2 The second url to compare with the first + * + * @return True if both URLs point to the same file, false otherwise. + * + * @specnote Now protected + */ + protected boolean sameFile(URL url1, URL url2) + { + if (url1 == url2) + return true; + + // This comparison is very conservative. It assumes that any + // field can be null. + if (url1 == null || url2 == null) + return false; + int p1 = url1.getPort(); + if (p1 == -1) + p1 = url1.ph.getDefaultPort(); + int p2 = url2.getPort(); + if (p2 == -1) + p2 = url2.ph.getDefaultPort(); + if (p1 != p2) + return false; + String s1; + String s2; + s1 = url1.getProtocol(); + s2 = url2.getProtocol(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = url1.getHost(); + s2 = url2.getHost(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = canonicalizeFilename(url1.getFile()); + s2 = canonicalizeFilename(url2.getFile()); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + return true; + } + + /** + * This methods sets the instance variables representing the various fields + * of the URL to the values passed in. + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to et + * @param port The port number to set + * @param file The filename to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + * + * @deprecated 1.2 Please use + * #setURL(URL,String,String,int,String,String,String,String); + */ + protected void setURL(URL u, String protocol, String host, int port, + String file, String ref) + { + u.set(protocol, host, port, file, ref); + } + + /** + * Sets the fields of the URL argument to the indicated values + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to set + * @param port The port number to set + * @param authority The authority to set + * @param userInfo The user information to set + * @param path The path/filename to set + * @param query The query part to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + */ + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) + { + u.set(protocol, host, port, authority, userInfo, path, query, ref); + } + + /** + * Provides the default equals calculation. May be overidden by handlers for + * other protocols that have different requirements for equals(). This method + * requires that none of its arguments is null. This is guaranteed by the + * fact that it is only called by java.net.URL class. + * + * @param url1 An URL object + * @param url2 An URL object + * + * @return True if both given URLs are equal, false otherwise. + */ + protected boolean equals(URL url1, URL url2) + { + // This comparison is very conservative. It assumes that any + // field can be null. + return (url1.getPort() == url2.getPort() + && ((url1.getProtocol() == null && url2.getProtocol() == null) + || (url1.getProtocol() != null + && url1.getProtocol().equals(url2.getProtocol()))) + && ((url1.getUserInfo() == null && url2.getUserInfo() == null) + || (url1.getUserInfo() != null + && url1.getUserInfo().equals(url2.getUserInfo()))) + && ((url1.getAuthority() == null && url2.getAuthority() == null) + || (url1.getAuthority() != null + && url1.getAuthority().equals(url2.getAuthority()))) + && ((url1.getHost() == null && url2.getHost() == null) + || (url1.getHost() != null && url1.getHost().equals(url2.getHost()))) + && ((url1.getPath() == null && url2.getPath() == null) + || (url1.getPath() != null && url1.getPath().equals(url2.getPath()))) + && ((url1.getQuery() == null && url2.getQuery() == null) + || (url1.getQuery() != null + && url1.getQuery().equals(url2.getQuery()))) + && ((url1.getRef() == null && url2.getRef() == null) + || (url1.getRef() != null && url1.getRef().equals(url2.getRef())))); + } + + /** + * Compares the host components of two URLs. + * + * @param url1 The first URL. + * @param url2 The second URL. + * + * @return True if both URLs contain the same host. + * + * @exception UnknownHostException If an unknown host is found + */ + protected boolean hostsEqual(URL url1, URL url2) + { + InetAddress addr1 = getHostAddress(url1); + InetAddress addr2 = getHostAddress(url2); + + if (addr1 != null && addr2 != null) + return addr1.equals(addr2); + + String host1 = url1.getHost(); + String host2 = url2.getHost(); + + if (host1 != null && host2 != null) + return host1.equalsIgnoreCase(host2); + + return host1 == null && host2 == null; + } + + /** + * Get the IP address of our host. An empty host field or a DNS failure will + * result in a null return. + * + * @param url The URL to return the host address for. + * + * @return The address of the hostname in url. + */ + protected InetAddress getHostAddress(URL url) + { + String hostname = url.getHost(); + + if (hostname.equals("")) + return null; + + try + { + return InetAddress.getByName(hostname); + } + catch (UnknownHostException e) + { + return null; + } + } + + /** + * Returns the default port for a URL parsed by this handler. This method is + * meant to be overidden by handlers with default port numbers. + * + * @return The default port number. + */ + protected int getDefaultPort() + { + return -1; + } + + /** + * Provides the default hash calculation. May be overidden by handlers for + * other protocols that have different requirements for hashCode calculation. + * + * @param url The URL to calc the hashcode for. + * + * @return The hashcode for the given URL. + */ + protected int hashCode(URL url) + { + return url.getProtocol().hashCode() + + ((url.getHost() == null) ? 0 : url.getHost().hashCode()) + + url.getFile().hashCode() + url.getPort(); + } + + /** + * This method converts a URL object into a String. This method creates + * Strings in the mold of http URL's, so protocol handlers which use URL's + * that have a different syntax should override this method + * + * @param url The URL object to convert + * + * @return A string representation of the url + */ + protected String toExternalForm(URL url) + { + String protocol; + String file; + String ref; + String authority; + + protocol = url.getProtocol(); + authority = url.getAuthority(); + if (authority == null) + authority = ""; + + file = url.getFile(); + ref = url.getRef(); + + // Guess a reasonable size for the string buffer so we have to resize + // at most once. + int size = protocol.length() + authority.length() + file.length() + 24; + StringBuffer sb = new StringBuffer(size); + + if (protocol != null && protocol.length() > 0) + { + sb.append(protocol); + sb.append(":"); + } + + if (authority.length() != 0) + { + sb.append("//").append(authority); + } + + sb.append(file); + + if (ref != null) + sb.append('#').append(ref); + + return sb.toString(); + } +} diff --git a/libjava/classpath/java/net/URLStreamHandlerFactory.java b/libjava/classpath/java/net/URLStreamHandlerFactory.java new file mode 100644 index 00000000000..c92c71fb2b3 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandlerFactory.java @@ -0,0 +1,65 @@ +/* URLStreamHandlerFactory.java -- Maps protocols to URLStreamHandlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface contains one method which maps the protocol portion of + * a URL (eg, "http" in "http://www.urbanophile.com/arenn/") to a + * <code>URLStreamHandler</code> object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface URLStreamHandlerFactory +{ + /** + * This method maps the protocol portion of a URL to a + * <code>URLStreamHandler</code> object. + * + * @param protocol The protocol name to map ("http", "ftp", etc). + * + * @return The <code>URLStreamHandler</code> for the specified protocol + */ + URLStreamHandler createURLStreamHandler(String protocol); +} // interface URLStreamHandlerFactory diff --git a/libjava/classpath/java/net/UnknownHostException.java b/libjava/classpath/java/net/UnknownHostException.java new file mode 100644 index 00000000000..c5ba18330d3 --- /dev/null +++ b/libjava/classpath/java/net/UnknownHostException.java @@ -0,0 +1,77 @@ +/* UnknownHostException.java -- The hostname is unknown + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that an attempt was made to reference a hostname + * or IP address that is not valid. This could possibly indicate that a + * DNS problem has occurred, but most often means that the host was not + * correctly specified. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class UnknownHostException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4639126076052875403L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownHostException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * name of the host that could not be resolved. + * + * @param message a message describing the error that occurred + */ + public UnknownHostException(String message) + { + super(message); + } +} // class UnknownHostException diff --git a/libjava/classpath/java/net/UnknownServiceException.java b/libjava/classpath/java/net/UnknownServiceException.java new file mode 100644 index 00000000000..65cc8f59225 --- /dev/null +++ b/libjava/classpath/java/net/UnknownServiceException.java @@ -0,0 +1,76 @@ +/* UnknownServiceException.java -- A service error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * Contrary to what you might think, this does not indicate that the + * TCP/IP service name specified was invalid. Instead it indicates that + * the MIME type returned from a URL could not be determined or that an + * attempt was made to write to a read-only URL. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class UnknownServiceException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4169033248853639508L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownServiceException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public UnknownServiceException(String message) + { + super(message); + } +} // class UnknownServiceException diff --git a/libjava/classpath/java/net/class-dependencies.conf b/libjava/classpath/java/net/class-dependencies.conf new file mode 100644 index 00000000000..8b130f53684 --- /dev/null +++ b/libjava/classpath/java/net/class-dependencies.conf @@ -0,0 +1,122 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +java/net/InetAddress: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/net/UnknownHostException.<init>(Ljava/lang/String;)V + +java/net/DatagramSocketImpl: \ + java/net/DatagramSocketImpl.fd(Ljava/io/FileDescriptor;) \ + java/net/DatagramSocketImpl.localPort(I) + +java/net/PlainDatagramSocketImpl: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/io/FileDescriptor.<init>()V \ + java/lang/Boolean.<init>(Z)V \ + java/lang/Integer.<init>(I)V \ + java/net/InetAddress.getByName(Ljava/lang/String;)Ljava/net/InetAddress; \ + java/net/InetAddress.getAddress()[B \ + java/lang/Boolean.booleanValue()Z \ + java/lang/Integer.intValue()I \ + java/net/SocketException.<init>(Ljava/lang/String;)V \ + java/net/DatagramPacket.getData()[B \ + java/net/SocketImpl.address(Ljava/net/InetAddress;) \ + java/net/PlainSocketImpl.native_fd(I) \ + java/net/SocketImpl.fd(Ljava/io/FileDescriptor;) \ + java/net/SocketImpl.address(Ljava/net/InetAddress;) \ + java/net/PlainDatagramSocketImpl.native_fd(I) \ + java/net/SocketImpl.localport(I) \ + java/net/SocketImpl.port(I) + +java/net/PlainSocketImpl: \ + java/lang/ClassNotFoundException.<init>(Ljava/lang/String;)V \ + java/lang/InternalError.<init>(Ljava/lang/String;)V \ + java/io/IOException.<init>(Ljava/lang/String;)V \ + java/io/FileDescriptor.<init>()V \ + java/lang/Boolean.<init>(Z)V \ + java/lang/Integer.<init>(I)V \ + java/net/InetAddress.getByName(Ljava/lang/String;)Ljava/net/InetAddress; \ + java/net/InetAddress.getAddress()[B \ + java/lang/Boolean.booleanValue()Z \ + java/lang/Integer.intValue()I \ + java/net/SocketException.<init>(Ljava/lang/String;)V \ + java/net/DatagramPacket.getData()[B \ + java/net/SocketImpl.address(Ljava/net/InetAddress;) \ + java/net/PlainSocketImpl.native_fd(I) \ + java/net/SocketImpl.fd(Ljava/io/FileDescriptor;) \ + java/net/SocketImpl.address(Ljava/net/InetAddress;) \ + java/net/PlainDatagramSocketImpl.native_fd(I) \ + java/net/SocketImpl.localport(I) \ + java/net/SocketImpl.port(I) + +# All protocols supported are loaded via URL.getURLStreamHandler from +# class gnu.java.net.protocol.<protocol>.Handler. +# +# This introduces a dependency for all protocols. To allow an easy selection +# and addition of protocols, the library variable {protocols} can be set to +# the set of supported protocols. +# +{protocols}: http file jar + +java/net/URL.getURLStreamHandler(Ljava/lang/String;)Ljava/net/URLStreamHandler;: \ + gnu/java/net/protocol/{protocols}/Handler.* \ + com/aicas/java/net/protocol/rom/Handler.* + +# end of file diff --git a/libjava/classpath/java/net/package.html b/libjava/classpath/java/net/package.html new file mode 100644 index 00000000000..133ee716f02 --- /dev/null +++ b/libjava/classpath/java/net/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.net package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.net</title></head> + +<body> +<p>Network communication through TCP and UDP sockets or URLs.</p> + +</body> +</html> diff --git a/libjava/classpath/java/nio/Buffer.java b/libjava/classpath/java/nio/Buffer.java new file mode 100644 index 00000000000..47dabf29e42 --- /dev/null +++ b/libjava/classpath/java/nio/Buffer.java @@ -0,0 +1,361 @@ +/* Buffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.RawData; + +/** + * @since 1.4 + */ +public abstract class Buffer +{ + int cap = 0; + int limit = 0; + int pos = 0; + int mark = -1; + RawData address; + + /** + * Creates a new Buffer. + * + * Should be package private. + */ + Buffer (int capacity, int limit, int position, int mark) + { + if (capacity < 0) + throw new IllegalArgumentException (); + + cap = capacity; + limit (limit); + position (position); + + if (mark >= 0) + { + if (mark > pos) + throw new IllegalArgumentException (); + + this.mark = mark; + } + } + + /** + * Retrieves the capacity of the buffer. + * + * @return the capacity of the buffer + */ + public final int capacity () + { + return cap; + } + + /** + * Clears the buffer. + * + * @return this buffer + */ + public final Buffer clear () + { + limit = cap; + pos = 0; + mark = -1; + return this; + } + + /** + * Flips the buffer. + * + * @return this buffer + */ + public final Buffer flip () + { + limit = pos; + pos = 0; + mark = -1; + return this; + } + + /** + * Tells whether the buffer has remaining data to read or not. + * + * @return true if the buffer contains remaining data to read, + * false otherwise + */ + public final boolean hasRemaining () + { + return remaining() > 0; + } + + /** + * Tells whether this buffer is read only or not. + * + * @return true if the buffer is read only, false otherwise + */ + public abstract boolean isReadOnly (); + + /** + * Retrieves the current limit of the buffer. + * + * @return the limit of the buffer + */ + public final int limit () + { + return limit; + } + + /** + * Sets this buffer's limit. + * + * @param newLimit The new limit value; must be non-negative and no larger + * than this buffer's capacity. + * + * @return this buffer + * + * @exception IllegalArgumentException If the preconditions on newLimit + * do not hold. + */ + public final Buffer limit (int newLimit) + { + if ((newLimit < 0) || (newLimit > cap)) + throw new IllegalArgumentException (); + + if (newLimit < mark) + mark = -1; + + if (pos > newLimit) + pos = newLimit; + + limit = newLimit; + return this; + } + + /** + * Sets this buffer's mark at its position. + * + * @return this buffer + */ + public final Buffer mark () + { + mark = pos; + return this; + } + + /** + * Retrieves the current position of this buffer. + * + * @return the current position of this buffer + */ + public final int position () + { + return pos; + } + + /** + * Sets this buffer's position. If the mark is defined and larger than the + * new position then it is discarded. + * + * @param newPosition The new position value; must be non-negative and no + * larger than the current limit. + * + * @return this buffer + * + * @exception IllegalArgumentException If the preconditions on newPosition + * do not hold + */ + public final Buffer position (int newPosition) + { + if ((newPosition < 0) || (newPosition > limit)) + throw new IllegalArgumentException (); + + if (newPosition <= mark) + mark = -1; + + pos = newPosition; + return this; + } + + /** + * Returns the number of elements between the current position and the limit. + * + * @return the number of remaining elements + */ + public final int remaining() + { + return limit - pos; + } + + /** + * Resets this buffer's position to the previously-marked position. + * + * @return this buffer + * + * @exception InvalidMarkException If the mark has not been set. + */ + public final Buffer reset() + { + if (mark == -1) + throw new InvalidMarkException (); + + pos = mark; + return this; + } + + /** + * Rewinds this buffer. The position is set to zero and the mark + * is discarded. + * + * @return this buffer + */ + public final Buffer rewind() + { + pos = 0; + mark = -1; + return this; + } + + /** + * Checks for underflow. This method is used internally to check + * whether a buffer has enough elements left to satisfy a read + * request. + * + * @exception BufferUnderflowException If there are no remaining + * elements in this buffer. + */ + final void checkForUnderflow() + { + if (!hasRemaining()) + throw new BufferUnderflowException(); + } + + /** + * Checks for underflow. This method is used internally to check + * whether a buffer has enough elements left to satisfy a read + * request for a given number of elements. + * + * @param length The length of a sequence of elements. + * + * @exception BufferUnderflowException If there are not enough + * remaining elements in this buffer. + */ + final void checkForUnderflow(int length) + { + if (remaining() < length) + throw new BufferUnderflowException(); + } + + /** + * Checks for overflow. This method is used internally to check + * whether a buffer has enough space left to satisfy a write + * request. + * + * @exception BufferOverflowException If there is no remaining + * space in this buffer. + */ + final void checkForOverflow() + { + if (!hasRemaining()) + throw new BufferOverflowException(); + } + + /** + * Checks for overflow. This method is used internally to check + * whether a buffer has enough space left to satisfy a write + * request for a given number of elements. + * + * @param length The length of a sequence of elements. + * + * @exception BufferUnderflowException If there is not enough + * remaining space in this buffer. + */ + final void checkForOverflow(int length) + { + if (remaining() < length) + throw new BufferOverflowException(); + } + + /** + * Checks if index is negative or not smaller than the buffer's + * limit. This method is used internally to check whether + * an indexed request can be fulfilled. + * + * @param index The requested position in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + final void checkIndex(int index) + { + if (index < 0 + || index >= limit ()) + throw new IndexOutOfBoundsException (); + } + + /** + * Checks if buffer is read-only. This method is used internally to + * check if elements can be put into a buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + final void checkIfReadOnly() + { + if (isReadOnly()) + throw new ReadOnlyBufferException (); + } + + /** + * Checks whether an array is large enough to hold the given number of + * elements at the given offset. This method is used internally to + * check if an array is big enough. + * + * @param arraylength The length of the array. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than arraylength. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than arraylength - offset. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + static final void checkArraySize(int arraylength, int offset, int length) + { + if ((offset < 0) || + (length < 0) || + (arraylength < length + offset)) + throw new IndexOutOfBoundsException (); + } +} diff --git a/libjava/classpath/java/nio/BufferOverflowException.java b/libjava/classpath/java/nio/BufferOverflowException.java new file mode 100644 index 00000000000..588c03290a1 --- /dev/null +++ b/libjava/classpath/java/nio/BufferOverflowException.java @@ -0,0 +1,51 @@ +/* BufferOverflowException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @since 1.4 + */ +public class BufferOverflowException extends RuntimeException +{ + /** + * Creates the exception + */ + public BufferOverflowException () + { + } +} diff --git a/libjava/classpath/java/nio/BufferUnderflowException.java b/libjava/classpath/java/nio/BufferUnderflowException.java new file mode 100644 index 00000000000..4b4161c647c --- /dev/null +++ b/libjava/classpath/java/nio/BufferUnderflowException.java @@ -0,0 +1,51 @@ +/* BufferUnderflowException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @since 1.4 + */ +public class BufferUnderflowException extends RuntimeException +{ + /** + * Creates the exception + */ + public BufferUnderflowException () + { + } +} diff --git a/libjava/classpath/java/nio/ByteBuffer.java b/libjava/classpath/java/nio/ByteBuffer.java new file mode 100644 index 00000000000..0ccf7663cfa --- /dev/null +++ b/libjava/classpath/java/nio/ByteBuffer.java @@ -0,0 +1,651 @@ +/* ByteBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class ByteBuffer extends Buffer + implements Comparable +{ + ByteOrder endian = ByteOrder.BIG_ENDIAN; + + int array_offset; + byte[] backing_buffer; + + ByteBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + } + + /** + * Allocates a new direct byte buffer. + */ + public static ByteBuffer allocateDirect (int capacity) + { + return DirectByteBufferImpl.allocate (capacity); + } + + /** + * Allocates a new <code>ByteBuffer</code> object with a given capacity. + */ + public static ByteBuffer allocate (int capacity) + { + return wrap(new byte[capacity], 0, capacity); + } + + /** + * Wraps a <code>byte</code> array into a <code>ByteBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final ByteBuffer wrap (byte[] array, int offset, int length) + { + // FIXME: In GCJ and other implementations where arrays may not + // move we might consider, at least when offset==0: + // return new DirectByteBufferImpl(array, + // address_of_data(array) + offset, + // length, length, 0, false); + // This may be more efficient, mainly because we can then use the + // same logic for all ByteBuffers. + + return new ByteBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>byte</code> array into a <code>ByteBuffer</code> + * object. + */ + public static final ByteBuffer wrap (byte[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>byte</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>byte</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>byte</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>byte</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public ByteBuffer get (byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>byte</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>byte</code>s remaining in this buffer. + */ + public ByteBuffer get (byte[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>ByteBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>byte</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (ByteBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + byte[] toPut = new byte [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>byte array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>byte</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (byte[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>byte array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>byte</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final ByteBuffer put (byte[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>byte</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>byte</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final byte[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>int</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof ByteBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>ByteBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>ByteBuffer</code>. + */ + public int compareTo (Object obj) + { + ByteBuffer other = (ByteBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + byte a = get(pos_this++); + byte b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public final ByteOrder order () + { + return endian; + } + + /** + * Modifies this buffer's byte order. + */ + public final ByteBuffer order (ByteOrder endian) + { + this.endian = endian; + return this; + } + + /** + * Reads the <code>byte</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>byte</code>s in this buffer. + */ + public abstract byte get (); + + /** + * Writes the <code>byte</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>byte</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer put (byte b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract byte get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer put (int index, byte b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer compact (); + + void shiftDown (int dst_offset, int src_offset, int count) + { + for (int i = 0; i < count; i++) + put(dst_offset + i, get(src_offset + i)); + } + + /** + * Tells whether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>ByteBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract ByteBuffer slice (); + + /** + * Creates a new <code>ByteBuffer</code> that shares this buffer's + * content. + */ + public abstract ByteBuffer duplicate (); + + /** + * Creates a new read-only <code>ByteBuffer</code> that shares this + * buffer's content. + */ + public abstract ByteBuffer asReadOnlyBuffer (); + + /** + * Creates a view of this byte buffer as a short buffer. + */ + public abstract ShortBuffer asShortBuffer (); + + /** + * Creates a view of this byte buffer as a char buffer. + */ + public abstract CharBuffer asCharBuffer (); + + /** + * Creates a view of this byte buffer as an integer buffer. + */ + public abstract IntBuffer asIntBuffer (); + + /** + * Creates a view of this byte buffer as a long buffer. + */ + public abstract LongBuffer asLongBuffer (); + + /** + * Creates a view of this byte buffer as a float buffer. + */ + public abstract FloatBuffer asFloatBuffer (); + + /** + * Creates a view of this byte buffer as a double buffer. + */ + public abstract DoubleBuffer asDoubleBuffer (); + + /** + * Relative get method for reading a character value. + * + * @exception BufferUnderflowException If there are fewer than two bytes + * remaining in this buffer. + */ + public abstract char getChar (); + + /** + * Relative put method for writing a character value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putChar (char value); + + /** + * Absolute get method for reading a character value. + * + * @exception IndexOutOfBoundsException If there are fewer than two bytes + * remaining in this buffer + */ + public abstract char getChar (int index); + + /** + * Absolute put method for writing a character value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract ByteBuffer putChar (int index, char value); + + /** + * Relative get method for reading a short value. + * + * @exception BufferUnderflowException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract short getShort (); + + /** + * Relative put method for writing a short value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putShort (short value); + + /** + * Absolute get method for reading a short value. + * + * @exception IndexOutOfBoundsException If there are fewer than two bytes + * remaining in this buffer + */ + public abstract short getShort (int index); + + /** + * Absolute put method for writing a short value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract ByteBuffer putShort (int index, short value); + + /** + * Relative get method for reading an integer value. + * + * @exception BufferUnderflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract int getInt (); + + /** + * Relative put method for writing an integer value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putInt (int value); + + /** + * Absolute get method for reading an integer value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract int getInt (int index); + + /** + * Absolute put method for writing an integer value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract ByteBuffer putInt (int index, int value); + + /** + * Relative get method for reading a long value. + * + * @exception BufferUnderflowException If there are fewer than eight bytes + * remaining in this buffer. + */ + public abstract long getLong (); + + /** + * Relative put method for writing a long value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putLong (long value); + + /** + * Absolute get method for reading a long value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract long getLong (int index); + + /** + * Absolute put method for writing a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract ByteBuffer putLong (int index, long value); + + /** + * Relative get method for reading a float value. + * + * @exception BufferUnderflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract float getFloat (); + + /** + * Relative put method for writing a float value. + * + * @exception BufferOverflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract ByteBuffer putFloat (float value); + + /** + * Absolute get method for reading a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract float getFloat (int index); + + /** + * Relative put method for writing a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract ByteBuffer putFloat (int index, float value); + + /** + * Relative get method for reading a double value. + * + * @exception BufferUnderflowException If there are fewer than eight bytes + * remaining in this buffer. + */ + public abstract double getDouble (); + + /** + * Relative put method for writing a double value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putDouble (double value); + + /** + * Absolute get method for reading a double value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract double getDouble (int index); + + /** + * Absolute put method for writing a double value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract ByteBuffer putDouble (int index, double value); + + /** + * Returns a string summarizing the state of this buffer. + */ + public String toString () + { + return getClass ().getName () + + "[pos=" + position () + + " lim=" + limit () + + " cap=" + capacity () + "]"; + } +} diff --git a/libjava/classpath/java/nio/ByteBufferHelper.java b/libjava/classpath/java/nio/ByteBufferHelper.java new file mode 100644 index 00000000000..6c46ca5d7ab --- /dev/null +++ b/libjava/classpath/java/nio/ByteBufferHelper.java @@ -0,0 +1,344 @@ +/* ByteBufferImpl.java -- + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +final class ByteBufferHelper +{ + public static char getChar (ByteBuffer buffer, ByteOrder order) + { + return (char) getShort (buffer, order); + } + + public static void putChar (ByteBuffer buffer, char value, ByteOrder order) + { + putShort (buffer, (short) value, order); + } + + public static char getChar (ByteBuffer buffer, int index, ByteOrder order) + { + return (char) getShort (buffer, index, order); + } + + public static void putChar (ByteBuffer buffer, int index, + char value, ByteOrder order) + { + putShort (buffer, index, (short) value, order); + } + + public static short getShort (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(2); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return (short) ((buffer.get() & 0xff) + + (buffer.get() << 8)); + } + + return (short) ((buffer.get() << 8) + + (buffer.get() & 0xff)); + } + + public static void putShort (ByteBuffer buffer, short value, ByteOrder order) + { + buffer.checkForOverflow(2); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + } + else + { + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static short getShort (ByteBuffer buffer, + int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return (short) ((buffer.get (index) & 0xff) + + (buffer.get (++index) << 8)); + } + + return (short) ((buffer.get (index) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putShort (ByteBuffer buffer, int index, + short value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + } + else + { + buffer.put (index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static int getInt (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(4); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get() & 0xff) + + ((buffer.get() & 0xff) << 8) + + ((buffer.get() & 0xff) << 16) + + (buffer.get() << 24)); + } + + return (int) ((buffer.get() << 24) + + ((buffer.get() & 0xff) << 16) + + ((buffer.get() & 0xff) << 8) + + (buffer.get() & 0xff)); + } + + public static void putInt (ByteBuffer buffer, int value, ByteOrder order) + { + buffer.checkForOverflow(4); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 24)); + } + else + { + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static int getInt (ByteBuffer buffer, int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get (index) & 0xff) + + ((buffer.get (++index) & 0xff) << 8) + + ((buffer.get (++index) & 0xff) << 16) + + (buffer.get (++index) << 24)); + } + + return ((buffer.get (index) << 24) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xff) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putInt (ByteBuffer buffer, int index, + int value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 24)); + } + else + { + buffer.put (index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static long getLong (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(8); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get() & 0xff) + + (((buffer.get() & 0xff)) << 8) + + (((buffer.get() & 0xff)) << 16) + + (((buffer.get() & 0xffL)) << 24) + + (((buffer.get() & 0xffL)) << 32) + + (((buffer.get() & 0xffL)) << 40) + + (((buffer.get() & 0xffL)) << 48) + + (((long) buffer.get()) << 56)); + } + + return ((((long) buffer.get()) << 56) + + ((buffer.get() & 0xffL) << 48) + + ((buffer.get() & 0xffL) << 40) + + ((buffer.get() & 0xffL) << 32) + + ((buffer.get() & 0xffL) << 24) + + ((buffer.get() & 0xff) << 16) + + ((buffer.get() & 0xff) << 8) + + (buffer.get() & 0xff)); + } + + public static void putLong (ByteBuffer buffer, long value, ByteOrder order) + { + buffer.checkForOverflow(8); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 32)); + buffer.put ((byte) (value >> 40)); + buffer.put ((byte) (value >> 48)); + buffer.put ((byte) (value >> 56)); + } + else + { + buffer.put ((byte) (value >> 56)); + buffer.put ((byte) (value >> 48)); + buffer.put ((byte) (value >> 40)); + buffer.put ((byte) (value >> 32)); + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static long getLong (ByteBuffer buffer, int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get (index) & 0xff) + + ((buffer.get (++index) & 0xff) << 8) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xffL) << 24) + + ((buffer.get (++index) & 0xffL) << 32) + + ((buffer.get (++index) & 0xffL) << 40) + + ((buffer.get (++index) & 0xffL) << 48) + + (((long) buffer.get (++index)) << 56)); + } + + return ((((long) buffer.get (index)) << 56) + + ((buffer.get (++index) & 0xffL) << 48) + + ((buffer.get (++index) & 0xffL) << 40) + + ((buffer.get (++index) & 0xffL) << 32) + + ((buffer.get (++index) & 0xffL) << 24) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xff) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putLong (ByteBuffer buffer, int index, + long value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 32)); + buffer.put (++index, (byte) (value >> 40)); + buffer.put (++index, (byte) (value >> 48)); + buffer.put (++index, (byte) (value >> 56)); + } + else + { + buffer.put (index, (byte) (value >> 56)); + buffer.put (++index, (byte) (value >> 48)); + buffer.put (++index, (byte) (value >> 40)); + buffer.put (++index, (byte) (value >> 32)); + buffer.put (++index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static float getFloat (ByteBuffer buffer, ByteOrder order) + { + return Float.intBitsToFloat (getInt (buffer, order)); + } + + public static void putFloat (ByteBuffer buffer, float value, ByteOrder order) + { + putInt (buffer, Float.floatToRawIntBits (value), order); + } + + public static float getFloat (ByteBuffer buffer, int index, ByteOrder order) + { + return Float.intBitsToFloat (getInt (buffer, index, order)); + } + + public static void putFloat (ByteBuffer buffer, int index, + float value, ByteOrder order) + { + putInt (buffer, index, Float.floatToRawIntBits (value), order); + } + + public static double getDouble (ByteBuffer buffer, ByteOrder order) + { + return Double.longBitsToDouble (getLong (buffer, order)); + } + + public static void putDouble (ByteBuffer buffer, double value, ByteOrder order) + { + putLong (buffer, Double.doubleToRawLongBits (value), order); + } + + public static double getDouble (ByteBuffer buffer, int index, ByteOrder order) + { + return Double.longBitsToDouble (getLong (buffer, index, order)); + } + + public static void putDouble (ByteBuffer buffer, int index, + double value, ByteOrder order) + { + putLong (buffer, index, Double.doubleToRawLongBits (value), order); + } +} // ByteBufferHelper + diff --git a/libjava/classpath/java/nio/ByteBufferImpl.java b/libjava/classpath/java/nio/ByteBufferImpl.java new file mode 100644 index 00000000000..48d7152000b --- /dev/null +++ b/libjava/classpath/java/nio/ByteBufferImpl.java @@ -0,0 +1,379 @@ +/* ByteBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class ByteBufferImpl extends ByteBuffer +{ + private boolean readOnly; + + ByteBufferImpl (byte[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public CharBuffer asCharBuffer () + { + return new CharViewBufferImpl (this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer () + { + return new ShortViewBufferImpl (this, remaining() >> 1); + } + + public IntBuffer asIntBuffer () + { + return new IntViewBufferImpl (this, remaining() >> 2); + } + + public LongBuffer asLongBuffer () + { + return new LongViewBufferImpl (this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer () + { + return new FloatViewBufferImpl (this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer () + { + return new DoubleViewBufferImpl (this, remaining() >> 3); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public ByteBuffer slice () + { + return new ByteBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public ByteBuffer duplicate () + { + return new ByteBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public ByteBuffer asReadOnlyBuffer () + { + return new ByteBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + void shiftDown (int dst_offset, int src_offset, int count) + { + System.arraycopy(backing_buffer, array_offset + src_offset, + backing_buffer, array_offset + dst_offset, + count); + } + + public ByteBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + if (pos > 0) + { + int count = remaining(); + shiftDown(0, pos, count); + position(count); + limit(capacity()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>byte</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>bytes</code> in this buffer. + */ + public byte get () + { + if (pos >= limit) + throw new BufferUnderflowException(); + + return backing_buffer [(pos++) + array_offset]; + } + + /** + * Bulk get + */ + public ByteBuffer get (byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + if ( (limit - pos) < length) // check for overflow + throw new BufferUnderflowException(); + + System.arraycopy(backing_buffer, pos + array_offset, + dst, offset, length); + pos += length; + + return this; + } + + /** + * Relative bulk put(), overloads the ByteBuffer impl. + */ + public ByteBuffer put (byte[] src, int offset, int length) + { + if ( (limit - pos) < length) // check for overflow + throw new BufferOverflowException(); + checkArraySize(src.length, offset, length); + + System.arraycopy(src, offset, backing_buffer, pos + array_offset, length); + pos += length; + + return this; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there is no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (byte value) + { + if (readOnly) + throw new ReadOnlyBufferException(); + if (pos >= limit) + throw new BufferOverflowException(); + + backing_buffer [(pos++) + array_offset] = value; + return this; + } + + /** + * Absolute get method. Reads the <code>byte</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public byte get (int index) + { + checkIndex(index); + + return backing_buffer [index + array_offset]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (int index, byte value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index + array_offset] = value; + return this; + } + + public char getChar () + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar (char value) + { + if (readOnly) + throw new ReadOnlyBufferException (); + if ( (limit-pos) < 2) + throw new BufferOverflowException(); + + if (endian == ByteOrder.LITTLE_ENDIAN) + { + backing_buffer [(pos++) + array_offset] = (byte)(value&0xFF); + backing_buffer [(pos++) + array_offset] = (byte)(value>>8); + } + else + { + backing_buffer [(pos++) + array_offset] = (byte)(value>>8); + backing_buffer [(pos++) + array_offset] = (byte)(value&0xFF); + } + return this; + } + + public char getChar (int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar (int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort () + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort (short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort (int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort (int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt () + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt (int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt (int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt (int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong () + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong (long value) + { + ByteBufferHelper.putLong (this, value, order()); + return this; + } + + public long getLong (int index) + { + return ByteBufferHelper.getLong (this, index, order()); + } + + public ByteBuffer putLong (int index, long value) + { + ByteBufferHelper.putLong (this, index, value, order()); + return this; + } + + public float getFloat () + { + return ByteBufferHelper.getFloat (this, order()); + } + + public ByteBuffer putFloat (float value) + { + ByteBufferHelper.putFloat (this, value, order()); + return this; + } + + public float getFloat (int index) + { + return ByteBufferHelper.getFloat (this, index, order()); + } + + public ByteBuffer putFloat (int index, float value) + { + ByteBufferHelper.putFloat (this, index, value, order()); + return this; + } + + public double getDouble () + { + return ByteBufferHelper.getDouble (this, order()); + } + + public ByteBuffer putDouble (double value) + { + ByteBufferHelper.putDouble (this, value, order()); + return this; + } + + public double getDouble (int index) + { + return ByteBufferHelper.getDouble (this, index, order()); + } + + public ByteBuffer putDouble (int index, double value) + { + ByteBufferHelper.putDouble (this, index, value, order()); + return this; + } +} diff --git a/libjava/classpath/java/nio/ByteOrder.java b/libjava/classpath/java/nio/ByteOrder.java new file mode 100644 index 00000000000..39a3ff893bd --- /dev/null +++ b/libjava/classpath/java/nio/ByteOrder.java @@ -0,0 +1,82 @@ +/* ByteOrder.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public final class ByteOrder +{ + /** + * Constant indicating big endian byte order. + */ + public static final ByteOrder BIG_ENDIAN = new ByteOrder(); + + /** + * Constant indicating little endian byte order. + */ + public static final ByteOrder LITTLE_ENDIAN = new ByteOrder(); + + /** + * Returns the native byte order of the platform currently running. + * + * @return the native byte order + */ + public static ByteOrder nativeOrder() + { + return (System.getProperty ("gnu.cpu.endian").equals("big") + ? BIG_ENDIAN : LITTLE_ENDIAN); + } + + /** + * Returns a string representation of the byte order. + * + * @return the string + */ + public String toString() + { + return this == BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN"; + } + + // This class can only be instantiated here. + private ByteOrder() + { + } +} diff --git a/libjava/classpath/java/nio/CharBuffer.java b/libjava/classpath/java/nio/CharBuffer.java new file mode 100644 index 00000000000..6551555e20b --- /dev/null +++ b/libjava/classpath/java/nio/CharBuffer.java @@ -0,0 +1,508 @@ +/* CharBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class CharBuffer extends Buffer + implements Comparable, CharSequence +{ + int array_offset; + char[] backing_buffer; + + CharBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>CharBuffer</code> object with a given capacity. + */ + public static CharBuffer allocate (int capacity) + { + return new CharBufferImpl (capacity); + } + + /** + * Wraps a <code>char</code> array into a <code>CharBuffer</code> + * object. + * + * @param array the array to wrap + * @param offset the offset of the region in the array to wrap + * @param length the length of the region in the array to wrap + * + * @return a new <code>CharBuffer</code> object + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final CharBuffer wrap(char[] array, int offset, int length) + { + return new CharBufferImpl(array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a character sequence into a <code>CharBuffer</code> object. + * + * @param seq the sequence to wrap + * + * @return a new <code>CharBuffer</code> object + */ + public static final CharBuffer wrap(CharSequence seq) + { + return wrap(seq, 0, seq.length()); + } + + /** + * Wraps a character sequence into a <code>CharBuffer</code> object. + * + * @param seq the sequence to wrap + * @param start the index of the first character to wrap + * @param end the index of the first character not to wrap + * + * @return a new <code>CharBuffer</code> object + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final CharBuffer wrap(CharSequence seq, int start, int end) + { + // FIXME: implement better handling of java.lang.String. + // Probably share data with String via reflection. + + if ((start < 0) + || (start > seq.length()) + || (end < start) + || (end > (seq.length() - start))) + throw new IndexOutOfBoundsException(); + + int len = end - start; + char[] buffer = new char[len]; + + for (int i = 0; i < len; i++) + buffer[i] = seq.charAt(i + start); + + return wrap(buffer, 0, len).asReadOnlyBuffer(); + } + + /** + * Wraps a <code>char</code> array into a <code>CharBuffer</code> + * object. + * + * @param array the array to wrap + * + * @return a new <code>CharBuffer</code> object + */ + public static final CharBuffer wrap(char[] array) + { + return wrap(array, 0, array.length); + } + + /** + * This method transfers <code>char</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>char</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>char</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>char</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public CharBuffer get (char[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>char</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>char</code>s remaining in this buffer. + */ + public CharBuffer get (char[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>CharBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>char</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (CharBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + char[] toPut = new char [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>char array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>char</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (char[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>char array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>char</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final CharBuffer put (char[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>char</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>char</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final char[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof CharBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>CharBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>CharBuffer</code>. + */ + public int compareTo (Object obj) + { + CharBuffer other = (CharBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + char a = get(pos_this++); + char b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>char</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>char</code>s in this buffer. + */ + public abstract char get (); + + /** + * Writes the <code>char</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>char</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer put (char b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract char get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer put (int index, char b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>CharBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract CharBuffer slice (); + + /** + * Creates a new <code>CharBuffer</code> that shares this buffer's + * content. + */ + public abstract CharBuffer duplicate (); + + /** + * Creates a new read-only <code>CharBuffer</code> that shares this + * buffer's content. + */ + public abstract CharBuffer asReadOnlyBuffer (); + + /** + * Returns the remaining content of the buffer as a string. + */ + public String toString () + { + if (hasArray ()) + return new String (array (), position (), length ()); + + char[] buf = new char [length ()]; + int pos = position (); + get (buf, 0, buf.length); + position (pos); + return new String (buf); + } + + /** + * Returns the length of the remaining chars in this buffer. + */ + public final int length () + { + return remaining (); + } + + /** + * Creates a new character buffer that represents the specified subsequence + * of this buffer, relative to the current position. + * + * @exception IndexOutOfBoundsException If the preconditions on start and + * end do not hold. + */ + public abstract CharSequence subSequence (int start, int length); + + /** + * Relative put method. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception IndexOutOfBoundsException If the preconditions on the start + * and end parameters do not hold. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (String str, int start, int length) + { + return put (str.toCharArray (), start, length); + } + + /** + * Relative put method. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final CharBuffer put (String str) + { + return put (str.toCharArray (), 0, str.length ()); + } + + /** + * Returns the character at <code>position() + index</code>. + * + * @exception IndexOutOfBoundsException If index is negative not smaller than + * <code>remaining()</code>. + */ + public final char charAt (int index) + { + if (index < 0 + || index >= remaining ()) + throw new IndexOutOfBoundsException (); + + return get (position () + index); + } +} diff --git a/libjava/classpath/java/nio/CharBufferImpl.java b/libjava/classpath/java/nio/CharBufferImpl.java new file mode 100644 index 00000000000..33f8dab983f --- /dev/null +++ b/libjava/classpath/java/nio/CharBufferImpl.java @@ -0,0 +1,219 @@ +/* CharBufferImpl.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class CharBufferImpl extends CharBuffer +{ + private boolean readOnly; + + CharBufferImpl (int capacity) + { + this (new char [capacity], 0, capacity, capacity, 0, -1, false); + } + + CharBufferImpl (char[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public CharBufferImpl (CharBufferImpl copy) + { + super (copy.capacity (), copy.limit (), copy.position (), 0); + backing_buffer = copy.backing_buffer; + array_offset = copy.array_offset; + readOnly = copy.isReadOnly (); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public CharBuffer slice () + { + return new CharBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public CharBuffer duplicate () + { + return new CharBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public CharBuffer asReadOnlyBuffer () + { + return new CharBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public CharBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + public CharSequence subSequence (int start, int end) + { + if (start < 0 + || start > length () + || end < start + || end > length ()) + throw new IndexOutOfBoundsException (); + + return new CharBufferImpl (backing_buffer, array_offset, capacity (), position () + end, position () + start, -1, isReadOnly ()); + } + + /** + * Reads the <code>char</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>char</code>s in this buffer. + */ + public char get () + { + if (pos >= limit) + throw new BufferUnderflowException(); + + return backing_buffer [(pos++) + array_offset]; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (char value) + { + if (readOnly) + throw new ReadOnlyBufferException(); + if (pos >= limit) + throw new BufferOverflowException(); + + backing_buffer [(pos++) + array_offset] = value; + return this; + } + + /** + * Absolute get method. Reads the <code>char</code> at position + * <code>index</code>. + * + * @param index Position to read the <code>char</code> from. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public char get (int index) + { + checkIndex(index); + + return backing_buffer [index + array_offset]; + } + + /** + * Bulk get, overloaded for speed. + */ + public CharBuffer get (char[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + System.arraycopy(backing_buffer, pos + array_offset, + dst, offset, length); + pos += length; + return this; + } + + /** + * Bulk put, overloaded for speed. + */ + public CharBuffer put (char[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + System.arraycopy(src, offset, + backing_buffer, pos + array_offset, length); + pos += length; + return this; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (int index, char value) + { + checkIndex(index); + checkIfReadOnly(); + + backing_buffer [index + array_offset] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/CharViewBufferImpl.java b/libjava/classpath/java/nio/CharViewBufferImpl.java new file mode 100644 index 00000000000..31983154461 --- /dev/null +++ b/libjava/classpath/java/nio/CharViewBufferImpl.java @@ -0,0 +1,187 @@ +/* CharViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +class CharViewBufferImpl extends CharBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + CharViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public CharViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>char</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>char</code>s in this buffer. + */ + public char get () + { + int p = position(); + char result = ByteBufferHelper.getChar(bb, (p << 1) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>char</code> at position + * <code>index</code>. + * + * @param index Position to read the <code>char</code> from. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public char get (int index) + { + return ByteBufferHelper.getChar(bb, (index << 1) + offset, endian); + } + + public CharBuffer put (char value) + { + int p = position(); + ByteBufferHelper.putChar(bb, (p << 1) + offset, value, endian); + position(p + 1); + return this; + } + + public CharBuffer put (int index, char value) + { + ByteBufferHelper.putChar(bb, (index << 1) + offset, value, endian); + return this; + } + + public CharBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 2 * position(), 2 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public CharBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new CharViewBufferImpl (bb, (position () >> 1) + offset, + remaining (), remaining (), 0, -1, + isReadOnly (), endian); + } + + CharBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new CharViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public CharBuffer duplicate () + { + return duplicate(readOnly); + } + + public CharBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public CharSequence subSequence (int start, int end) + { + if (start < 0 + || end < start + || end > length ()) + throw new IndexOutOfBoundsException (); + + return new CharViewBufferImpl (bb, array_offset, capacity (), + position () + end, position () + start, + -1, isReadOnly (), endian); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/DirectByteBufferImpl.java b/libjava/classpath/java/nio/DirectByteBufferImpl.java new file mode 100644 index 00000000000..16672993add --- /dev/null +++ b/libjava/classpath/java/nio/DirectByteBufferImpl.java @@ -0,0 +1,419 @@ +/* DirectByteBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.RawData; + +abstract class DirectByteBufferImpl extends ByteBuffer +{ + /** + * The owner is used to keep alive the object that actually owns the + * memory. There are three possibilities: + * 1) owner == this: We allocated the memory and we should free it, + * but *only* in finalize (if we've been sliced + * other objects will also have access to the + * memory). + * 2) owner == null: The byte buffer was created thru + * JNI.NewDirectByteBuffer. The JNI code is + * responsible for freeing the memory. + * 3) owner == some other object: The other object allocated the + * memory and should free it. + */ + private final Object owner; + + static final class ReadOnly extends DirectByteBufferImpl + { + ReadOnly(Object owner, RawData address, + int capacity, int limit, + int position) + { + super(owner, address, capacity, limit, position); + } + + public ByteBuffer put(byte value) + { + throw new ReadOnlyBufferException (); + } + + public ByteBuffer put(int index, byte value) + { + throw new ReadOnlyBufferException (); + } + + public boolean isReadOnly() + { + return true; + } + } + + static final class ReadWrite extends DirectByteBufferImpl + { + ReadWrite(int capacity) + { + super(capacity); + } + + ReadWrite(Object owner, RawData address, + int capacity, int limit, + int position) + { + super(owner, address, capacity, limit, position); + } + + public boolean isReadOnly() + { + return false; + } + } + + DirectByteBufferImpl(int capacity) + { + super(capacity, capacity, 0, -1); + this.owner = this; + this.address = VMDirectByteBuffer.allocate(capacity); + } + + DirectByteBufferImpl(Object owner, RawData address, + int capacity, int limit, + int position) + { + super(capacity, limit, position, -1); + this.owner = owner; + this.address = address; + } + + /** + * Allocates a new direct byte buffer. + */ + public static ByteBuffer allocate(int capacity) + { + return new DirectByteBufferImpl.ReadWrite(capacity); + } + + protected void finalize() throws Throwable + { + if (owner == this) + VMDirectByteBuffer.free(address); + } + + public byte get() + { + checkForUnderflow(); + + int pos = position(); + byte result = VMDirectByteBuffer.get(address, pos); + position(pos + 1); + return result; + } + + public byte get(int index) + { + checkIndex(index); + + return VMDirectByteBuffer.get(address, index); + } + + public ByteBuffer get(byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + int index = position(); + VMDirectByteBuffer.get(address, index, dst, offset, length); + position(index+length); + + return this; + } + + public ByteBuffer put(byte value) + { + checkForOverflow(); + + int pos = position(); + VMDirectByteBuffer.put(address, pos, value); + position(pos + 1); + return this; + } + + public ByteBuffer put(int index, byte value) + { + checkIndex(index); + + VMDirectByteBuffer.put(address, index, value); + return this; + } + + void shiftDown(int dst_offset, int src_offset, int count) + { + VMDirectByteBuffer.shiftDown(address, dst_offset, src_offset, count); + } + + public ByteBuffer compact() + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + if (pos > 0) + { + int count = remaining(); + VMDirectByteBuffer.shiftDown(address, 0, pos, count); + position(count); + limit(capacity()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public ByteBuffer slice() + { + int rem = remaining(); + if (isReadOnly()) + return new DirectByteBufferImpl.ReadOnly + (owner, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + else + return new DirectByteBufferImpl.ReadWrite + (owner, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + } + + private ByteBuffer duplicate(boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + DirectByteBufferImpl result; + if (readOnly) + result = new DirectByteBufferImpl.ReadOnly(owner, address, capacity(), + limit(), pos); + else + result = new DirectByteBufferImpl.ReadWrite(owner, address, capacity(), + limit(), pos); + + if (mark != pos) + { + result.position(mark); + result.mark(); + result.position(pos); + } + return result; + } + + public ByteBuffer duplicate() + { + return duplicate(isReadOnly()); + } + + public ByteBuffer asReadOnlyBuffer() + { + return duplicate(true); + } + + public boolean isDirect() + { + return true; + } + + public CharBuffer asCharBuffer() + { + return new CharViewBufferImpl(this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer() + { + return new ShortViewBufferImpl(this, remaining() >> 1); + } + + public IntBuffer asIntBuffer() + { + return new IntViewBufferImpl(this, remaining() >> 2); + } + + public LongBuffer asLongBuffer() + { + return new LongViewBufferImpl(this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer() + { + return new FloatViewBufferImpl(this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer() + { + return new DoubleViewBufferImpl(this, remaining() >> 3); + } + + public char getChar() + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar(char value) + { + ByteBufferHelper.putChar(this, value, order()); + return this; + } + + public char getChar(int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar(int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort() + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort(short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort(int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort(int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt() + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt(int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt(int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt(int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong() + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong(long value) + { + ByteBufferHelper.putLong(this, value, order()); + return this; + } + + public long getLong(int index) + { + return ByteBufferHelper.getLong(this, index, order()); + } + + public ByteBuffer putLong(int index, long value) + { + ByteBufferHelper.putLong(this, index, value, order()); + return this; + } + + public float getFloat() + { + return ByteBufferHelper.getFloat(this, order()); + } + + public ByteBuffer putFloat(float value) + { + ByteBufferHelper.putFloat(this, value, order()); + return this; + } + + public float getFloat(int index) + { + return ByteBufferHelper.getFloat(this, index, order()); + } + + public ByteBuffer putFloat(int index, float value) + { + ByteBufferHelper.putFloat(this, index, value, order()); + return this; + } + + public double getDouble() + { + return ByteBufferHelper.getDouble(this, order()); + } + + public ByteBuffer putDouble(double value) + { + ByteBufferHelper.putDouble(this, value, order()); + return this; + } + + public double getDouble(int index) + { + return ByteBufferHelper.getDouble(this, index, order()); + } + + public ByteBuffer putDouble(int index, double value) + { + ByteBufferHelper.putDouble(this, index, value, order()); + return this; + } +} diff --git a/libjava/classpath/java/nio/DoubleBuffer.java b/libjava/classpath/java/nio/DoubleBuffer.java new file mode 100644 index 00000000000..381bb716636 --- /dev/null +++ b/libjava/classpath/java/nio/DoubleBuffer.java @@ -0,0 +1,383 @@ +/* DoubleBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class DoubleBuffer extends Buffer + implements Comparable +{ + int array_offset; + double[] backing_buffer; + + DoubleBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>DoubleBuffer</code> object with a given capacity. + */ + public static DoubleBuffer allocate (int capacity) + { + return new DoubleBufferImpl (capacity); + } + + /** + * Wraps a <code>double</code> array into a <code>DoubleBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final DoubleBuffer wrap (double[] array, int offset, int length) + { + return new DoubleBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>double</code> array into a <code>DoubleBuffer</code> + * object. + */ + public static final DoubleBuffer wrap (double[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>double</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>double</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>double</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>double</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public DoubleBuffer get (double[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>double</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>double</code>s remaining in this buffer. + */ + public DoubleBuffer get (double[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>DoubleBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>double</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (DoubleBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + double[] toPut = new double [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>double array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>double</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (double[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>double array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>double</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final DoubleBuffer put (double[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>double</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>double</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final double[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>long</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data, in Double.doubleToLongBits() form + * Note that the hashcode is dependent on buffer content, + * and therefore is not useful if the buffer content may change. + * + * @return the hash code (casted to int) + */ + public int hashCode () + { + long hashCode = Double.doubleToLongBits(get(position())) + 31; + long multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (Double.doubleToLongBits(get(i)) + 30)*multiplier; + } + return ((int)hashCode); + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof DoubleBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>DoubleBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>DoubleBuffer</code>. + */ + public int compareTo (Object obj) + { + DoubleBuffer other = (DoubleBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + double a = get(pos_this++); + double b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>double</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>double</code>s in this buffer. + */ + public abstract double get (); + + /** + * Writes the <code>double</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>double</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer put (double b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract double get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer put (int index, double b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>DoubleBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract DoubleBuffer slice (); + + /** + * Creates a new <code>DoubleBuffer</code> that shares this buffer's + * content. + */ + public abstract DoubleBuffer duplicate (); + + /** + * Creates a new read-only <code>DoubleBuffer</code> that shares this + * buffer's content. + */ + public abstract DoubleBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/DoubleBufferImpl.java b/libjava/classpath/java/nio/DoubleBufferImpl.java new file mode 100644 index 00000000000..248ab45e7c2 --- /dev/null +++ b/libjava/classpath/java/nio/DoubleBufferImpl.java @@ -0,0 +1,172 @@ +/* DoubleBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class DoubleBufferImpl extends DoubleBuffer +{ + private boolean readOnly; + + DoubleBufferImpl (int capacity) + { + this (new double [capacity], 0, capacity, capacity, 0, -1, false); + } + + DoubleBufferImpl (double[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public DoubleBuffer slice () + { + return new DoubleBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public DoubleBuffer duplicate () + { + return new DoubleBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public DoubleBuffer asReadOnlyBuffer () + { + return new DoubleBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public DoubleBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>double</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>double</code>s in this buffer. + */ + public double get () + { + checkForUnderflow(); + + double result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (double value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the <code>double</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public double get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (int index, double value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/DoubleViewBufferImpl.java b/libjava/classpath/java/nio/DoubleViewBufferImpl.java new file mode 100644 index 00000000000..e860f2f808d --- /dev/null +++ b/libjava/classpath/java/nio/DoubleViewBufferImpl.java @@ -0,0 +1,172 @@ +/* DoubleViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class DoubleViewBufferImpl extends DoubleBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + DoubleViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public DoubleViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>double</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>double</code>s in this buffer. + */ + public double get () + { + int p = position(); + double result = ByteBufferHelper.getDouble(bb, (p << 3) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>double</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public double get (int index) + { + return ByteBufferHelper.getDouble(bb, (index << 3) + offset, endian); + } + + public DoubleBuffer put (double value) + { + int p = position(); + ByteBufferHelper.putDouble(bb, (p << 3) + offset, value, endian); + position(p + 1); + return this; + } + + public DoubleBuffer put (int index, double value) + { + ByteBufferHelper.putDouble(bb, (index << 3) + offset, value, endian); + return this; + } + + public DoubleBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 8 * position(), 8 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public DoubleBuffer slice () + { + return new DoubleViewBufferImpl (bb, (position () >> 3) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + DoubleBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new DoubleViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public DoubleBuffer duplicate () + { + return duplicate(readOnly); + } + + public DoubleBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/FloatBuffer.java b/libjava/classpath/java/nio/FloatBuffer.java new file mode 100644 index 00000000000..8042333cbac --- /dev/null +++ b/libjava/classpath/java/nio/FloatBuffer.java @@ -0,0 +1,383 @@ +/* FloatBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class FloatBuffer extends Buffer + implements Comparable +{ + int array_offset; + float[] backing_buffer; + + FloatBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>FloatBuffer</code> object with a given capacity. + */ + public static FloatBuffer allocate (int capacity) + { + return new FloatBufferImpl (capacity); + } + + /** + * Wraps a <code>float</code> array into a <code>FloatBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final FloatBuffer wrap (float[] array, int offset, int length) + { + return new FloatBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>float</code> array into a <code>FloatBuffer</code> + * object. + */ + public static final FloatBuffer wrap (float[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>float</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>float</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>float</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>float</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public FloatBuffer get (float[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>float</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>float</code>s remaining in this buffer. + */ + public FloatBuffer get (float[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>FloatBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>float</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (FloatBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + float[] toPut = new float [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>float array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>float</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (float[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>float array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>float</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final FloatBuffer put (float[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>float</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>float</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final float[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>int</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data, in Float.floatToIntBits() form + * Note that the hashcode is dependent on buffer content, + * and therefore is not useful if the buffer content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = Float.floatToIntBits(get(position())) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (Float.floatToIntBits(get(i)) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof FloatBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>FloatBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>FloatBuffer</code>. + */ + public int compareTo (Object obj) + { + FloatBuffer other = (FloatBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + float a = get(pos_this++); + float b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>float</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>float</code>s in this buffer. + */ + public abstract float get (); + + /** + * Writes the <code>float</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>float</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer put (float b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract float get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer put (int index, float b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>FloatBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract FloatBuffer slice (); + + /** + * Creates a new <code>FloatBuffer</code> that shares this buffer's + * content. + */ + public abstract FloatBuffer duplicate (); + + /** + * Creates a new read-only <code>FloatBuffer</code> that shares this + * buffer's content. + */ + public abstract FloatBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/FloatBufferImpl.java b/libjava/classpath/java/nio/FloatBufferImpl.java new file mode 100644 index 00000000000..b4868780c42 --- /dev/null +++ b/libjava/classpath/java/nio/FloatBufferImpl.java @@ -0,0 +1,172 @@ +/* FloatBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class FloatBufferImpl extends FloatBuffer +{ + private boolean readOnly; + + FloatBufferImpl (int capacity) + { + this (new float [capacity], 0, capacity, capacity, 0, -1, false); + } + + FloatBufferImpl (float[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public FloatBuffer slice () + { + return new FloatBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public FloatBuffer duplicate () + { + return new FloatBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public FloatBuffer asReadOnlyBuffer () + { + return new FloatBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public FloatBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>float</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>floats</code> in this buffer. + */ + public float get () + { + checkForUnderflow(); + + float result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (float value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the <code>float</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public float get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (int index, float value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/FloatViewBufferImpl.java b/libjava/classpath/java/nio/FloatViewBufferImpl.java new file mode 100644 index 00000000000..55770d52a29 --- /dev/null +++ b/libjava/classpath/java/nio/FloatViewBufferImpl.java @@ -0,0 +1,173 @@ +/* FloatViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class FloatViewBufferImpl extends FloatBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + FloatViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public FloatViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>float</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>floats</code> in this buffer. + */ + public float get () + { + int p = position(); + float result = ByteBufferHelper.getFloat(bb, (p << 2) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>float</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public float get (int index) + { + return ByteBufferHelper.getFloat(bb, (index << 2) + offset, endian); + } + + public FloatBuffer put (float value) + { + int p = position(); + ByteBufferHelper.putFloat(bb, (p << 2) + offset, value, endian); + position(p + 1); + return this; + } + + public FloatBuffer put (int index, float value) + { + ByteBufferHelper.putFloat(bb, (index << 2) + offset, value, endian); + return this; + } + + public FloatBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 4 * position(), 4 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public FloatBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new FloatViewBufferImpl (bb, (position () >> 2) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + FloatBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new FloatViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public FloatBuffer duplicate () + { + return duplicate(readOnly); + } + + public FloatBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/IntBuffer.java b/libjava/classpath/java/nio/IntBuffer.java new file mode 100644 index 00000000000..1e1fe9c7565 --- /dev/null +++ b/libjava/classpath/java/nio/IntBuffer.java @@ -0,0 +1,383 @@ +/* IntBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class IntBuffer extends Buffer + implements Comparable +{ + int array_offset; + int[] backing_buffer; + + IntBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>IntBuffer</code> object with a given capacity. + */ + public static IntBuffer allocate (int capacity) + { + return new IntBufferImpl (capacity); + } + + /** + * Wraps a <code>int</code> array into a <code>IntBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final IntBuffer wrap (int[] array, int offset, int length) + { + return new IntBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>int</code> array into a <code>IntBuffer</code> + * object. + */ + public static final IntBuffer wrap (int[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>int</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>int</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>int</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>int</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public IntBuffer get (int[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>int</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>int</code>s remaining in this buffer. + */ + public IntBuffer get (int[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>IntBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>int</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (IntBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + int[] toPut = new int [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>int array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>int</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>int array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>int</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final IntBuffer put (int[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>int</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>int</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>int</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof IntBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>IntBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>IntBuffer</code>. + */ + public int compareTo (Object obj) + { + IntBuffer other = (IntBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + int a = get(pos_this++); + int b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>int</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>int</code>s in this buffer. + */ + public abstract int get (); + + /** + * Writes the <code>int</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>int</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer put (int b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract int get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer put (int index, int b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>IntBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract IntBuffer slice (); + + /** + * Creates a new <code>IntBuffer</code> that shares this buffer's + * content. + */ + public abstract IntBuffer duplicate (); + + /** + * Creates a new read-only <code>IntBuffer</code> that shares this + * buffer's content. + */ + public abstract IntBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/IntBufferImpl.java b/libjava/classpath/java/nio/IntBufferImpl.java new file mode 100644 index 00000000000..22657482b42 --- /dev/null +++ b/libjava/classpath/java/nio/IntBufferImpl.java @@ -0,0 +1,172 @@ +/* IntBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class IntBufferImpl extends IntBuffer +{ + private boolean readOnly; + + IntBufferImpl (int capacity) + { + this (new int [capacity], 0, capacity, capacity, 0, -1, false); + } + + IntBufferImpl (int[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public IntBuffer slice () + { + return new IntBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public IntBuffer duplicate () + { + return new IntBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public IntBuffer asReadOnlyBuffer () + { + return new IntBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public IntBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>int</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>ints</code> in this buffer. + */ + public int get () + { + checkForUnderflow(); + + int result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the <code>int</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public int get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int index, int value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/IntViewBufferImpl.java b/libjava/classpath/java/nio/IntViewBufferImpl.java new file mode 100644 index 00000000000..d0b0057c117 --- /dev/null +++ b/libjava/classpath/java/nio/IntViewBufferImpl.java @@ -0,0 +1,173 @@ +/* IntViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class IntViewBufferImpl extends IntBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + IntViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public IntViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>int</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>ints</code> in this buffer. + */ + public int get () + { + int p = position(); + int result = ByteBufferHelper.getInt(bb, (p << 2) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>int</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public int get (int index) + { + return ByteBufferHelper.getInt(bb, (index << 2) + offset, endian); + } + + public IntBuffer put (int value) + { + int p = position(); + ByteBufferHelper.putInt(bb, (p << 2) + offset, value, endian); + position(p + 1); + return this; + } + + public IntBuffer put (int index, int value) + { + ByteBufferHelper.putInt(bb, (index << 2) + offset, value, endian); + return this; + } + + public IntBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 4 * position(), 4 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public IntBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new IntViewBufferImpl (bb, (position () >> 2) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + IntBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new IntViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public IntBuffer duplicate () + { + return duplicate(readOnly); + } + + public IntBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/InvalidMarkException.java b/libjava/classpath/java/nio/InvalidMarkException.java new file mode 100644 index 00000000000..937d417ab13 --- /dev/null +++ b/libjava/classpath/java/nio/InvalidMarkException.java @@ -0,0 +1,52 @@ +/* InvalidMarkException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class InvalidMarkException extends IllegalStateException +{ + /** + * Creates the exception + */ + public InvalidMarkException () + { + } +} diff --git a/libjava/classpath/java/nio/LongBuffer.java b/libjava/classpath/java/nio/LongBuffer.java new file mode 100644 index 00000000000..b3d3557edcf --- /dev/null +++ b/libjava/classpath/java/nio/LongBuffer.java @@ -0,0 +1,383 @@ +/* LongBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class LongBuffer extends Buffer + implements Comparable +{ + int array_offset; + long[] backing_buffer; + + LongBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>LongBuffer</code> object with a given capacity. + */ + public static LongBuffer allocate (int capacity) + { + return new LongBufferImpl (capacity); + } + + /** + * Wraps a <code>long</code> array into a <code>LongBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final LongBuffer wrap (long[] array, int offset, int length) + { + return new LongBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>long</code> array into a <code>LongBuffer</code> + * object. + */ + public static final LongBuffer wrap (long[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>long</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>long</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>long</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>long</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public LongBuffer get (long[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>long</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>long</code>s remaining in this buffer. + */ + public LongBuffer get (long[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>LongBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>long</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (LongBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + long[] toPut = new long [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>long array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>long</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (long[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>long array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>long</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final LongBuffer put (long[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>long</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>long</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final long[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>long</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code (casted to int) + */ + public int hashCode () + { + long hashCode = get(position()) + 31; + long multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return ((int)hashCode); + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof LongBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>LongBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>LongBuffer</code>. + */ + public int compareTo (Object obj) + { + LongBuffer other = (LongBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + long a = get(pos_this++); + long b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>long</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>long</code>s in this buffer. + */ + public abstract long get (); + + /** + * Writes the <code>long</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>long</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer put (long b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract long get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer put (int index, long b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>LongBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract LongBuffer slice (); + + /** + * Creates a new <code>LongBuffer</code> that shares this buffer's + * content. + */ + public abstract LongBuffer duplicate (); + + /** + * Creates a new read-only <code>LongBuffer</code> that shares this + * buffer's content. + */ + public abstract LongBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/LongBufferImpl.java b/libjava/classpath/java/nio/LongBufferImpl.java new file mode 100644 index 00000000000..8772f618c19 --- /dev/null +++ b/libjava/classpath/java/nio/LongBufferImpl.java @@ -0,0 +1,172 @@ +/* LongBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class LongBufferImpl extends LongBuffer +{ + private boolean readOnly; + + LongBufferImpl (int capacity) + { + this (new long [capacity], 0, capacity, capacity, 0, -1, false); + } + + LongBufferImpl (long[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public LongBuffer slice () + { + return new LongBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public LongBuffer duplicate () + { + return new LongBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public LongBuffer asReadOnlyBuffer () + { + return new LongBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public LongBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>long</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>longs</code> in this buffer. + */ + public long get () + { + checkForUnderflow(); + + long result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (long value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the <code>long</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public long get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (int index, long value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/LongViewBufferImpl.java b/libjava/classpath/java/nio/LongViewBufferImpl.java new file mode 100644 index 00000000000..9c3452ae809 --- /dev/null +++ b/libjava/classpath/java/nio/LongViewBufferImpl.java @@ -0,0 +1,173 @@ +/* LongViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class LongViewBufferImpl extends LongBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + LongViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public LongViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>long</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>longs</code> in this buffer. + */ + public long get () + { + int p = position(); + long result = ByteBufferHelper.getLong(bb, (p << 3) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>long</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public long get (int index) + { + return ByteBufferHelper.getLong(bb, (index << 3) + offset, endian); + } + + public LongBuffer put (long value) + { + int p = position(); + ByteBufferHelper.putLong(bb, (p << 3) + offset, value, endian); + position(p + 1); + return this; + } + + public LongBuffer put (int index, long value) + { + ByteBufferHelper.putLong(bb, (index << 3) + offset, value, endian); + return this; + } + + public LongBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 8 * position(), 8 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public LongBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new LongViewBufferImpl (bb, (position () >> 3) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + LongBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new LongViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public LongBuffer duplicate () + { + return duplicate(readOnly); + } + + public LongBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/MappedByteBuffer.java b/libjava/classpath/java/nio/MappedByteBuffer.java new file mode 100644 index 00000000000..fa25bb7f1e7 --- /dev/null +++ b/libjava/classpath/java/nio/MappedByteBuffer.java @@ -0,0 +1,93 @@ +/* MappedByteBuffer.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public abstract class MappedByteBuffer extends ByteBuffer +{ + MappedByteBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + } + + void forceImpl() + { + } + + public final MappedByteBuffer force () + { + forceImpl(); + return this; + } + + boolean isLoadedImpl() + { + load(); + return true; + } + + public final boolean isLoaded () + { + return isLoadedImpl(); + } + + void loadImpl() + { + } + + public final MappedByteBuffer load () + { + loadImpl(); + return this; + } + + void unmapImpl () + { + forceImpl(); + } + + public void finalize() + throws Throwable + { + unmapImpl(); + } +} diff --git a/libjava/classpath/java/nio/MappedByteBufferImpl.java b/libjava/classpath/java/nio/MappedByteBufferImpl.java new file mode 100644 index 00000000000..1b075d8753e --- /dev/null +++ b/libjava/classpath/java/nio/MappedByteBufferImpl.java @@ -0,0 +1,360 @@ +/* MappedByteBufferImpl.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.RawData; + +import java.io.IOException; + +final class MappedByteBufferImpl extends MappedByteBuffer +{ + boolean readOnly; + + /** Posix uses this for the pointer returned by mmap; + * Win32 uses it for the pointer returned by MapViewOfFile. */ + public RawData implPtr; + /** Posix uses this for the actual length passed to mmap; + * Win32 uses it for the pointer returned by CreateFileMapping. */ + public long implLen; + + public MappedByteBufferImpl(RawData address, int size, boolean readOnly) + throws IOException + { + super(size, size, 0, -1); + this.address = address; + this.readOnly = readOnly; + } + + public boolean isReadOnly() + { + return readOnly; + } + + public byte get() + { + checkForUnderflow(); + + int pos = position(); + byte result = VMDirectByteBuffer.get(address, pos); + position(pos + 1); + return result; + } + + public ByteBuffer put(byte value) + { + checkIfReadOnly(); + checkForOverflow(); + + int pos = position(); + VMDirectByteBuffer.put(address, pos, value); + position(pos + 1); + return this; + } + + public byte get(int index) + { + checkIndex(index); + + return VMDirectByteBuffer.get(address, index); + } + + public ByteBuffer get(byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + int index = position(); + VMDirectByteBuffer.get(address, index, dst, offset, length); + position(index+length); + + return this; + } + + public ByteBuffer put(int index, byte value) + { + checkIfReadOnly(); + checkIndex(index); + + VMDirectByteBuffer.put(address, index, value); + return this; + } + + public ByteBuffer compact() + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + if (pos > 0) + { + int count = remaining(); + // Call shiftDown method optimized for direct buffers. + VMDirectByteBuffer.shiftDown(address, 0, pos, count); + position(count); + limit(capacity()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public boolean isDirect() + { + return true; + } + + public ByteBuffer slice() + { + int rem = remaining(); + if (isReadOnly()) + return new DirectByteBufferImpl.ReadOnly + (this, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + else + return new DirectByteBufferImpl.ReadWrite + (this, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + } + + private ByteBuffer duplicate(boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + DirectByteBufferImpl result; + if (readOnly) + result = new DirectByteBufferImpl.ReadOnly(this, address, capacity(), + limit(), pos); + else + result = new DirectByteBufferImpl.ReadWrite(this, address, capacity(), + limit(), pos); + + if (mark != pos) + { + result.position(mark); + result.mark(); + result.position(pos); + } + return result; + } + + public ByteBuffer duplicate() + { + return duplicate(isReadOnly()); + } + + public ByteBuffer asReadOnlyBuffer() + { + return duplicate(true); + } + + public CharBuffer asCharBuffer() + { + return new CharViewBufferImpl(this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer() + { + return new ShortViewBufferImpl(this, remaining() >> 1); + } + + public IntBuffer asIntBuffer() + { + return new IntViewBufferImpl(this, remaining() >> 2); + } + + public LongBuffer asLongBuffer() + { + return new LongViewBufferImpl(this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer() + { + return new FloatViewBufferImpl(this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer() + { + return new DoubleViewBufferImpl(this, remaining() >> 3); + } + + public char getChar() + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar(char value) + { + ByteBufferHelper.putChar(this, value, order()); + return this; + } + + public char getChar(int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar(int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort() + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort(short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort(int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort(int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt() + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt(int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt(int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt(int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong() + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong(long value) + { + ByteBufferHelper.putLong(this, value, order()); + return this; + } + + public long getLong(int index) + { + return ByteBufferHelper.getLong(this, index, order()); + } + + public ByteBuffer putLong(int index, long value) + { + ByteBufferHelper.putLong(this, index, value, order()); + return this; + } + + public float getFloat() + { + return ByteBufferHelper.getFloat(this, order()); + } + + public ByteBuffer putFloat(float value) + { + ByteBufferHelper.putFloat(this, value, order()); + return this; + } + + public float getFloat(int index) + { + return ByteBufferHelper.getFloat(this, index, order()); + } + + public ByteBuffer putFloat(int index, float value) + { + ByteBufferHelper.putFloat(this, index, value, order()); + return this; + } + + public double getDouble() + { + return ByteBufferHelper.getDouble(this, order()); + } + + public ByteBuffer putDouble(double value) + { + ByteBufferHelper.putDouble(this, value, order()); + return this; + } + + public double getDouble(int index) + { + return ByteBufferHelper.getDouble(this, index, order()); + } + + public ByteBuffer putDouble(int index, double value) + { + ByteBufferHelper.putDouble(this, index, value, order()); + return this; + } + + // NOTE: In libgcj these methods are implemented in natFileChannelXxx.cc, + // because they're small, and to put them next to FileChannelImpl::mapImpl. + native void unmapImpl(); + native boolean isLoadedImpl(); + // FIXME: Try to load all pages into memory. + native void loadImpl(); + + native void forceImpl(); +} diff --git a/libjava/classpath/java/nio/ReadOnlyBufferException.java b/libjava/classpath/java/nio/ReadOnlyBufferException.java new file mode 100644 index 00000000000..d710e1b26a1 --- /dev/null +++ b/libjava/classpath/java/nio/ReadOnlyBufferException.java @@ -0,0 +1,52 @@ +/* ReadOnlyBufferException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ReadOnlyBufferException extends UnsupportedOperationException +{ + /** + * Creates the exception + */ + public ReadOnlyBufferException () + { + } +} diff --git a/libjava/classpath/java/nio/ShortBuffer.java b/libjava/classpath/java/nio/ShortBuffer.java new file mode 100644 index 00000000000..958fe8cd6b6 --- /dev/null +++ b/libjava/classpath/java/nio/ShortBuffer.java @@ -0,0 +1,383 @@ +/* ShortBuffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @since 1.4 + */ +public abstract class ShortBuffer extends Buffer + implements Comparable +{ + int array_offset; + short[] backing_buffer; + + ShortBuffer (int capacity, int limit, int position, int mark) + { + super (capacity, limit, position, mark); + array_offset = 0; + } + + /** + * Allocates a new <code>ShortBuffer</code> object with a given capacity. + */ + public static ShortBuffer allocate (int capacity) + { + return new ShortBufferImpl (capacity); + } + + /** + * Wraps a <code>short</code> array into a <code>ShortBuffer</code> + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final ShortBuffer wrap (short[] array, int offset, int length) + { + return new ShortBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a <code>short</code> array into a <code>ShortBuffer</code> + * object. + */ + public static final ShortBuffer wrap (short[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers <code>short</code>s from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length <code>short</code>s remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first <code>short</code> + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * <code>short</code>s remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public ShortBuffer get (short[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers <code>short</code>s from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * <code>short</code>s remaining in this buffer. + */ + public ShortBuffer get (short[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the <code>ShortBUFFER</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * <code>src.remaining()</code> space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>short</code>s in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (ShortBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + short[] toPut = new short [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the <code>short array</code> src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>short</code>s in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (short[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the <code>short array</code> src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining <code>short</code>s in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final ShortBuffer put (short[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * <code>short</code> array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the <code>short</code> array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final short[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with <code>int</code> arithmetic, + * where ** represents exponentiation, by this formula:<br> + * <code>s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1)</code>. + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof ShortBuffer) + { + return compareTo (obj) == 0; + } + + return false; + } + + /** + * Compares two <code>ShortBuffer</code> objects. + * + * @exception ClassCastException If obj is not an object derived from + * <code>ShortBuffer</code>. + */ + public int compareTo (Object obj) + { + ShortBuffer other = (ShortBuffer) obj; + + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + short a = get(pos_this++); + short b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the <code>short</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>short</code>s in this buffer. + */ + public abstract short get (); + + /** + * Writes the <code>short</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * <code>short</code>s in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer put (short b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract short get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer put (int index, short b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new <code>ShortBuffer</code> whose content is a shared + * subsequence of this buffer's content. + */ + public abstract ShortBuffer slice (); + + /** + * Creates a new <code>ShortBuffer</code> that shares this buffer's + * content. + */ + public abstract ShortBuffer duplicate (); + + /** + * Creates a new read-only <code>ShortBuffer</code> that shares this + * buffer's content. + */ + public abstract ShortBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/ShortBufferImpl.java b/libjava/classpath/java/nio/ShortBufferImpl.java new file mode 100644 index 00000000000..ee5bff2f95b --- /dev/null +++ b/libjava/classpath/java/nio/ShortBufferImpl.java @@ -0,0 +1,172 @@ +/* ShortBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class ShortBufferImpl extends ShortBuffer +{ + private boolean readOnly; + + ShortBufferImpl (int capacity) + { + this (new short [capacity], 0, capacity, capacity, 0, -1, false); + } + + ShortBufferImpl (short[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark); + this.backing_buffer = buffer; + this.array_offset = offset; + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public ShortBuffer slice () + { + return new ShortBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public ShortBuffer duplicate () + { + return new ShortBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public ShortBuffer asReadOnlyBuffer () + { + return new ShortBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public ShortBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int copied = 0; + + while (remaining () > 0) + { + put (copied, get ()); + copied++; + } + + position (copied); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the <code>short</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>short</code>s in this buffer. + */ + public short get () + { + checkForUnderflow(); + + short result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes <code>value</code> to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (short value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the <code>short</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public short get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes <code>value</code> to position + * <code>index</code> in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (int index, short value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/ShortViewBufferImpl.java b/libjava/classpath/java/nio/ShortViewBufferImpl.java new file mode 100644 index 00000000000..cdd559522ff --- /dev/null +++ b/libjava/classpath/java/nio/ShortViewBufferImpl.java @@ -0,0 +1,173 @@ +/* ShortViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class ShortViewBufferImpl extends ShortBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private int offset; + private ByteBuffer bb; + private boolean readOnly; + private ByteOrder endian; + + ShortViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + public ShortViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + if (bb.isDirect()) + this.address = VMDirectByteBuffer.adjustAddress(bb.address, offset); + } + + /** + * Reads the <code>short</code> at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * <code>short</code>s in this buffer. + */ + public short get () + { + int p = position(); + short result = ByteBufferHelper.getShort(bb, (p << 1) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the <code>short</code> at position + * <code>index</code>. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public short get (int index) + { + return ByteBufferHelper.getShort(bb, (index << 1) + offset, endian); + } + + public ShortBuffer put (short value) + { + int p = position(); + ByteBufferHelper.putShort(bb, (p << 1) + offset, value, endian); + position(p + 1); + return this; + } + + public ShortBuffer put (int index, short value) + { + ByteBufferHelper.putShort(bb, (index << 1) + offset, value, endian); + return this; + } + + public ShortBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 2 * position(), 2 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public ShortBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new ShortViewBufferImpl (bb, (position () >> 1) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + ShortBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new ShortViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public ShortBuffer duplicate () + { + return duplicate(readOnly); + } + + public ShortBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/channels/AlreadyConnectedException.java b/libjava/classpath/java/nio/channels/AlreadyConnectedException.java new file mode 100644 index 00000000000..133547ef308 --- /dev/null +++ b/libjava/classpath/java/nio/channels/AlreadyConnectedException.java @@ -0,0 +1,48 @@ +/* AlreadyConnectedException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +public class AlreadyConnectedException extends IllegalStateException +{ + /** + * Creates the exception + */ + public AlreadyConnectedException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/AsynchronousCloseException.java b/libjava/classpath/java/nio/channels/AsynchronousCloseException.java new file mode 100644 index 00000000000..f45fdd81ab4 --- /dev/null +++ b/libjava/classpath/java/nio/channels/AsynchronousCloseException.java @@ -0,0 +1,53 @@ +/* AsynchronousCloseException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class AsynchronousCloseException extends ClosedChannelException +{ + /** + * Creates the exception + */ + public AsynchronousCloseException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ByteChannel.java b/libjava/classpath/java/nio/channels/ByteChannel.java new file mode 100644 index 00000000000..d7d7a451b4d --- /dev/null +++ b/libjava/classpath/java/nio/channels/ByteChannel.java @@ -0,0 +1,43 @@ +/* ByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +public interface ByteChannel extends ReadableByteChannel, + WritableByteChannel +{ +} diff --git a/libjava/classpath/java/nio/channels/CancelledKeyException.java b/libjava/classpath/java/nio/channels/CancelledKeyException.java new file mode 100644 index 00000000000..02108f6c7b3 --- /dev/null +++ b/libjava/classpath/java/nio/channels/CancelledKeyException.java @@ -0,0 +1,53 @@ +/* CancelledKeyException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class CancelledKeyException extends IllegalStateException +{ + /** + * Creates the exception + */ + public CancelledKeyException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/Channel.java b/libjava/classpath/java/nio/channels/Channel.java new file mode 100644 index 00000000000..d488bd27dd0 --- /dev/null +++ b/libjava/classpath/java/nio/channels/Channel.java @@ -0,0 +1,59 @@ +/* Channel.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import java.io.IOException; + +public interface Channel +{ + /** + * Tells whether this channel is open or not + * + * @return <code>true</code>if channel is open, + * <code>false</code> otherwise + */ + boolean isOpen(); + + /** + * Closes this channel + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/Channels.java b/libjava/classpath/java/nio/channels/Channels.java new file mode 100644 index 00000000000..cdb2e833e9d --- /dev/null +++ b/libjava/classpath/java/nio/channels/Channels.java @@ -0,0 +1,143 @@ +/* Channels.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import gnu.java.nio.ChannelReader; +import gnu.java.nio.InputStreamChannel; +import gnu.java.nio.OutputStreamChannel; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + + +/** + * @since 1.4 + */ +public final class Channels +{ + /** + * This class isn't intended to be instantiated. + */ + private Channels() + { + // Do nothing here. + } + + /** + * Constructs a stream that reads bytes from the given channel. + */ + public static InputStream newInputStream(ReadableByteChannel ch) + { + return VMChannels.newInputStream(ch); + } + + /** + * Constructs a stream that writes bytes to the given channel. + */ + public static OutputStream newOutputStream(WritableByteChannel ch) + { + return VMChannels.newOutputStream(ch); + } + + /** + * Constructs a channel that reads bytes from the given stream. + */ + public static ReadableByteChannel newChannel(InputStream in) + { + return new InputStreamChannel(in); + } + + /** + * Constructs a channel that writes bytes to the given stream. + */ + public static WritableByteChannel newChannel(OutputStream out) + { + return new OutputStreamChannel(out); + } + + /** + * Constructs a reader that decodes bytes from the given channel using the + * given decoder. + */ + public static Reader newReader(ReadableByteChannel ch, CharsetDecoder dec, + int minBufferCap) + { + return new ChannelReader(ch, dec, minBufferCap); + } + + /** + * Constructs a reader that decodes bytes from the given channel according to + * the named charset. + * + * @exception UnsupportedCharsetException If no support for the named charset + * is available in this instance of the Java virtual machine. + */ + public static Reader newReader(ReadableByteChannel ch, String csName) + { + return newReader(ch, Charset.forName(csName).newDecoder(), -1); + } + + /** + * Constructs a writer that encodes characters using the given encoder and + * writes the resulting bytes to the given channel. + */ + public static Writer newWriter(WritableByteChannel ch, CharsetEncoder enc, + int minBufferCap) + { + // FIXME: implement java.nio.channels.Channel.newWriter(WritableByteChannel, CharsetEncoder, int) + throw new Error("not implemented"); + } + + /** + * Constructs a writer that encodes characters according to the named charset + * and writes the resulting bytes to the given channel. + * + * @exception UnsupportedCharsetException If no support for the named charset + * is available in this instance of the Java virtual machine. + */ + public static Writer newWriter(WritableByteChannel ch, String csName) + { + return newWriter(ch, Charset.forName(csName).newEncoder(), -1); + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedByInterruptException.java b/libjava/classpath/java/nio/channels/ClosedByInterruptException.java new file mode 100644 index 00000000000..17858f613c6 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedByInterruptException.java @@ -0,0 +1,53 @@ +/* ClosedByInterruptException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedByInterruptException extends AsynchronousCloseException +{ + /** + * Creates the exception + */ + public ClosedByInterruptException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedChannelException.java b/libjava/classpath/java/nio/channels/ClosedChannelException.java new file mode 100644 index 00000000000..0f8df9b26c4 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedChannelException.java @@ -0,0 +1,55 @@ +/* ClosedChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedChannelException extends IOException +{ + /** + * Creates the exception + */ + public ClosedChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedSelectorException.java b/libjava/classpath/java/nio/channels/ClosedSelectorException.java new file mode 100644 index 00000000000..e1b7a8ce939 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedSelectorException.java @@ -0,0 +1,53 @@ +/* ClosedSelectorException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedSelectorException extends IllegalStateException +{ + /** + * Creates the exception + */ + public ClosedSelectorException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ConnectionPendingException.java b/libjava/classpath/java/nio/channels/ConnectionPendingException.java new file mode 100644 index 00000000000..b0b71294f7e --- /dev/null +++ b/libjava/classpath/java/nio/channels/ConnectionPendingException.java @@ -0,0 +1,53 @@ +/* ConnectionPendingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ConnectionPendingException extends IllegalStateException +{ + /** + * Creates the exception + */ + public ConnectionPendingException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/DatagramChannel.java b/libjava/classpath/java/nio/channels/DatagramChannel.java new file mode 100644 index 00000000000..d257ff33865 --- /dev/null +++ b/libjava/classpath/java/nio/channels/DatagramChannel.java @@ -0,0 +1,210 @@ +/* DatagramChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @since 1.4 + */ +public abstract class DatagramChannel extends AbstractSelectableChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel +{ + /** + * Initializes the channel. + */ + protected DatagramChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Opens a datagram channel. + * + * @exception IOException If an error occurs + */ + public static DatagramChannel open() throws IOException + { + return SelectorProvider.provider().openDatagramChannel(); + } + + /** + * Reads data from this channel. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += read(dsts[i]); + + return b; + } + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public final long write(ByteBuffer[] srcs) throws IOException + { + long b = 0; + + for (int i = 0; i < srcs.length; i++) + b += write(srcs[i]); + + return b; + } + + /** + * Connects this channel's socket. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs. + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract DatagramChannel connect(SocketAddress remote) + throws IOException; + + /** + * Disonnects this channel's socket. + * + * @exception IOException If an error occurs + */ + public abstract DatagramChannel disconnect() throws IOException; + + /** + * Tells whether or not this channel's socket is connected. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract boolean isConnected(); + + /** + * Reads data from this channel. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Reads data from this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Receives a datagram via this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract SocketAddress receive(ByteBuffer dst) + throws IOException; + + /** + * Sends a datagram via this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract int send(ByteBuffer src, SocketAddress target) + throws IOException; + + /** + * Retrieves the channel's socket. + */ + public abstract DatagramSocket socket(); + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Retrieves the valid operations for this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public final int validOps() + { + return SelectionKey.OP_READ | SelectionKey.OP_WRITE; + } +} diff --git a/libjava/classpath/java/nio/channels/FileChannel.java b/libjava/classpath/java/nio/channels/FileChannel.java new file mode 100644 index 00000000000..944ec0b8f3e --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileChannel.java @@ -0,0 +1,367 @@ +/* FileChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.spi.AbstractInterruptibleChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class FileChannel extends AbstractInterruptibleChannel + implements ByteChannel, GatheringByteChannel, ScatteringByteChannel +{ + public static class MapMode + { + int m; + public static final MapMode READ_ONLY = new MapMode(0); + public static final MapMode READ_WRITE = new MapMode(1); + public static final MapMode PRIVATE = new MapMode(2); + + /** + * Initializes the MapMode. + */ + MapMode(int a) + { + m = a; + } + + /** + * Returns a string representation of the <code>MapMode</code> object. + */ + public String toString() + { + if (this == READ_ONLY) + return "READ_ONLY"; + else if (this == READ_WRITE) + return "READ_WRITE"; + + return "PRIVATE"; + } + } + + /** + * Initializes the channel. + */ + protected FileChannel() + { + } + + /** + * Maps the file into the memory. + * + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If mode is READ_ONLY but this channel was + * not opened for reading. + * @exception NonWritableChannelException If mode is READ_WRITE or PRIVATE but this + * channel was not opened for writing. + */ + public abstract MappedByteBuffer map(MapMode mode, long position, long size) + throws IOException; + + /** + * Return the size of the file thus far + * + * @exception ClosedChannelException If this channel is closed. + */ + public abstract long size() throws IOException; + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public final long write(ByteBuffer[] srcs) throws IOException + { + long result = 0; + + for (int i = 0; i < srcs.length; i++) + result += write(srcs[i]); + + return result; + } + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to the channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If position is negative. + * @exception IOException If an I/O error occurs. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract int write(ByteBuffer srcs, long position) + throws IOException; + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + long result = 0; + + for (int i = 0; i < dsts.length; i++) + read(dsts[i]); + + return result; + } + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Reads data from the channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If position is negative. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If this channel was not opened for + * reading. + */ + public abstract int read(ByteBuffer dst, long position) + throws IOException; + + /** + * Closes the channel. + * + * This is called from @see close. + * + * @exception IOException If an I/O error occurs. + */ + protected abstract void implCloseChannel() throws IOException; + + /** + * msync with the disk + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + */ + public abstract void force(boolean metaData) throws IOException; + + /** + * Creates a file lock for the whole assoziated file. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedChannelException If this channel is closed. + * @exception FileLockInterruptionException If the invoking thread is + * interrupted while blocked in this method. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If shared is true and this channel + * was not opened for reading. + * @exception NonWritableChannelException If shared is false and this channel + * was not opened for writing. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public final FileLock lock() throws IOException + { + return lock(0, Long.MAX_VALUE, false); + } + + /** + * Creates a file lock for a region of the assoziated file. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedChannelException If this channel is closed. + * @exception FileLockInterruptionException If the invoking thread is + * interrupted while blocked in this method. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + * @exception NonReadableChannelException If shared is true and this channel + * was not opened for reading. + * @exception NonWritableChannelException If shared is false and this channel + * was not opened for writing. + */ + public abstract FileLock lock(long position, long size, boolean shared) + throws IOException; + + /** + * Tries to aqquire alock on the whole assoziated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public final FileLock tryLock() throws IOException + { + return tryLock(0, Long.MAX_VALUE, false); + } + + /** + * Tries to aqquire a lock on a region of the assoziated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public abstract FileLock tryLock(long position, long size, boolean shared) + throws IOException; + + /** + * Returns the current position on the file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + */ + public abstract long position() throws IOException; + + /** + * Sets the position of the channel on the assoziated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If newPosition is negative. + * @exception IOException If an I/O error occurs. + */ + public abstract FileChannel position(long newPosition) + throws IOException; + + /** + * Transfers bytes from this channel's file to the given writable byte + * channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If this channel was not opened for + * reading. + * @exception NonWritableChannelException If the target channel was not + * opened for writing. + */ + public abstract long transferTo(long position, long count, + WritableByteChannel target) + throws IOException; + + /** + * Transfers bytes from the given readable channel into this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If the source channel was not + * opened for reading. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract long transferFrom(ReadableByteChannel src, long position, + long count) throws IOException; + + /** + * Truncates the channel's file at <code>size</code>. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If size is negative. + * @exception IOException If an I/O error occurs. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract FileChannel truncate(long size) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/FileLock.java b/libjava/classpath/java/nio/channels/FileLock.java new file mode 100644 index 00000000000..151c23f9f6b --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileLock.java @@ -0,0 +1,150 @@ +/* FileLock.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +/** + * @since 1.4 + */ +public abstract class FileLock +{ + FileChannel channel; + long position; + long size; + boolean shared; + + /** + * Initializes the file lock. + * + * @exception IllegalArgumentException If the preconditions on the parameters do not hold + */ + protected FileLock(FileChannel channel, long position, long size, + boolean shared) + { + if (position < 0 || size < 0) + throw new IllegalArgumentException(); + + this.channel = channel; + this.position = position; + this.size = size; + this.shared = shared; + } + + /** + * Tells whether or not this lock is valid. + */ + public abstract boolean isValid(); + + /** + * Releases this lock. + * + * @exception IOException If an error occurs + * @exception ClosedChannelException If the locked channel is no longer open. + */ + public abstract void release() throws IOException; + + /** + * Returns the file channel upon whose file this lock is held. + */ + public final FileChannel channel() + { + return channel; + } + + /** + * Tells whether this lock is shared. + */ + public final boolean isShared() + { + return shared; + } + + /** + * Tells whether or not this lock overlaps the given lock range. + */ + public final boolean overlaps(long position, long size) + { + if (position > this.position + this.size) + return false; + + if (position + size < this.position) + return false; + + return true; + } + + /** + * Returns the position within the file of the first byte of the + * locked region. + */ + public final long position() + { + return position; + } + + /** + * Returns the size of the locked region in bytes. + */ + public final long size() + { + return size; + } + + /** + * Returns a string describing the range, type, and validity of this lock. + */ + public final String toString() + { + StringBuffer buf = new StringBuffer(getClass().getName()); + buf.append("["); + buf.append(position); + buf.append(":"); + buf.append(size); + if (shared) + buf.append(" shared"); + else + buf.append(" exclusive"); + if (isValid()) + buf.append(" valid]"); + else + buf.append(" invalid]"); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/nio/channels/FileLockInterruptionException.java b/libjava/classpath/java/nio/channels/FileLockInterruptionException.java new file mode 100644 index 00000000000..7d9e620464f --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileLockInterruptionException.java @@ -0,0 +1,55 @@ +/* FileLockInterruptionException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class FileLockInterruptionException extends IOException +{ + /** + * Creates the exception + */ + public FileLockInterruptionException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/GatheringByteChannel.java b/libjava/classpath/java/nio/channels/GatheringByteChannel.java new file mode 100644 index 00000000000..822ea232a5c --- /dev/null +++ b/libjava/classpath/java/nio/channels/GatheringByteChannel.java @@ -0,0 +1,79 @@ +/* GatheringByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface GatheringByteChannel extends WritableByteChannel +{ + /** + * Writes a sequence of bytes to this channel from a subsequence of + * the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Writes a sequence of bytes to this channel from the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + long write(ByteBuffer[] srcs) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java b/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java new file mode 100644 index 00000000000..7352b54f7f1 --- /dev/null +++ b/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java @@ -0,0 +1,57 @@ +/* IllegalBlockingModeException.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + * + * Written using JDK 1.4.1 Online API from Sun + * Status: JDK 1.4 complete + */ +public class IllegalBlockingModeException extends IllegalStateException +{ + /** + * Creates the exception + */ + public IllegalBlockingModeException() + { + super(); + } +} diff --git a/libjava/classpath/java/nio/channels/IllegalSelectorException.java b/libjava/classpath/java/nio/channels/IllegalSelectorException.java new file mode 100644 index 00000000000..049a8d594f4 --- /dev/null +++ b/libjava/classpath/java/nio/channels/IllegalSelectorException.java @@ -0,0 +1,53 @@ +/* IllegalSelectorException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class IllegalSelectorException extends IllegalArgumentException +{ + /** + * Creates the exception + */ + public IllegalSelectorException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/InterruptibleChannel.java b/libjava/classpath/java/nio/channels/InterruptibleChannel.java new file mode 100644 index 00000000000..54122ceadbc --- /dev/null +++ b/libjava/classpath/java/nio/channels/InterruptibleChannel.java @@ -0,0 +1,51 @@ +/* InterruptibleChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +public interface InterruptibleChannel extends Channel +{ + /** + * Closes this channel + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/NoConnectionPendingException.java b/libjava/classpath/java/nio/channels/NoConnectionPendingException.java new file mode 100644 index 00000000000..afefebf9807 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NoConnectionPendingException.java @@ -0,0 +1,53 @@ +/* NoConnectionPendingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NoConnectionPendingException extends IllegalStateException +{ + /** + * Creates the exception + */ + public NoConnectionPendingException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NonReadableChannelException.java b/libjava/classpath/java/nio/channels/NonReadableChannelException.java new file mode 100644 index 00000000000..e6852a73d3f --- /dev/null +++ b/libjava/classpath/java/nio/channels/NonReadableChannelException.java @@ -0,0 +1,53 @@ +/* NonReadableChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NonReadableChannelException extends IllegalStateException +{ + /** + * Creates the exception + */ + public NonReadableChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NonWritableChannelException.java b/libjava/classpath/java/nio/channels/NonWritableChannelException.java new file mode 100644 index 00000000000..61d40bbc4d6 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NonWritableChannelException.java @@ -0,0 +1,53 @@ +/* NonWritableChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NonWritableChannelException extends IllegalStateException +{ + /** + * Creates the exception + */ + public NonWritableChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NotYetBoundException.java b/libjava/classpath/java/nio/channels/NotYetBoundException.java new file mode 100644 index 00000000000..7d0c66388cd --- /dev/null +++ b/libjava/classpath/java/nio/channels/NotYetBoundException.java @@ -0,0 +1,53 @@ +/* NotYetBoundException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NotYetBoundException extends IllegalStateException +{ + /** + * Creates the exception + */ + public NotYetBoundException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NotYetConnectedException.java b/libjava/classpath/java/nio/channels/NotYetConnectedException.java new file mode 100644 index 00000000000..463e05934a8 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NotYetConnectedException.java @@ -0,0 +1,53 @@ +/* NotYetConnectedException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NotYetConnectedException extends IllegalStateException +{ + /** + * Creates the exception + */ + public NotYetConnectedException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/OverlappingFileLockException.java b/libjava/classpath/java/nio/channels/OverlappingFileLockException.java new file mode 100644 index 00000000000..ce0900c6a49 --- /dev/null +++ b/libjava/classpath/java/nio/channels/OverlappingFileLockException.java @@ -0,0 +1,53 @@ +/* OverlappingFileLockException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class OverlappingFileLockException extends IllegalStateException +{ + /** + * Creates the exception + */ + public OverlappingFileLockException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/Pipe.java b/libjava/classpath/java/nio/channels/Pipe.java new file mode 100644 index 00000000000..c7b04c88c8f --- /dev/null +++ b/libjava/classpath/java/nio/channels/Pipe.java @@ -0,0 +1,121 @@ +/* Pipe.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class Pipe +{ + public abstract static class SinkChannel extends AbstractSelectableChannel + implements WritableByteChannel, GatheringByteChannel + { + /** + * Initializes the channel. + */ + protected SinkChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Returns an operation set that is valid on this channel. + * + * The only valid operation on this channel is @see SelectionKey.OP_WRITE. + */ + public final int validOps() + { + return SelectionKey.OP_WRITE; + } + } + + public abstract static class SourceChannel extends AbstractSelectableChannel + implements ReadableByteChannel, ScatteringByteChannel + { + /** + * Initializes the channel. + */ + protected SourceChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Returns an operation set that is valid on this channel. + * + * The only valid operation on this channel is @see SelectionKey.OP_READ. + */ + public final int validOps() + { + return SelectionKey.OP_READ; + } + } + + /** + * Initializes the pipe. + */ + protected Pipe() + { + } + + /** + * Opens a pipe. + * + * @exception IOException If an error occurs + */ + public static Pipe open() throws IOException + { + return SelectorProvider.provider().openPipe(); + } + + /** + * Returns a pipe's sink channel. + */ + public abstract Pipe.SinkChannel sink(); + + /** + * Returns a pipe's source channel + */ + public abstract Pipe.SourceChannel source(); +} diff --git a/libjava/classpath/java/nio/channels/ReadableByteChannel.java b/libjava/classpath/java/nio/channels/ReadableByteChannel.java new file mode 100644 index 00000000000..889662de054 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ReadableByteChannel.java @@ -0,0 +1,64 @@ +/* ReadableByteChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface ReadableByteChannel extends Channel +{ + /** + * Reads a sequence of bytes from this channel into the given buffer + * + * @param dst the buffer to put the read data into + * + * @return the numer of bytes read + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the read operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + int read(ByteBuffer dst) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/ScatteringByteChannel.java b/libjava/classpath/java/nio/channels/ScatteringByteChannel.java new file mode 100644 index 00000000000..5437ea15818 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ScatteringByteChannel.java @@ -0,0 +1,79 @@ +/* ScatteringByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface ScatteringByteChannel extends ReadableByteChannel +{ + /** + * Reads a sequence of bytes from this channel into a subsequence of the + * given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + long read(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Reads a sequence of bytes from this channel into the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + long read(ByteBuffer[] srcs) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/SelectableChannel.java b/libjava/classpath/java/nio/channels/SelectableChannel.java new file mode 100644 index 00000000000..70fa785ce54 --- /dev/null +++ b/libjava/classpath/java/nio/channels/SelectableChannel.java @@ -0,0 +1,140 @@ +/* SelectableChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.AbstractInterruptibleChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectableChannel extends AbstractInterruptibleChannel +{ + /** + * Initializes the channel. + */ + protected SelectableChannel() + { + } + + /** + * Returns the lock of this channel. + */ + public abstract Object blockingLock(); + + /** + * Adjusts this channel's blocking mode. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IOException If an error occurs. + */ + public abstract SelectableChannel configureBlocking(boolean block) + throws IOException; + + /** + * Tells whether this channel is blocking or not. + */ + public abstract boolean isBlocking(); + + /** + * Tells whether or not this channel is currently registered with + * any selectors. + */ + public abstract boolean isRegistered(); + + /** + * Retrieves the key representing the channel's registration with + * the given selector. + */ + public abstract SelectionKey keyFor(Selector sel); + + /** + * Returns the provider that created this channel. + */ + public abstract SelectorProvider provider(); + + /** + * Registers this channel with the given selector, + * returning a selection key. + * + * @exception CancelledKeyException If this channel is currently registered + * with the given selector but the corresponding key has already been cancelled + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If a bit in ops does not correspond + * to an operation that is supported by this channel, that is, if + * set & ~validOps() != 0. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IllegalSelectorException If this channel was not created by + * the same provider as the given selector. + */ + public final SelectionKey register(Selector sel, int ops) + throws ClosedChannelException + { + return register(sel, ops, null); + } + + /** + * Registers this channel with the given selector, + * returning a selection key. + * + * @exception CancelledKeyException If this channel is currently registered + * with the given selector but the corresponding key has already been + * cancelled. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If a bit in ops does not correspond + * to an operation that is supported by this channel, that is, if + * set & ~validOps() != 0. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IllegalSelectorException If this channel was not created by + * the same provider as the given selector. + */ + public abstract SelectionKey register(Selector sel, int ops, Object att) + throws ClosedChannelException; + + /** + * Returns a set of valid operations on this channel. + */ + public abstract int validOps(); +} diff --git a/libjava/classpath/java/nio/channels/SelectionKey.java b/libjava/classpath/java/nio/channels/SelectionKey.java new file mode 100644 index 00000000000..5219b6bff84 --- /dev/null +++ b/libjava/classpath/java/nio/channels/SelectionKey.java @@ -0,0 +1,164 @@ +/* SelectionKey.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectionKey +{ + public static final int OP_ACCEPT = 16; + public static final int OP_CONNECT = 8; + public static final int OP_READ = 1; + public static final int OP_WRITE = 4; + Object attached; + + /** + * Initializes the selection key. + */ + protected SelectionKey() + { + } + + /** + * Attaches obj to the key and returns the old attached object. + */ + public final Object attach(Object obj) + { + Object old = attached; + attached = obj; + return old; + } + + /** + * Returns the object attached to the key. + */ + public final Object attachment() + { + return attached; + } + + /** + * Tests if the channel attached to this key is ready to accept + * a new socket connection. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isAcceptable() + { + return (readyOps() & OP_ACCEPT) != 0; + } + + /** + * Tests whether this key's channel has either finished, + * or failed to finish, its socket-connection operation. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isConnectable() + { + return (readyOps() & OP_CONNECT) != 0; + } + + /** + * Tests if the channel attached to the key is readable. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isReadable() + { + return (readyOps() & OP_READ) != 0; + } + + /** + * Tests if the channel attached to the key is writable. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isWritable() + { + return (readyOps() & OP_WRITE) != 0; + } + + /** + * Requests that the registration of this key's channel with + * its selector be cancelled. + */ + public abstract void cancel(); + + /** + * return the channel attached to the key. + */ + public abstract SelectableChannel channel(); + + /** + * Returns the key's interest set. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public abstract int interestOps(); + + /** + * Sets this key's interest set to the given value. + * + * @exception CancelledKeyException If this key has been cancelled + * @exception IllegalArgumentException If a bit in the set does not + * correspond to an operation that is supported by this key's channel, + * that is, if set & ~(channel().validOps()) != 0 + */ + public abstract SelectionKey interestOps(int ops); + + /** + * Tells whether or not this key is valid. + */ + public abstract boolean isValid(); + + /** + * Retrieves this key's ready-operation set. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public abstract int readyOps(); + + /** + * Returns the selector for which this key was created. + */ + public abstract Selector selector(); +} diff --git a/libjava/classpath/java/nio/channels/Selector.java b/libjava/classpath/java/nio/channels/Selector.java new file mode 100644 index 00000000000..2c883efd1a1 --- /dev/null +++ b/libjava/classpath/java/nio/channels/Selector.java @@ -0,0 +1,134 @@ +/* Selector.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.SelectorProvider; +import java.util.Set; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class Selector +{ + /** + * Initializes the selector. + */ + protected Selector() + { + } + + /** + * Opens a selector. + * + * @exception IOException If an error occurs + */ + public static Selector open() throws IOException + { + return SelectorProvider.provider().openSelector(); + } + + /** + * Closes the selector. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * Tells whether the selector is open or not. + */ + public abstract boolean isOpen(); + + /** + * Returns this selector's key set. + * + * @exception ClosedSelectorException If this selector is closed. + */ + public abstract Set keys(); + + /** + * Returns the SelectorProvider that created the selector. + */ + public abstract SelectorProvider provider(); + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IOException If an error occurs + */ + public abstract int select() throws IOException; + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @param timeout The timeout to use. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IllegalArgumentException If the timeout value is negative. + * @exception IOException If an error occurs + */ + public abstract int select(long timeout) throws IOException; + + /** + * Returns this selector's selected-key set. + * + * @exception ClosedSelectorException If this selector is closed. + */ + public abstract Set selectedKeys(); + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IOException If an error occurs + */ + public abstract int selectNow() throws IOException; + + /** + * Causes the first selection operation that has not yet returned to + * return immediately. + */ + public abstract Selector wakeup(); +} diff --git a/libjava/classpath/java/nio/channels/ServerSocketChannel.java b/libjava/classpath/java/nio/channels/ServerSocketChannel.java new file mode 100644 index 00000000000..105d17f94ec --- /dev/null +++ b/libjava/classpath/java/nio/channels/ServerSocketChannel.java @@ -0,0 +1,98 @@ +/* ServerSocketChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class ServerSocketChannel extends AbstractSelectableChannel +{ + /** + * Initializes this channel. + */ + protected ServerSocketChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Accepts a connection made to this channel's socket. + * + * @exception IOException If an error occurs + * @exception AsynchronousCloseException If another thread closes this + * channel while the accept operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the accept operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If the channel is closed. + * @exception NotYetBoundException If the channel's socket is not yet bound. + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the remote endpoint of the new connection. + */ + public abstract SocketChannel accept() throws IOException; + + /** + * Retrieves the channels socket. + */ + public abstract ServerSocket socket(); + + /** + * Opens a server socket channel. + * + * @exception IOException If an error occurs + */ + public static ServerSocketChannel open() throws IOException + { + return SelectorProvider.provider().openServerSocketChannel(); + } + + /** + * Retrieves the valid operations for this channel. + */ + public final int validOps() + { + return SelectionKey.OP_ACCEPT; + } +} diff --git a/libjava/classpath/java/nio/channels/SocketChannel.java b/libjava/classpath/java/nio/channels/SocketChannel.java new file mode 100644 index 00000000000..50f21368c88 --- /dev/null +++ b/libjava/classpath/java/nio/channels/SocketChannel.java @@ -0,0 +1,248 @@ +/* SocketChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public abstract class SocketChannel extends AbstractSelectableChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel +{ + /** + * Initializes this socket channel. + */ + protected SocketChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Opens a socket channel. + * + * @return the new <code>SocketChannel</code> object + * + * @exception IOException If an error occurs + */ + public static SocketChannel open() throws IOException + { + return SelectorProvider.provider().openSocketChannel(); + } + + /** + * Opens a channel and connects it to a remote address. + * + * @return the new <code>SocketChannel</code> object + * + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the given remote endpoint. + * @exception UnresolvedAddressException If the given remote address is not + * fully resolved. + * @exception UnsupportedAddressTypeException If the type of the given remote + * address is not supported. + */ + public static SocketChannel open(SocketAddress remote) + throws IOException + { + SocketChannel ch = open(); + ch.connect(remote); + return ch; + } + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += read(dsts[i]); + + return b; + } + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public final long write(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += write(dsts[i]); + + return b; + } + + /** + * Retrieves the valid operations for this channel. + * + * @return the valid operations + */ + public final int validOps() + { + return SelectionKey.OP_CONNECT | SelectionKey.OP_READ + | SelectionKey.OP_WRITE; + } + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Connects the channel's socket to the remote address. + * + * @return <code>true</code> if the channel got successfully connected, + * <code>false</code> if the channel is in non-blocking mode and connection + * operation is still in progress. + * + * @exception AlreadyConnectedException If this channel is already connected. + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception ConnectionPendingException If a non-blocking connection + * operation is already in progress on this channel. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the given remote endpoint. + * @exception UnresolvedAddressException If the given remote address is not + * fully resolved. + * @exception UnsupportedAddressTypeException If the type of the given remote + * address is not supported. + */ + public abstract boolean connect(SocketAddress remote) + throws IOException; + + /** + * Finishes the process of connecting a socket channel. + * + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception NoConnectionPendingException If this channel is not connected + * and a connection operation has not been initiated. + */ + public abstract boolean finishConnect() throws IOException; + + /** + * Tells whether or not the channel's socket is connected. + */ + public abstract boolean isConnected(); + + /** + * Tells whether or not a connection operation is in progress on this channel. + */ + public abstract boolean isConnectionPending(); + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Retrieves the channel's socket. + * + * @return the socket + */ + public abstract Socket socket(); + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/UnresolvedAddressException.java b/libjava/classpath/java/nio/channels/UnresolvedAddressException.java new file mode 100644 index 00000000000..4db95a7ffdd --- /dev/null +++ b/libjava/classpath/java/nio/channels/UnresolvedAddressException.java @@ -0,0 +1,53 @@ +/* UnresolvedAddressException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnresolvedAddressException extends IllegalArgumentException +{ + /** + * Creates the exception + */ + public UnresolvedAddressException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java b/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java new file mode 100644 index 00000000000..7c16c813fb0 --- /dev/null +++ b/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java @@ -0,0 +1,53 @@ +/* UnsupportedAddressTypeException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnsupportedAddressTypeException extends IllegalArgumentException +{ + /** + * Creates the exception + */ + public UnsupportedAddressTypeException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/WritableByteChannel.java b/libjava/classpath/java/nio/channels/WritableByteChannel.java new file mode 100644 index 00000000000..3845723bca0 --- /dev/null +++ b/libjava/classpath/java/nio/channels/WritableByteChannel.java @@ -0,0 +1,60 @@ +/* WritableByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface WritableByteChannel extends Channel +{ + /** + * Writes a sequence of bytes to this channel from the given buffer + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + int write(ByteBuffer src) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/package.html b/libjava/classpath/java/nio/channels/package.html new file mode 100644 index 00000000000..4e2bbc9f6ce --- /dev/null +++ b/libjava/classpath/java/nio/channels/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.nio.channels package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.nio.channels</title></head> + +<body> +<p>Classes for creating, selecting and non-blocking reading and writing of +buffers from files and sockets.</p> + +</body> +</html> diff --git a/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java b/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java new file mode 100644 index 00000000000..25e8785c08e --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java @@ -0,0 +1,119 @@ +/* AbstractInterruptibleChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.Channel; +import java.nio.channels.InterruptibleChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class AbstractInterruptibleChannel + implements Channel, InterruptibleChannel +{ + private boolean closed; + + /** + * Initializes the channel. + */ + protected AbstractInterruptibleChannel() + { + } + + /** + * Marks the beginning of an I/O operation that might block indefinitely. + */ + protected final void begin() + { + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + public final void close() throws IOException + { + if (! closed) + { + closed = true; + implCloseChannel(); + } + } + + /** + * Marks the end of an I/O operation that might block indefinitely. + * + * @param completed true if the task completed successfully, + * false otherwise + * + * @exception IOException if an error occurs + * @exception AsynchronousCloseException If the channel was asynchronously + * closed. + * @exception ClosedByInterruptException If the thread blocked in the + * I/O operation was interrupted. + */ + protected final void end(boolean completed) + throws AsynchronousCloseException + { + // FIXME: check more here. + + if (closed) throw new AsynchronousCloseException(); + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + protected abstract void implCloseChannel() throws IOException; + + /** + * Tells whether or not this channel is open. + * + * @return true if the channel is open, false otherwise + */ + public final boolean isOpen() + { + return ! closed; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java b/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java new file mode 100644 index 00000000000..42ceab7e2c1 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java @@ -0,0 +1,256 @@ +/* AbstractSelectableChannel.java + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.LinkedList; +import java.util.ListIterator; + +public abstract class AbstractSelectableChannel extends SelectableChannel +{ + private boolean blocking = true; + private Object LOCK = new Object(); + private SelectorProvider provider; + private LinkedList keys = new LinkedList(); + + /** + * Initializes the channel + * + * @param provider the provider that created this channel + */ + protected AbstractSelectableChannel(SelectorProvider provider) + { + this.provider = provider; + } + + /** + * Retrieves the object upon which the configureBlocking and register + * methods synchronize. + * + * @return the blocking lock + */ + public final Object blockingLock() + { + return LOCK; + } + + /** + * Adjusts this channel's blocking mode. + * + * @param blocking true if blocking should be enabled, false otherwise + * + * @return this channel + * + * @exception IOException If an error occurs + */ + public final SelectableChannel configureBlocking(boolean blocking) + throws IOException + { + synchronized (blockingLock()) + { + if (this.blocking != blocking) + { + implConfigureBlocking(blocking); + this.blocking = blocking; + } + } + + return this; + } + + /** + * Closes this channel. + * + * @exception IOException If an error occurs + */ + protected final void implCloseChannel() throws IOException + { + implCloseSelectableChannel(); + } + + /** + * Closes this selectable channel. + * + * @exception IOException If an error occurs + */ + protected abstract void implCloseSelectableChannel() + throws IOException; + + /** + * Adjusts this channel's blocking mode. + * + * @param blocking true if blocking should be enabled, false otherwise + * + * @exception IOException If an error occurs + */ + protected abstract void implConfigureBlocking(boolean blocking) + throws IOException; + + /** + * Tells whether or not every I/O operation on this channel will block + * until it completes. + * + * @return true of this channel is blocking, false otherwise + */ + public final boolean isBlocking() + { + return blocking; + } + + /** + * Tells whether or not this channel is currently registered with + * any selectors. + * + * @return true if this channel is registered, false otherwise + */ + public final boolean isRegistered() + { + return ! keys.isEmpty(); + } + + /** + * Retrieves the key representing the channel's registration with the + * given selector. + * + * @param selector the selector to get a selection key for + * + * @return the selection key this channel is registered with + */ + public final SelectionKey keyFor(Selector selector) + { + if (! isOpen()) + return null; + + try + { + synchronized (blockingLock()) + { + return locate(selector); + } + } + catch (Exception e) + { + return null; + } + } + + /** + * Returns the provider that created this channel. + * + * @return the selector provider that created this channel + */ + public final SelectorProvider provider() + { + return provider; + } + + private SelectionKey locate(Selector selector) + { + ListIterator it = keys.listIterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (key.selector() == selector) + return key; + } + + return null; + } + + /** + * Registers this channel with the given selector, returning a selection key. + * + * @param selin the seletor to use + * @param ops the interested operations + * @param att an attachment for the returned selection key + * + * @return the registered selection key + * + * @exception ClosedChannelException If the channel is already closed. + */ + public final SelectionKey register(Selector selin, int ops, Object att) + throws ClosedChannelException + { + if (! isOpen()) + throw new ClosedChannelException(); + + if ((ops & ~validOps()) != 0) + throw new IllegalArgumentException(); + + SelectionKey key = null; + AbstractSelector selector = (AbstractSelector) selin; + + synchronized (blockingLock()) + { + key = locate(selector); + + if (key != null && key.isValid()) + { + if (att != null) + key.attach(att); + } + else + { + key = selector.register(this, ops, att); + + if (key != null) + addSelectionKey(key); + } + } + + return key; + } + + void addSelectionKey(SelectionKey key) + { + keys.add(key); + } + + // This method gets called by AbstractSelector.deregister(). + void removeSelectionKey(SelectionKey key) + { + keys.remove(key); + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java new file mode 100644 index 00000000000..5ab8468bf2e --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java @@ -0,0 +1,78 @@ +/* AbstractSelectionKey.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.nio.channels.SelectionKey; + + +/** + * @since 1.4 + */ +public abstract class AbstractSelectionKey extends SelectionKey +{ + private boolean cancelled; + + /** + * Initializes the key. + */ + protected AbstractSelectionKey() + { + } + + /** + * Cancels this key. + */ + public final void cancel() + { + if (isValid()) + { + ((AbstractSelector) selector()).cancelKey(this); + cancelled = true; + } + } + + /** + * Tells whether this key is valid or not. + * + * @return true if this key is valid, false otherwise + */ + public final boolean isValid() + { + return ! cancelled; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelector.java b/libjava/classpath/java/nio/channels/spi/AbstractSelector.java new file mode 100644 index 00000000000..78380738a2c --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelector.java @@ -0,0 +1,167 @@ +/* AbstractSelector.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.HashSet; +import java.util.Set; + + +public abstract class AbstractSelector extends Selector +{ + private boolean closed; + private SelectorProvider provider; + private HashSet cancelledKeys; + + /** + * Initializes the slector. + * + * @param provider the provider that created this selector + */ + protected AbstractSelector(SelectorProvider provider) + { + this.provider = provider; + this.cancelledKeys = new HashSet(); + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + public final synchronized void close() throws IOException + { + if (closed) + return; + + implCloseSelector(); + closed = true; + } + + /** + * Tells whether this channel is open or not. + * + * @return true if channel is open, false otherwise. + */ + public final boolean isOpen() + { + return ! closed; + } + + /** + * Marks the beginning of an I/O operation that might block indefinitely. + */ + protected final void begin() + { + } + + /** + * Marks the end of an I/O operation that might block indefinitely. + */ + protected final void end() + { + } + + /** + * Returns the provider for this selector object. + * + * @return the SelectorProvider object that created this seletor + */ + public final SelectorProvider provider() + { + return provider; + } + + /** + * Returns the cancelled keys set. + * + * @return the cancelled keys set + */ + protected final Set cancelledKeys() + { + if (! isOpen()) + throw new ClosedSelectorException(); + + return cancelledKeys; + } + + /** + * Cancels a selection key. + */ + + // This method is only called by AbstractSelectionKey.cancel(). + final void cancelKey(AbstractSelectionKey key) + { + synchronized (cancelledKeys) + { + cancelledKeys.add(key); + } + } + + /** + * Closes the channel. + * + * @exception IOException if an error occurs + */ + protected abstract void implCloseSelector() throws IOException; + + /** + * Registers a channel for the selection process. + * + * @param ch the channel register + * @param ops the interested operations + * @param att an attachement to the selection key + * + * @return the registered selection key + */ + protected abstract SelectionKey register(AbstractSelectableChannel ch, + int ops, Object att); + + /** + * Deregisters the given selection key. + * + * @param key the key to deregister + */ + protected final void deregister(AbstractSelectionKey key) + { + ((AbstractSelectableChannel) key.channel()).removeSelectionKey(key); + } +} diff --git a/libjava/classpath/java/nio/channels/spi/SelectorProvider.java b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java new file mode 100644 index 00000000000..db4e65fe14e --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java @@ -0,0 +1,151 @@ +/* SelectorProvider.java + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import gnu.java.nio.SelectorProviderImpl; + +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.Pipe; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectorProvider +{ + private static SelectorProvider systemDefaultProvider; + + /** + * Initializes the selector provider. + * + * @exception SecurityException If a security manager has been installed and + * it denies @see RuntimePermission ("selectorProvider"). + */ + protected SelectorProvider() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("selectorProvider")); + } + + /** + * Opens a datagram channel. + * + * @return a new datagram channel object + * + * @exception IOException if an error occurs + */ + public abstract DatagramChannel openDatagramChannel() + throws IOException; + + /** + * Opens a pipe. + * + * @return a new pipe object + * + * @exception IOException if an error occurs + */ + public abstract Pipe openPipe() throws IOException; + + /** + * Opens a selector. + * + * @return a new selector object + * + * @exception IOException if an error occurs + */ + public abstract AbstractSelector openSelector() throws IOException; + + /** + * Opens a server socket channel. + * + * @return a new server socket channel object + * + * @exception IOException if an error occurs + */ + public abstract ServerSocketChannel openServerSocketChannel() + throws IOException; + + /** + * Opens a socket channel. + * + * @return a new socket channel object + * + * @exception IOException if an error occurs + */ + public abstract SocketChannel openSocketChannel() throws IOException; + + /** + * Returns the system-wide default selector provider for this invocation + * of the Java virtual machine. + * + * @return the default seletor provider + */ + public static synchronized SelectorProvider provider() + { + if (systemDefaultProvider == null) + { + String propertyValue = + System.getProperty("java.nio.channels.spi.SelectorProvider"); + + if (propertyValue == null || propertyValue.equals("")) + systemDefaultProvider = new SelectorProviderImpl(); + else + { + try + { + systemDefaultProvider = + (SelectorProvider) Class.forName(propertyValue) + .newInstance(); + } + catch (Exception e) + { + System.err.println("Could not instantiate class: " + + propertyValue); + systemDefaultProvider = new SelectorProviderImpl(); + } + } + } + + return systemDefaultProvider; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/package.html b/libjava/classpath/java/nio/channels/spi/package.html new file mode 100644 index 00000000000..133fb04f2d7 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.nio.channels.spi package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.nio.channels.spi</title></head> + +<body> +<p>Utility classes and interfaces for implementing channels.</p> + +</body> +</html> diff --git a/libjava/classpath/java/nio/charset/CharacterCodingException.java b/libjava/classpath/java/nio/charset/CharacterCodingException.java new file mode 100644 index 00000000000..6812ebb18a9 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharacterCodingException.java @@ -0,0 +1,53 @@ +/* CharacterCodingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.io.IOException; + +/** + * @since 1.4 + */ +public class CharacterCodingException extends IOException +{ + /** + * Creates the exception + */ + public CharacterCodingException() + { + } +} diff --git a/libjava/classpath/java/nio/charset/Charset.java b/libjava/classpath/java/nio/charset/Charset.java new file mode 100644 index 00000000000..6de2917f7cb --- /dev/null +++ b/libjava/classpath/java/nio/charset/Charset.java @@ -0,0 +1,406 @@ +/* Charset.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.charset; + +import gnu.classpath.SystemProperties; + +import gnu.java.nio.charset.Provider; +import gnu.java.nio.charset.iconv.IconvProvider; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.spi.CharsetProvider; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author Jesse Rosenstock + * @since 1.4 + * @status updated to 1.5 + */ +public abstract class Charset implements Comparable +{ + private CharsetEncoder cachedEncoder; + private CharsetDecoder cachedDecoder; + + /** + * Charset providers. + */ + private static CharsetProvider[] providers; + + private final String canonicalName; + private final String[] aliases; + + protected Charset (String canonicalName, String[] aliases) + { + checkName (canonicalName); + if (aliases != null) + { + int n = aliases.length; + for (int i = 0; i < n; ++i) + checkName (aliases[i]); + } + + cachedEncoder = null; + cachedDecoder = null; + this.canonicalName = canonicalName; + this.aliases = aliases; + } + + /** + * @throws IllegalCharsetNameException if the name is illegal + */ + private static void checkName (String name) + { + int n = name.length (); + + if (n == 0) + throw new IllegalCharsetNameException (name); + + char ch = name.charAt (0); + if (!(('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9'))) + throw new IllegalCharsetNameException (name); + + for (int i = 1; i < n; ++i) + { + ch = name.charAt (i); + if (!(('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9') + || ch == '-' || ch == '.' || ch == ':' || ch == '_')) + throw new IllegalCharsetNameException (name); + } + } + + /** + * Returns the system default charset. + * + * This may be set by the user or VM with the file.encoding + * property. + */ + public static Charset defaultCharset() + { + String encoding; + + try + { + encoding = SystemProperties.getProperty("file.encoding"); + } + catch(SecurityException e) + { + // Use fallback. + encoding = "ISO-8859-1"; + } + catch(IllegalArgumentException e) + { + // Use fallback. + encoding = "ISO-8859-1"; + } + + try + { + return forName(encoding); + } + catch(UnsupportedCharsetException e) + { + // Ignore. + } + catch(IllegalCharsetNameException e) + { + // Ignore. + } + catch(IllegalArgumentException e) + { + // Ignore. + } + + throw new IllegalStateException("Can't get default charset!"); + } + + public static boolean isSupported (String charsetName) + { + return charsetForName (charsetName) != null; + } + + /** + * Returns the Charset instance for the charset of the given name. + * + * @param charsetName + * @return + * @throws UnsupportedCharsetException if this VM does not support + * the charset of the given name. + * @throws IllegalCharsetNameException if the given charset name is + * legal. + * @throws IllegalArgumentException if <code>charsetName</code> is null. + */ + public static Charset forName (String charsetName) + { + // Throws IllegalArgumentException as the JDK does. + if(charsetName == null) + throw new IllegalArgumentException("Charset name must not be null."); + + Charset cs = charsetForName (charsetName); + if (cs == null) + throw new UnsupportedCharsetException (charsetName); + return cs; + } + + /** + * Retrieves a charset for the given charset name. + * + * @return A charset object for the charset with the specified name, or + * <code>null</code> if no such charset exists. + * + * @throws IllegalCharsetNameException if the name is illegal + */ + private static Charset charsetForName(String charsetName) + { + checkName (charsetName); + Charset cs = null; + CharsetProvider[] providers = providers2(); + for (int i = 0; i < providers.length; i++) + { + cs = providers[i].charsetForName(charsetName); + if (cs != null) + break; + } + return cs; + } + + public static SortedMap availableCharsets() + { + TreeMap charsets = new TreeMap(String.CASE_INSENSITIVE_ORDER); + + CharsetProvider[] providers = providers2(); + for (int j = 0; j < providers.length; j++) + { + for (Iterator i = providers[j].charsets(); i.hasNext(); ) + { + Charset cs = (Charset) i.next(); + charsets.put(cs.name(), cs); + } + } + + return Collections.unmodifiableSortedMap(charsets); + } + + private static CharsetProvider provider() + { + String useIconv = SystemProperties.getProperty + ("gnu.classpath.nio.charset.provider.iconv"); + + if (useIconv != null) + return IconvProvider.provider(); + + return Provider.provider(); + } + + /** + * We need to support multiple providers, reading them from + * java.nio.charset.spi.CharsetProvider in the resource directory + * META-INF/services. + */ + private static CharsetProvider[] providers2() + { + if (providers == null) + { + try + { + Enumeration en = ClassLoader.getSystemResources + ("META-INF/services/java.nio.charset.spi.CharsetProvider"); + LinkedHashSet set = new LinkedHashSet(); + set.add(provider()); + while (en.hasMoreElements()) + { + BufferedReader rdr = new BufferedReader(new InputStreamReader + (((URL) (en.nextElement())).openStream())); + while (true) + { + String s = rdr.readLine(); + if (s == null) + break; + CharsetProvider p = + (CharsetProvider) ((Class.forName(s)).newInstance()); + set.add(p); + } + } + + providers = new CharsetProvider[set.size()]; + set.toArray(providers); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + return providers; + } + + public final String name () + { + return canonicalName; + } + + public final Set aliases () + { + if (aliases == null) + return Collections.EMPTY_SET; + + // should we cache the aliasSet instead? + int n = aliases.length; + HashSet aliasSet = new HashSet (n); + for (int i = 0; i < n; ++i) + aliasSet.add (aliases[i]); + return Collections.unmodifiableSet (aliasSet); + } + + public String displayName () + { + return canonicalName; + } + + public String displayName (Locale locale) + { + return canonicalName; + } + + public final boolean isRegistered () + { + return (!canonicalName.startsWith ("x-") + && !canonicalName.startsWith ("X-")); + } + + public abstract boolean contains (Charset cs); + + public abstract CharsetDecoder newDecoder (); + + public abstract CharsetEncoder newEncoder (); + + public boolean canEncode () + { + return true; + } + + // NB: This implementation serializes different threads calling + // Charset.encode(), a potential performance problem. It might + // be better to remove the cache, or use ThreadLocal to cache on + // a per-thread basis. + public final synchronized ByteBuffer encode (CharBuffer cb) + { + try + { + if (cachedEncoder == null) + { + cachedEncoder = newEncoder () + .onMalformedInput (CodingErrorAction.REPLACE) + .onUnmappableCharacter (CodingErrorAction.REPLACE); + } else + cachedEncoder.reset(); + return cachedEncoder.encode (cb); + } + catch (CharacterCodingException e) + { + throw new AssertionError (e); + } + } + + public final ByteBuffer encode (String str) + { + return encode (CharBuffer.wrap (str)); + } + + // NB: This implementation serializes different threads calling + // Charset.decode(), a potential performance problem. It might + // be better to remove the cache, or use ThreadLocal to cache on + // a per-thread basis. + public final synchronized CharBuffer decode (ByteBuffer bb) + { + try + { + if (cachedDecoder == null) + { + cachedDecoder = newDecoder () + .onMalformedInput (CodingErrorAction.REPLACE) + .onUnmappableCharacter (CodingErrorAction.REPLACE); + } else + cachedDecoder.reset(); + + return cachedDecoder.decode (bb); + } + catch (CharacterCodingException e) + { + throw new AssertionError (e); + } + } + + public final int compareTo (Object ob) + { + return canonicalName.compareToIgnoreCase (((Charset) ob).canonicalName); + } + + public final int hashCode () + { + return canonicalName.hashCode (); + } + + public final boolean equals (Object ob) + { + if (ob instanceof Charset) + return canonicalName.equalsIgnoreCase (((Charset) ob).canonicalName); + else + return false; + } + + public final String toString () + { + return canonicalName; + } +} diff --git a/libjava/classpath/java/nio/charset/CharsetDecoder.java b/libjava/classpath/java/nio/charset/CharsetDecoder.java new file mode 100644 index 00000000000..c86f1c8a4ad --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharsetDecoder.java @@ -0,0 +1,317 @@ +/* CharsetDecoder.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public abstract class CharsetDecoder +{ + private static final int STATE_RESET = 0; + private static final int STATE_CODING = 1; + private static final int STATE_END = 2; + private static final int STATE_FLUSHED = 3; + + private static final String DEFAULT_REPLACEMENT = "\uFFFD"; + + private final Charset charset; + private final float averageCharsPerByte; + private final float maxCharsPerByte; + private String replacement; + + private int state = STATE_RESET; + + private CodingErrorAction malformedInputAction + = CodingErrorAction.REPORT; + private CodingErrorAction unmappableCharacterAction + = CodingErrorAction.REPORT; + + private CharsetDecoder (Charset cs, float averageCharsPerByte, + float maxCharsPerByte, String replacement) + { + if (averageCharsPerByte <= 0.0f) + throw new IllegalArgumentException ("Non-positive averageCharsPerByte"); + if (maxCharsPerByte <= 0.0f) + throw new IllegalArgumentException ("Non-positive maxCharsPerByte"); + + this.charset = cs; + this.averageCharsPerByte + = averageCharsPerByte; + this.maxCharsPerByte + = maxCharsPerByte; + this.replacement = replacement; + implReplaceWith (replacement); + } + + protected CharsetDecoder (Charset cs, float averageCharsPerByte, + float maxCharsPerByte) + { + this (cs, averageCharsPerByte, maxCharsPerByte, DEFAULT_REPLACEMENT); + } + + public final float averageCharsPerByte () + { + return averageCharsPerByte; + } + + public final Charset charset () + { + return charset; + } + + public final CharBuffer decode (ByteBuffer in) + throws CharacterCodingException + { + // XXX: Sun's Javadoc seems to contradict itself saying an + // IllegalStateException is thrown "if a decoding operation is already + // in progress" and also that "it resets this Decoder". + // Should we check to see that the state is reset, or should we + // call reset()? + if (state != STATE_RESET) + throw new IllegalStateException (); + + // REVIEW: Using max instead of average may allocate a very large + // buffer. Maybe we should do something more efficient? + int remaining = in.remaining (); + int n = (int) (remaining * maxCharsPerByte ()); + CharBuffer out = CharBuffer.allocate (n); + + if (remaining == 0) + { + state = STATE_FLUSHED; + return out; + } + + CoderResult cr = decode (in, out, true); + if (cr.isError ()) + cr.throwException (); + + cr = flush (out); + if (cr.isError ()) + cr.throwException (); + + reset(); + out.flip (); + + // Unfortunately, resizing the actual charbuffer array is required. + char[] resized = new char[out.remaining()]; + out.get(resized); + return CharBuffer.wrap(resized); + } + + public final CoderResult decode (ByteBuffer in, CharBuffer out, + boolean endOfInput) + { + int newState = endOfInput ? STATE_END : STATE_CODING; + // XXX: Need to check for "previous step was an invocation [not] of + // this method with a value of true for the endOfInput parameter but + // a return value indicating an incomplete decoding operation" + // XXX: We will not check the previous return value, just + // that the previous call passed true for endOfInput + if (state != STATE_RESET && state != STATE_CODING + && !(endOfInput && state == STATE_END)) + throw new IllegalStateException (); + state = newState; + + for (;;) + { + CoderResult cr; + try + { + cr = decodeLoop (in, out); + } + catch (RuntimeException e) + { + throw new CoderMalfunctionError (e); + } + + if (cr.isOverflow ()) + return cr; + + if (cr.isUnderflow ()) + { + if (endOfInput && in.hasRemaining ()) + cr = CoderResult.malformedForLength (in.remaining ()); + else + return cr; + } + + CodingErrorAction action = cr.isMalformed () + ? malformedInputAction + : unmappableCharacterAction; + + if (action == CodingErrorAction.REPORT) + return cr; + + if (action == CodingErrorAction.REPLACE) + { + if (out.remaining () < replacement.length ()) + return CoderResult.OVERFLOW; + out.put (replacement); + } + + in.position (in.position () + cr.length ()); + } + } + + protected abstract CoderResult decodeLoop (ByteBuffer in, CharBuffer out); + + public Charset detectedCharset () + { + throw new UnsupportedOperationException (); + } + + public final CoderResult flush (CharBuffer out) + { + // It seems weird that you can flush after reset, but Sun's javadoc + // says an IllegalStateException is thrown "If the previous step of the + // current decoding operation was an invocation neither of the reset + // method nor ... of the three-argument decode method with a value of + // true for the endOfInput parameter." + // Further note that flush() only requires that there not be + // an IllegalStateException if the previous step was a call to + // decode with true as the last argument. It does not require + // that the call succeeded. decode() does require that it succeeded. + // XXX: test this to see if reality matches javadoc + if (state != STATE_RESET && state != STATE_END) + throw new IllegalStateException (); + + state = STATE_FLUSHED; + return implFlush (out); + } + + protected CoderResult implFlush (CharBuffer out) + { + return CoderResult.UNDERFLOW; + } + + public final CharsetDecoder onMalformedInput (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + malformedInputAction = newAction; + implOnMalformedInput (newAction); + return this; + } + + protected void implOnMalformedInput (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implOnUnmappableCharacter (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implReplaceWith (String newReplacement) + { + // default implementation does nothing + } + + protected void implReset () + { + // default implementation does nothing + } + + public boolean isAutoDetecting () + { + return false; + } + + public boolean isCharsetDetected () + { + throw new UnsupportedOperationException (); + } + + public CodingErrorAction malformedInputAction () + { + return malformedInputAction; + } + + public final float maxCharsPerByte () + { + return maxCharsPerByte; + } + + public final CharsetDecoder onUnmappableCharacter + (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + unmappableCharacterAction = newAction; + implOnUnmappableCharacter (newAction); + return this; + } + + public final String replacement () + { + return replacement; + } + + public final CharsetDecoder replaceWith (String newReplacement) + { + if (newReplacement == null) + throw new IllegalArgumentException ("Null replacement"); + if (newReplacement.length () == 0) + throw new IllegalArgumentException ("Empty replacement"); + // XXX: what about maxCharsPerByte? + + this.replacement = newReplacement; + implReplaceWith (newReplacement); + return this; + } + + public final CharsetDecoder reset () + { + state = STATE_RESET; + implReset (); + return this; + } + + public CodingErrorAction unmappableCharacterAction () + { + return unmappableCharacterAction; + } +} diff --git a/libjava/classpath/java/nio/charset/CharsetEncoder.java b/libjava/classpath/java/nio/charset/CharsetEncoder.java new file mode 100644 index 00000000000..932fe355df9 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharsetEncoder.java @@ -0,0 +1,369 @@ +/* CharsetEncoder.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public abstract class CharsetEncoder +{ + private static final int STATE_RESET = 0; + private static final int STATE_CODING = 1; + private static final int STATE_END = 2; + private static final int STATE_FLUSHED = 3; + + private static final byte[] DEFAULT_REPLACEMENT = {(byte)'?'}; + + private final Charset charset; + private final float averageBytesPerChar; + private final float maxBytesPerChar; + private byte[] replacement; + + private int state = STATE_RESET; + + private CodingErrorAction malformedInputAction + = CodingErrorAction.REPORT; + private CodingErrorAction unmappableCharacterAction + = CodingErrorAction.REPORT; + + protected CharsetEncoder (Charset cs, float averageBytesPerChar, + float maxBytesPerChar) + { + this (cs, averageBytesPerChar, maxBytesPerChar, DEFAULT_REPLACEMENT); + } + + protected CharsetEncoder (Charset cs, float averageBytesPerChar, + float maxBytesPerChar, byte[] replacement) + { + if (averageBytesPerChar <= 0.0f) + throw new IllegalArgumentException ("Non-positive averageBytesPerChar"); + if (maxBytesPerChar <= 0.0f) + throw new IllegalArgumentException ("Non-positive maxBytesPerChar"); + + this.charset = cs; + this.averageBytesPerChar + = averageBytesPerChar; + this.maxBytesPerChar + = maxBytesPerChar; + this.replacement = replacement; + implReplaceWith (replacement); + } + + public final float averageBytesPerChar () + { + return averageBytesPerChar; + } + + public boolean canEncode (char c) + { + CharBuffer cb = CharBuffer.allocate (1).put (c); + cb.flip (); + return canEncode (cb); + } + + public boolean canEncode (CharSequence cs) + { + CharBuffer cb; + if (cs instanceof CharBuffer) + cb = ((CharBuffer) cs).duplicate (); + else + cb = CharBuffer.wrap (cs); + return canEncode (cb); + } + + private boolean canEncode (CharBuffer cb) + { + // It is an error if a coding operation is "in progress" + // I take that to mean the state is not reset or flushed. + // XXX: check "in progress" everywhere + if (state == STATE_FLUSHED) + reset (); + else if (state != STATE_RESET) + throw new IllegalStateException (); + + CodingErrorAction oldMalformedInputAction = malformedInputAction; + CodingErrorAction oldUnmappableCharacterAction + = unmappableCharacterAction; + + try + { + if (oldMalformedInputAction != CodingErrorAction.REPORT) + onMalformedInput (CodingErrorAction.REPORT); + if (oldUnmappableCharacterAction != CodingErrorAction.REPORT) + onUnmappableCharacter (CodingErrorAction.REPORT); + } + catch (Exception e) + { + return false; + } + finally + { + if (oldMalformedInputAction != CodingErrorAction.REPORT) + onMalformedInput (oldMalformedInputAction); + if (oldUnmappableCharacterAction != CodingErrorAction.REPORT) + onUnmappableCharacter (oldUnmappableCharacterAction); + } + + return true; + } + + public final Charset charset () + { + return charset; + } + + public final ByteBuffer encode (CharBuffer in) + throws CharacterCodingException + { + // XXX: Sun's Javadoc seems to contradict itself saying an + // IllegalStateException is thrown "if a decoding operation is already + // in progress" and also that "it resets this Encoder". + // Should we check to see that the state is reset, or should we + // call reset()? + if (state != STATE_RESET) + throw new IllegalStateException (); + + // REVIEW: Using max instead of average may allocate a very large + // buffer. Maybe we should do something more efficient? + int remaining = in.remaining (); + int n = (int) (remaining * maxBytesPerChar ()); + ByteBuffer out = ByteBuffer.allocate (n); + + if (remaining == 0) + { + state = STATE_FLUSHED; + return out; + } + + CoderResult cr = encode (in, out, true); + if (cr.isError ()) + cr.throwException (); + + cr = flush (out); + if (cr.isError ()) + cr.throwException (); + + out.flip (); + + // Unfortunately, resizing the actual bytebuffer array is required. + byte[] resized = new byte[out.remaining()]; + out.get(resized); + return ByteBuffer.wrap(resized); + } + + public final CoderResult encode (CharBuffer in, ByteBuffer out, + boolean endOfInput) + { + int newState = endOfInput ? STATE_END : STATE_CODING; + // XXX: Need to check for "previous step was an invocation [not] of + // this method with a value of true for the endOfInput parameter but + // a return value indicating an incomplete decoding operation" + // XXX: We will not check the previous return value, just + // that the previous call passed true for endOfInput + if (state != STATE_RESET && state != STATE_CODING + && !(endOfInput && state == STATE_END)) + throw new IllegalStateException (); + state = newState; + + for (;;) + { + CoderResult cr; + try + { + cr = encodeLoop (in, out); + } + catch (RuntimeException e) + { + throw new CoderMalfunctionError (e); + } + + if (cr.isOverflow ()) + return cr; + + if (cr.isUnderflow ()) + { + if (endOfInput && in.hasRemaining ()) + cr = CoderResult.malformedForLength (in.remaining ()); + else + return cr; + } + + CodingErrorAction action = cr.isMalformed () + ? malformedInputAction + : unmappableCharacterAction; + + if (action == CodingErrorAction.REPORT) + return cr; + + if (action == CodingErrorAction.REPLACE) + { + if (out.remaining () < replacement.length) + return CoderResult.OVERFLOW; + out.put (replacement); + } + + in.position (in.position () + cr.length ()); + } + } + + protected abstract CoderResult encodeLoop (CharBuffer in, ByteBuffer out); + + public final CoderResult flush (ByteBuffer out) + { + // It seems weird that you can flush after reset, but Sun's javadoc + // says an IllegalStateException is thrown "If the previous step of the + // current decoding operation was an invocation neither of the reset + // method nor ... of the three-argument encode method with a value of + // true for the endOfInput parameter." + // Further note that flush() only requires that there not be + // an IllegalStateException if the previous step was a call to + // encode with true as the last argument. It does not require + // that the call succeeded. encode() does require that it succeeded. + // XXX: test this to see if reality matches javadoc + if (state != STATE_RESET && state != STATE_END) + throw new IllegalStateException (); + + state = STATE_FLUSHED; + return implFlush (out); + } + + protected CoderResult implFlush (ByteBuffer out) + { + return CoderResult.UNDERFLOW; + } + + protected void implOnMalformedInput (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implOnUnmappableCharacter (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implReplaceWith (byte[] newReplacement) + { + // default implementation does nothing + } + + protected void implReset () + { + // default implementation does nothing + } + + public boolean isLegalReplacement (byte[] replacement) + { + // TODO: cache the decoder + // error actions will be REPORT after construction + CharsetDecoder decoder = charset.newDecoder (); + ByteBuffer bb = ByteBuffer.wrap (replacement); + CharBuffer cb + = CharBuffer.allocate ((int) (replacement.length + * decoder.maxCharsPerByte ())); + return !decoder.decode (bb, cb, true).isError (); + } + + public CodingErrorAction malformedInputAction () + { + return malformedInputAction; + } + + public final float maxBytesPerChar () + { + return maxBytesPerChar; + } + + public final CharsetEncoder onMalformedInput (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + malformedInputAction = newAction; + implOnMalformedInput (newAction); + return this; + } + + public CodingErrorAction unmappableCharacterAction () + { + return unmappableCharacterAction; + } + + public final CharsetEncoder onUnmappableCharacter + (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + unmappableCharacterAction = newAction; + implOnUnmappableCharacter (newAction); + return this; + } + + public final byte[] replacement () + { + return replacement; + } + + public final CharsetEncoder replaceWith (byte[] newReplacement) + { + if (newReplacement == null) + throw new IllegalArgumentException ("Null replacement"); + if (newReplacement.length == 0) + throw new IllegalArgumentException ("Empty replacement"); + // XXX: what about maxBytesPerChar? + + if (!isLegalReplacement (newReplacement)) + throw new IllegalArgumentException ("Illegal replacement"); + + this.replacement = newReplacement; + implReplaceWith (newReplacement); + return this; + } + + public final CharsetEncoder reset () + { + state = STATE_RESET; + implReset (); + return this; + } +} diff --git a/libjava/classpath/java/nio/charset/CoderMalfunctionError.java b/libjava/classpath/java/nio/charset/CoderMalfunctionError.java new file mode 100644 index 00000000000..08294cafb20 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CoderMalfunctionError.java @@ -0,0 +1,52 @@ +/* CoderMalfunctionError.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class CoderMalfunctionError extends Error +{ + /** + * Creates the error + */ + public CoderMalfunctionError(Exception cause) + { + super (cause); + } +} diff --git a/libjava/classpath/java/nio/charset/CoderResult.java b/libjava/classpath/java/nio/charset/CoderResult.java new file mode 100644 index 00000000000..664215d9a68 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CoderResult.java @@ -0,0 +1,189 @@ +/* CoderResult.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.lang.ref.WeakReference; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.util.HashMap; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public class CoderResult +{ + private static final int TYPE_MALFORMED = 0; + private static final int TYPE_OVERFLOW = 1; + private static final int TYPE_UNDERFLOW = 2; + private static final int TYPE_UNMAPPABLE = 3; + + public static final CoderResult OVERFLOW + = new CoderResult (TYPE_OVERFLOW, 0); + public static final CoderResult UNDERFLOW + = new CoderResult (TYPE_UNDERFLOW, 0); + + private static final String[] names + = { "MALFORMED", "OVERFLOW", "UNDERFLOW", "UNMAPPABLE" }; + + private static final Cache malformedCache + = new Cache () + { + protected CoderResult make (int length) + { + return new CoderResult (TYPE_MALFORMED, length); + } + }; + + private static final Cache unmappableCache + = new Cache () + { + protected CoderResult make (int length) + { + return new CoderResult (TYPE_UNMAPPABLE, length); + } + }; + + private final int type; + private final int length; + + // Package-private to avoid a trampoline constructor. + CoderResult (int type, int length) + { + this.type = type; + this.length = length; + } + + public boolean isError () + { + return length > 0; + } + + public boolean isMalformed () + { + return type == TYPE_MALFORMED; + } + + public boolean isOverflow () + { + return type == TYPE_OVERFLOW; + } + + public boolean isUnderflow () + { + return type == TYPE_UNDERFLOW; + } + + public boolean isUnmappable () + { + return type == TYPE_UNMAPPABLE; + } + + public int length () + { + if (length <= 0) + throw new UnsupportedOperationException (); + else + return length; + } + + public static CoderResult malformedForLength (int length) + { + return malformedCache.get (length); + } + + public void throwException () + throws CharacterCodingException + { + switch (type) + { + case TYPE_MALFORMED: + throw new MalformedInputException (length); + case TYPE_OVERFLOW: + throw new BufferOverflowException (); + case TYPE_UNDERFLOW: + throw new BufferUnderflowException (); + case TYPE_UNMAPPABLE: + throw new UnmappableCharacterException (length); + } + } + + public String toString () + { + String name = names[type]; + return (length > 0) ? name + '[' + length + ']' : name; + } + + public static CoderResult unmappableForLength (int length) + { + return unmappableCache.get (length); + } + + private abstract static class Cache + { + private final HashMap cache; + + // Package-private to avoid a trampoline constructor. + Cache () + { + cache = new HashMap (); + } + + // Package-private to avoid a trampoline. + synchronized CoderResult get (int length) + { + if (length <= 0) + throw new IllegalArgumentException ("Non-positive length"); + + Integer len = new Integer (length); + CoderResult cr = null; + Object o; + if ((o = cache.get (len)) != null) + cr = (CoderResult) ((WeakReference) o).get (); + if (cr == null) + { + cr = make (length); + cache.put (len, new WeakReference (cr)); + } + + return cr; + } + + protected abstract CoderResult make (int length); + } +} diff --git a/libjava/classpath/java/nio/charset/CodingErrorAction.java b/libjava/classpath/java/nio/charset/CodingErrorAction.java new file mode 100644 index 00000000000..592c15934ac --- /dev/null +++ b/libjava/classpath/java/nio/charset/CodingErrorAction.java @@ -0,0 +1,66 @@ +/* CodingErrorAction.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +public class CodingErrorAction +{ + public static final CodingErrorAction IGNORE + = new CodingErrorAction("ignore"); + public static final CodingErrorAction REPLACE + = new CodingErrorAction("replace"); + public static final CodingErrorAction REPORT + = new CodingErrorAction("report"); + + private final String name; + + /** + * Private constructor only used to create the constant CodingErrorActions. + */ + private CodingErrorAction(String name) + { + this.name = name; + } + + /** + * Returns the name of the CodingErrorAction. + */ + public String toString () + { + return name; + } +} diff --git a/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java b/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java new file mode 100644 index 00000000000..7baeac3234e --- /dev/null +++ b/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java @@ -0,0 +1,73 @@ +/* IllegalCharsetNameException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class IllegalCharsetNameException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 1457525358470002989L; + + private String charsetName; + + /** + * Creates the exception + * + * @param charsetName name of the illegal charset + */ + public IllegalCharsetNameException (String charsetName) + { + super (); + this.charsetName = charsetName; + } + + /** + * Retrieves the illegal charset name + * + * @return the illegal charset name + */ + public String getCharsetName () + { + return charsetName; + } +} diff --git a/libjava/classpath/java/nio/charset/MalformedInputException.java b/libjava/classpath/java/nio/charset/MalformedInputException.java new file mode 100644 index 00000000000..0beceb40cb3 --- /dev/null +++ b/libjava/classpath/java/nio/charset/MalformedInputException.java @@ -0,0 +1,77 @@ +/* MalformedInputException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class MalformedInputException extends CharacterCodingException +{ + private int inputLength; + + /** + * Creates the exception + * + * @param inputLength the position of malformed input in the input stream + */ + public MalformedInputException (int inputLength) + { + super (); + this.inputLength = inputLength; + } + + /** + * Retrieves the position of the malformed input in the input stream. + * + * @return the position + */ + public int getInputLength () + { + return inputLength; + } + + /** + * Returns the detail message string of this throwable + * + * @return the message + */ + public String getMessage () + { + return "Input length = " + inputLength; + } +} diff --git a/libjava/classpath/java/nio/charset/UnmappableCharacterException.java b/libjava/classpath/java/nio/charset/UnmappableCharacterException.java new file mode 100644 index 00000000000..71906510849 --- /dev/null +++ b/libjava/classpath/java/nio/charset/UnmappableCharacterException.java @@ -0,0 +1,71 @@ +/* UnmappableCharacterException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class UnmappableCharacterException extends CharacterCodingException +{ + private int inputLength; + + /** + * Creates the exception + */ + public UnmappableCharacterException (int inputLength) + { + super (); + this.inputLength = inputLength; + } + + /** + * Retrieves the illegal charset name + */ + public int getInputLength () + { + return inputLength; + } + + /** + * Returns the detail message string of this throwable + */ + public String getMessage () + { + return "Input length = " + inputLength; + } +} diff --git a/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java b/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java new file mode 100644 index 00000000000..39536baa563 --- /dev/null +++ b/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java @@ -0,0 +1,69 @@ +/* UnsupportedCharsetException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnsupportedCharsetException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 1490765524727386367L; + + String charsetName; + + /** + * Creates the exception + */ + public UnsupportedCharsetException (String charsetName) + { + super (); + this.charsetName = charsetName; + } + + /** + * Retrieves the illegal charset name + */ + public String getCharsetName () + { + return charsetName; + } +} diff --git a/libjava/classpath/java/nio/charset/package.html b/libjava/classpath/java/nio/charset/package.html new file mode 100644 index 00000000000..acc7c020333 --- /dev/null +++ b/libjava/classpath/java/nio/charset/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.nio.charset package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.nio.charset</title></head> + +<body> +<p>Classes to manipulate, encode and decode different character sets from +and to bytes.</p> + +</body> +</html> diff --git a/libjava/classpath/java/nio/charset/spi/CharsetProvider.java b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java new file mode 100644 index 00000000000..f0d40ab8591 --- /dev/null +++ b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java @@ -0,0 +1,91 @@ +/* CharsetProvider.java -- charset service provider interface + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset.spi; + +import java.nio.charset.Charset; +import java.util.Iterator; + + +/** + * This class allows an implementor to provide additional character sets. The + * subclass must have a nullary constructor, and be attached to charset + * implementation classes. These extensions are loaded via the context class + * loader. To provide the charset extension, all files named + * <code>META-INF/services/java.nio.charset.spi.CharsetProvider</code> are + * read from the classpath. Each one should be a UTF-8 encoded list of + * fully-qualified names of concrete subclasses of this class; whitespace is + * ignored, and '#' starts comments. Duplicates are ignored. The + * implementations must be accessible to the classloader that requests them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Charset + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class CharsetProvider +{ + /** + * Initialize a new charset provider. This performs a security check on + * RuntimePermission("charsetProvider"). + * + * @throws SecurityException if building a new set is not allowed + */ + protected CharsetProvider() + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new RuntimePermission("charsetProvider")); + } + + /** + * Returns an iterator over the charsets defined by this provider. + * + * @return the iterator + * @see Charset#availableCharsets() + */ + public abstract Iterator charsets(); + + /** + * Returns the named charset, by canonical name or alias. + * + * @param name the name of the character + * + * @return the charset, or null if not supported + */ + public abstract Charset charsetForName(String name); +} // class CharsetProvider diff --git a/libjava/classpath/java/nio/charset/spi/package.html b/libjava/classpath/java/nio/charset/spi/package.html new file mode 100644 index 00000000000..dd47a250be7 --- /dev/null +++ b/libjava/classpath/java/nio/charset/spi/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.nio.charset.spi package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.nio.charset.spi</title></head> + +<body> +<p>Classes to provide new character sets.</p> + +</body> +</html> diff --git a/libjava/classpath/java/nio/class-dependencies.conf b/libjava/classpath/java/nio/class-dependencies.conf new file mode 100644 index 00000000000..4fbf75eb1ce --- /dev/null +++ b/libjava/classpath/java/nio/class-dependencies.conf @@ -0,0 +1,58 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +# end of file diff --git a/libjava/classpath/java/nio/package.html b/libjava/classpath/java/nio/package.html new file mode 100644 index 00000000000..274498be3ff --- /dev/null +++ b/libjava/classpath/java/nio/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.nio package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.nio</title></head> + +<body> +<p>Abstract classes for manipulating buffers of different primitive types.</p> + +</body> +</html> diff --git a/libjava/classpath/java/rmi/AccessException.java b/libjava/classpath/java/rmi/AccessException.java new file mode 100644 index 00000000000..b470780046c --- /dev/null +++ b/libjava/classpath/java/rmi/AccessException.java @@ -0,0 +1,76 @@ +/* AccessException.java -- thrown if the caller does not have access + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown to indicate that the caller does not have permission to access + * certain data, such as <code>bind</code> in an ActivationSystem. + * + * @author unknown + * @see Naming + * @see ActivationSystem + * @since 1.1 + */ +public class AccessException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6314925228044966088l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public AccessException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public AccessException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/AlreadyBoundException.java b/libjava/classpath/java/rmi/AlreadyBoundException.java new file mode 100644 index 00000000000..091c0ee5862 --- /dev/null +++ b/libjava/classpath/java/rmi/AlreadyBoundException.java @@ -0,0 +1,73 @@ +/* AlreadyBoundException.java -- thrown if a binding is already bound + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to bind an object in the registry that is already + * bound. + * + * @author unknown + * @see Naming#bind(String, Remote) + * @see Registry#bind(String, Remote) + * @since 1.1 + * @status updated to 1.4 + */ +public class AlreadyBoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 9218657361741657110L; + + /** + * Create an exception with no message. + */ + public AlreadyBoundException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public AlreadyBoundException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/ConnectException.java b/libjava/classpath/java/rmi/ConnectException.java new file mode 100644 index 00000000000..dc3abd19741 --- /dev/null +++ b/libjava/classpath/java/rmi/ConnectException.java @@ -0,0 +1,74 @@ +/* ConnectException.java -- thrown if a connection is refused + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a connection is refused for a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4863550261346652506L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ConnectException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ConnectException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ConnectIOException.java b/libjava/classpath/java/rmi/ConnectIOException.java new file mode 100644 index 00000000000..dde753ad558 --- /dev/null +++ b/libjava/classpath/java/rmi/ConnectIOException.java @@ -0,0 +1,74 @@ +/* ConnectIOException.java -- thrown if an IO exception occurs during connect + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps an I/O Exception thrown while connecting for a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectIOException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8087809532704668744L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ConnectIOException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ConnectIOException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/MarshalException.java b/libjava/classpath/java/rmi/MarshalException.java new file mode 100644 index 00000000000..e5c10a4bf79 --- /dev/null +++ b/libjava/classpath/java/rmi/MarshalException.java @@ -0,0 +1,76 @@ +/* MarshalException.java -- wraps error while marshalling parameters + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an exception occurs while marshalling data to send in a remote + * call. The call may not be retransmitted, if the "at most once" semantics + * are to be preserved. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class MarshalException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6223554758134037936L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public MarshalException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public MarshalException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/MarshalledObject.java b/libjava/classpath/java/rmi/MarshalledObject.java new file mode 100644 index 00000000000..9ec0ace0e70 --- /dev/null +++ b/libjava/classpath/java/rmi/MarshalledObject.java @@ -0,0 +1,113 @@ +/* MarshalledObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi; + +import gnu.java.rmi.RMIMarshalledObjectInputStream; +import gnu.java.rmi.RMIMarshalledObjectOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.Serializable; + +/** + * FIXME - doc missing + */ +public final class MarshalledObject implements Serializable +{ + //The following fields are from Java API Documentation "Serialized form" + private static final long serialVersionUID = 8988374069173025854L; + byte[] objBytes; + byte[] locBytes; + int hash; + + public MarshalledObject(Object obj) throws java.io.IOException + { + ByteArrayOutputStream objStream = new ByteArrayOutputStream(); + RMIMarshalledObjectOutputStream stream = new RMIMarshalledObjectOutputStream(objStream); + stream.writeObject(obj); + stream.flush(); + objBytes = objStream.toByteArray(); + locBytes = stream.getLocBytes(); + + //The following algorithm of calculating hashCode is similar to String + hash = 0; + for (int i = 0; i < objBytes.length; i++) + hash = hash * 31 + objBytes[i]; + if(locBytes != null) + for (int i = 0; i < locBytes.length; i++) + hash = hash * 31 + locBytes[i]; + } + + public boolean equals(Object obj) + { + if (! (obj instanceof MarshalledObject)) + return false; + + // hashCode even differs, don't do the time-consuming comparisons + if (obj.hashCode() != hash) + return false; + + MarshalledObject aobj = (MarshalledObject)obj; + if (objBytes == null || aobj.objBytes == null) + return objBytes == aobj.objBytes; + if (objBytes.length != aobj.objBytes.length) + return false; + for (int i = 0; i < objBytes.length; i++) + { + if (objBytes[i] != aobj.objBytes[i]) + return false; + } + // Ignore comparison of locBytes(annotation) + return true; + } + +public Object get() + throws java.io.IOException, java.lang.ClassNotFoundException +{ + if(objBytes == null) + return null; + RMIMarshalledObjectInputStream stream = + new RMIMarshalledObjectInputStream(objBytes, locBytes); + return stream.readObject(); +} + + public int hashCode() { + return hash; + } + +} diff --git a/libjava/classpath/java/rmi/Naming.java b/libjava/classpath/java/rmi/Naming.java new file mode 100644 index 00000000000..d48df069d8d --- /dev/null +++ b/libjava/classpath/java/rmi/Naming.java @@ -0,0 +1,220 @@ +/* Naming.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * <p> + * The <code>Naming</code> class handles interactions with RMI registries. + * Each method takes a URL in <code>String</code> form, which points to + * the RMI registry. The scheme of the URL is irrelevant. The relevant + * part is: + * </p> + * <p> + * <code>//host:port/name</code> + * </p> + * <p> + * which tells the method how to locate and access the registry. The host + * and port are both optional, and default to `localhost' and the standard + * RMI registry port (1099) respectively. The name is simply a string + * used to refer to a particular service hosted by the registry. The + * registry does not attempt to interpret this further. + * </p> + * <p> + * RMI services are registered using one of these names, and the same name + * is later used by the client to lookup the service and access its methods. + * Registries can be shared by multiple services, or a service can create + * its own registry using <code>createRegistry()</code>. + * </p> + * + * @author Original author unknown. + * @author Ingo Proetel (proetel@aicas.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + */ +public final class Naming { + + /** + * This class isn't intended to be instantiated. + */ + private Naming() {} + +/** + * Looks for the remote object that is associated with the named service. + * Name and location is given in form of a URL without a scheme: + * + * <pre> + * //host:port/service-name + * </pre> + * + * The port is optional. + * + * @param name the service name and location + * @return Remote-object that implements the named service + * @throws NotBoundException if no object implements the service + * @throws MalformedURLException + * @throws RemoteException + */ +public static Remote lookup(String name) throws NotBoundException, MalformedURLException, RemoteException { + URL u = parseURL(name); + String serviceName = getName(u); + return (getRegistry(u).lookup(serviceName)); +} + +/** + * Try to bind the given object to the given service name. + * @param name + * @param obj + * @throws AlreadyBoundException + * @throws MalformedURLException + * @throws RemoteException + */ +public static void bind(String name, Remote obj) throws AlreadyBoundException, MalformedURLException, RemoteException { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).bind(serviceName, obj); +} + +/** + * Remove a binding for a given service name. + * @param name + * @throws RemoteException + * @throws NotBoundException + * @throws MalformedURLException + */ +public static void unbind(String name) throws RemoteException, NotBoundException, MalformedURLException { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).unbind(serviceName); +} + +/** + * Forces the binding between the given Remote-object and the given service name, even + * if there was already an object bound to this name. + * @param name + * @param obj + * @throws RemoteException + * @throws MalformedURLException + */ +public static void rebind(String name, Remote obj) throws RemoteException, MalformedURLException { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).rebind(serviceName, obj); +} + +/** + * Lists all services at the named registry. + * @param name url that specifies the registry + * @return list of services at the name registry + * @throws RemoteException + * @throws MalformedURLException + */ +public static String[] list(String name) throws RemoteException, MalformedURLException { + return (getRegistry(parseURL(name)).list()); +} + +private static Registry getRegistry(URL u) throws RemoteException { + if (u.getPort() == -1) { + return (LocateRegistry.getRegistry(u.getHost())); + } + else { + return (LocateRegistry.getRegistry(u.getHost(), u.getPort())); + } +} + + /** + * Parses the supplied URL and converts it to use the HTTP + * protocol. From an RMI perspective, the scheme is irrelevant + * and we want to be able to create a URL for which a handler is + * available. + * + * @param name the URL in String form. + * @throws MalformedURLException if the URL is invalid. + */ + private static URL parseURL(String name) + throws MalformedURLException + { + try + { + URI uri = new URI(name); + String host = uri.getHost(); + int port = uri.getPort(); + String query = uri.getQuery(); + String path = uri.getPath(); + return new URL("http", + (host == null ? "localhost" : host), + (port == -1 ? 1099 : port), + uri.getPath() + (query == null ? "" : query)); + } + catch (URISyntaxException e) + { + throw new MalformedURLException("The URL syntax was invalid: " + + e.getMessage()); + } + } + + /** + * Checks that the URL contains a name, and removes any leading + * slashes. + * + * @param url the URL to check. + * @throws MalformedURLException if no name is specified. + */ + private static String getName(URL url) + throws MalformedURLException + { + String filename = url.getFile(); + if (filename.length() == 0) + throw new MalformedURLException("No path specified: " + url); + // If the filename begins with a slash we must cut it for + // name resolution. + if (filename.charAt(0) == '/') + return filename.substring(1); + return filename; + } + +} diff --git a/libjava/classpath/java/rmi/NoSuchObjectException.java b/libjava/classpath/java/rmi/NoSuchObjectException.java new file mode 100644 index 00000000000..69f7d6c52fb --- /dev/null +++ b/libjava/classpath/java/rmi/NoSuchObjectException.java @@ -0,0 +1,68 @@ +/* NoSuchObjectException.java -- thrown if the remote object no longer exists + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to invoke a call on an object that no longer exists + * in the remote Virtual Machine. The call may be retransmitted and still + * obey the semantics of "at most once". + * + * @author unknown + * @see RemoteObject#toStub(Remote) + * @see UnicastRemoteObject#unexportObject(Remote, boolean) + * @see Activatable#unexportObject(Remote, boolean) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoSuchObjectException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6619395951570472985L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchObjectException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/NotBoundException.java b/libjava/classpath/java/rmi/NotBoundException.java new file mode 100644 index 00000000000..b8bc0a5320a --- /dev/null +++ b/libjava/classpath/java/rmi/NotBoundException.java @@ -0,0 +1,75 @@ +/* NotBoundException.java -- attempt to use a registry name with no binding + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to lookup or unbind a registry name that has no + * associated binding. + * + * @author unknown + * @see Naming#lookup(String) + * @see Naming#unbind(String) + * @see Registry#lookup(String) + * @see Registry#unbind(String) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotBoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1857741824849069317l; + + /** + * Create an exception with no message. + */ + public NotBoundException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NotBoundException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/RMISecurityException.java b/libjava/classpath/java/rmi/RMISecurityException.java new file mode 100644 index 00000000000..a44a67e2a02 --- /dev/null +++ b/libjava/classpath/java/rmi/RMISecurityException.java @@ -0,0 +1,77 @@ +/* RMISecurityException.java -- deprecated version of SecurityException + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Never thrown, but originally intended to wrap a java.lang.SecurityException. + * + * @author unknown + * @since 1.1 + * @deprecated use {@link SecurityException} instead + * @status updated to 1.4 + */ +public class RMISecurityException extends SecurityException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -8433406075740433514L; + + /** + * Create an exception with a message. + * + * @param s the message + * @deprecated no longer needed + */ + public RMISecurityException(String n) + { + super(n); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + * @deprecated no longer needed + */ + public RMISecurityException(String n, String a) + { + super(n); + } +} diff --git a/libjava/classpath/java/rmi/RMISecurityManager.java b/libjava/classpath/java/rmi/RMISecurityManager.java new file mode 100644 index 00000000000..a8eb13e1a0a --- /dev/null +++ b/libjava/classpath/java/rmi/RMISecurityManager.java @@ -0,0 +1,48 @@ +/* RMISecurityManager.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * @since 1.1 + */ +public class RMISecurityManager extends SecurityManager +{ + public RMISecurityManager() + { + } +} diff --git a/libjava/classpath/java/rmi/Remote.java b/libjava/classpath/java/rmi/Remote.java new file mode 100644 index 00000000000..93c8d0aa127 --- /dev/null +++ b/libjava/classpath/java/rmi/Remote.java @@ -0,0 +1,41 @@ +/* Remote.java + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +public interface Remote { +} diff --git a/libjava/classpath/java/rmi/RemoteException.java b/libjava/classpath/java/rmi/RemoteException.java new file mode 100644 index 00000000000..cbbb26299c3 --- /dev/null +++ b/libjava/classpath/java/rmi/RemoteException.java @@ -0,0 +1,127 @@ +/* RemoteException.java -- common superclass for exceptions in java.rmi + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +import java.io.IOException; + +/** + * The superclass of exceptions related to RMI (remote method invocation). + * Classes that implement <code>java.rmi.Remote</code> should list this + * exception in their throws clause. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class RemoteException extends IOException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -5148567311918794206l; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Throwable detail; + + /** + * Create an exception with no message, and cause initialized to null. + */ + public RemoteException() + { + this(null, null); + } + + /** + * Create an exception with the given message, and cause initialized to null. + * + * @param s the message + */ + public RemoteException(String s) + { + this(s, null); + } + + /** + * Create an exception with the given message and cause. + * + * @param s the message + * @param ex the cause + */ + public RemoteException(String s, Throwable e) + { + super(s); + initCause(e); + detail = e; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * <code>super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail)</code>. + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the <code>detail</code> field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if <code>detail == this</code>, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/ServerError.java b/libjava/classpath/java/rmi/ServerError.java new file mode 100644 index 00000000000..b1a15b02e74 --- /dev/null +++ b/libjava/classpath/java/rmi/ServerError.java @@ -0,0 +1,64 @@ +/* ServerError.java -- wraps an error while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any error thrown while processing the server of a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerError extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8455284893909696482L; + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerError(String s, Error e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ServerException.java b/libjava/classpath/java/rmi/ServerException.java new file mode 100644 index 00000000000..5170aa7b0b5 --- /dev/null +++ b/libjava/classpath/java/rmi/ServerException.java @@ -0,0 +1,74 @@ +/* ServerException.java -- wraps an exception while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any exception thrown while processing the server of a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4775845313121906682l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ServerRuntimeException.java b/libjava/classpath/java/rmi/ServerRuntimeException.java new file mode 100644 index 00000000000..1f0813739a6 --- /dev/null +++ b/libjava/classpath/java/rmi/ServerRuntimeException.java @@ -0,0 +1,67 @@ +/* ServerRuntimeException.java -- wraps an exception while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any runtime exception thrown while processing the server of a + * remote call. Note, this exception is no longer used. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement + * @status updated to 1.4 + */ +public class ServerRuntimeException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = 7054464920481467219L; + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + * @deprecated no longer needed + */ + public ServerRuntimeException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/StubNotFoundException.java b/libjava/classpath/java/rmi/StubNotFoundException.java new file mode 100644 index 00000000000..2f9e0f5ff59 --- /dev/null +++ b/libjava/classpath/java/rmi/StubNotFoundException.java @@ -0,0 +1,77 @@ +/* StubNotFoundException.java -- thrown if a valid stub is not found + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a valid stub class is not found for an object when it is exported. + * + * @author unknown + * @see UnicastRemoteObject + * @see Activatable + * @since 1.1 + * @status updated to 1.4 + */ +public class StubNotFoundException extends RemoteException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7088199405468872373L; + + + /** + * Create an exception with a message. + * + * @param s the message + */ + public StubNotFoundException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public StubNotFoundException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnexpectedException.java b/libjava/classpath/java/rmi/UnexpectedException.java new file mode 100644 index 00000000000..e9e0d6a9d48 --- /dev/null +++ b/libjava/classpath/java/rmi/UnexpectedException.java @@ -0,0 +1,75 @@ +/* UnexpectedException.java -- an unexpected checked exception was received + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an unexpected checked exception was received in a remote + * procedure call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnexpectedException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 1800467484195073863L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnexpectedException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnexpectedException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnknownHostException.java b/libjava/classpath/java/rmi/UnknownHostException.java new file mode 100644 index 00000000000..e21b9944c6b --- /dev/null +++ b/libjava/classpath/java/rmi/UnknownHostException.java @@ -0,0 +1,75 @@ +/* UnknownHostException.java -- wraps java.net.UnknownHostException in RMI + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a java.net.UnknownHostException occurs during a remote + * procedure call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnknownHostException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8152710247442114228L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnknownHostException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnknownHostException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnmarshalException.java b/libjava/classpath/java/rmi/UnmarshalException.java new file mode 100644 index 00000000000..6567062a523 --- /dev/null +++ b/libjava/classpath/java/rmi/UnmarshalException.java @@ -0,0 +1,88 @@ +/* UnmarshalException.java -- wraps error while unmarshalling parameters + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an exception occurs while unmarshalling parameters or results + * of a remote method call. This includes:<br><ul> + * <li>if an exception occurs while unmarshalling the call header</li> + * <li>if the protocol for the return value is invalid</li> + * <li>if a java.io.IOException occurs unmarshalling parameters (on the + * server side) or the return value (on the client side).</li> + * <li>if a java.lang.ClassNotFoundException occurs during unmarshalling + * parameters or return values</li> + * <li>if no skeleton can be loaded on the server-side; note that skeletons + * are required in the 1.1 stub protocol, but not in the 1.2 stub + * protocol.</li> + * <li>if the method hash is invalid (i.e., missing method).</li> + * <li>if there is a failure to create a remote reference object for a remote + * object's stub when it is unmarshalled.</li> + * </ul> + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnmarshalException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 594380845140740218l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnmarshalException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnmarshalException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/activation/Activatable.java b/libjava/classpath/java/rmi/activation/Activatable.java new file mode 100644 index 00000000000..b4c38bf61a8 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/Activatable.java @@ -0,0 +1,105 @@ +/* Activatable.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RemoteServer; + +public abstract class Activatable extends RemoteServer +{ +static final long serialVersionUID = -3120617863591563455L; + +protected Activatable(String location, MarshalledObject data, boolean restart, int port) throws ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +protected Activatable(String location, MarshalledObject data, boolean restart, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +protected Activatable(ActivationID id, int port) throws RemoteException { + throw new Error("Not implemented"); +} + +protected Activatable(ActivationID id, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { + throw new Error("Not implemented"); +} + +protected ActivationID getID() { + throw new Error("Not implemented"); +} + +public static Remote register(ActivationDesc desc) throws UnknownGroupException, ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +public static boolean inactive(ActivationID id) throws UnknownObjectException, ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +public static void unregister(ActivationID id) throws UnknownObjectException, ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +public static ActivationID exportObject(Remote obj, String location, MarshalledObject data, boolean restart, int port) throws ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +public static ActivationID exportObject(Remote obj, String location, MarshalledObject data, boolean restart, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws ActivationException, RemoteException { + throw new Error("Not implemented"); +} + +public static Remote exportObject(Remote obj, ActivationID id, int port) throws RemoteException { + throw new Error("Not implemented"); +} + +public static Remote exportObject(Remote obj, ActivationID id, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { + throw new Error("Not implemented"); +} + +public static boolean unexportObject(Remote obj, boolean force) throws NoSuchObjectException { + throw new Error("Not implemented"); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivateFailedException.java b/libjava/classpath/java/rmi/activation/ActivateFailedException.java new file mode 100644 index 00000000000..1c2e10ee3e6 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivateFailedException.java @@ -0,0 +1,76 @@ +/* ActivateFailedException.java -- thrown when activation fails + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.rmi.RemoteException; + +/** + * Thrown when activation fails on a remote call to an activatable object. + * + * @author unknown + * @since 1.2 + * @status updated to 1.4 + */ +public class ActivateFailedException extends RemoteException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4863550261346652506L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ActivateFailedException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param ex the cause + */ + public ActivateFailedException(String s, Exception ex) + { + super(s, ex); + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationDesc.java b/libjava/classpath/java/rmi/activation/ActivationDesc.java new file mode 100644 index 00000000000..65894f808d2 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationDesc.java @@ -0,0 +1,113 @@ +/* ActivationDecc.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.io.Serializable; +import java.rmi.MarshalledObject; + +public final class ActivationDesc implements Serializable +{ + static final long serialVersionUID = 7455834104417690957L; + +private ActivationGroupID groupid; +private String classname; +private String location; +private MarshalledObject data; +private boolean restart; + +public ActivationDesc(String className, String location, MarshalledObject data) throws ActivationException { + this(ActivationGroup.currentGroupID(), className, location, data, false); +} + +public ActivationDesc(String className, String location, MarshalledObject data, boolean restart) throws ActivationException { + this(ActivationGroup.currentGroupID(), className, location, data, restart); +} + +public ActivationDesc(ActivationGroupID groupID, String className, String location, MarshalledObject data) { + this(groupID, className, location, data, false); +} + +public ActivationDesc(ActivationGroupID groupID, String className, String location, MarshalledObject data, boolean restart) { + this.groupid = groupID; + this.classname = className; + this.location = location; + this.data = data; + this.restart = restart; +} + +public ActivationGroupID getGroupID() { + return (groupid); +} + +public String getClassName() { + return (classname); +} + +public String getLocation() { + return (location); +} + +public MarshalledObject getData() { + return (data); +} + +public boolean getRestartMode() { + return (restart); +} + +public boolean equals(Object obj) { + if (!(obj instanceof ActivationDesc)) { + return (false); + } + ActivationDesc that = (ActivationDesc)obj; + + if (this.groupid.equals(that.groupid) && + this.classname.equals(that.classname) && + this.location.equals(that.location) && + this.data.equals(that.data) && + this.restart == that.restart) { + return (true); + } + return (false); +} + +public int hashCode() { + return (groupid.hashCode() ^ classname.hashCode() ^ location.hashCode() ^ data.hashCode()); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationException.java b/libjava/classpath/java/rmi/activation/ActivationException.java new file mode 100644 index 00000000000..418f4385757 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationException.java @@ -0,0 +1,122 @@ +/* ActivationException.java -- general Activation exception + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * General exception class for <code>java.rmi.activation</code>. + * + * @author unknown + * @since 1.2 + * @status updated to 1.4 + */ +public class ActivationException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -4320118837291406071L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Throwable detail; + + /** + * Create an exception with no message, and cause initialized to null. + */ + public ActivationException() + { + this(null, null); + } + + /** + * Create an exception with the given message, and cause initialized to null. + * + * @param s the message + */ + public ActivationException(String s) + { + this(s, null); + } + + /** + * Create an exception with the given message and cause. + * + * @param s the message + * @param ex the cause + */ + public ActivationException(String s, Throwable ex) + { + super(s, ex); + detail = ex; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * <code>super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail)</code>. + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the <code>detail</code> field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if <code>detail == this</code>, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroup.java b/libjava/classpath/java/rmi/activation/ActivationGroup.java new file mode 100644 index 00000000000..e5774a1b961 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroup.java @@ -0,0 +1,85 @@ +/* ActivationGroup.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; + +public abstract class ActivationGroup extends UnicastRemoteObject + implements ActivationInstantiator +{ +static final long serialVersionUID = -7696947875314805420L; + +protected ActivationGroup(ActivationGroupID groupID) throws RemoteException { + throw new Error("Not implemented"); +} + +public boolean inactiveObject(ActivationID id) throws ActivationException, UnknownObjectException, RemoteException { + throw new Error("Not implemented"); +} + +public abstract void activeObject(ActivationID id, Remote obj) throws ActivationException, UnknownObjectException, RemoteException; + +public static ActivationGroup createGroup(ActivationGroupID id, ActivationGroupDesc desc, long incarnation) throws ActivationException { + throw new Error("Not implemented"); +} + +public static ActivationGroupID currentGroupID() { + throw new Error("Not implemented"); +} + +public static void setSystem(ActivationSystem system) throws ActivationException { + throw new Error("Not implemented"); +} + +public static ActivationSystem getSystem() throws ActivationException { + throw new Error("Not implemented"); +} + +protected void activeObject(ActivationID id, MarshalledObject mobj) throws ActivationException, UnknownObjectException, RemoteException { + throw new Error("Not implemented"); +} + +protected void inactiveGroup() throws UnknownGroupException, RemoteException { + throw new Error("Not implemented"); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java b/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java new file mode 100644 index 00000000000..35b546e3294 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java @@ -0,0 +1,134 @@ +/* ActivationGroupDesc.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.io.Serializable; +import java.rmi.MarshalledObject; +import java.util.Properties; + +public final class ActivationGroupDesc implements Serializable +{ + static final long serialVersionUID = -4936225423168276595L; + +public static class CommandEnvironment + implements Serializable { + +static final long serialVersionUID = 6165754737887770191L; + +private String cmdpath; +private String[] argv; + +public CommandEnvironment(String cmdpath, String[] argv) { + this.cmdpath = cmdpath; + this.argv = argv; +} + +public String getCommandPath() { + return (cmdpath); +} + +public String[] getCommandOptions() { + return (argv); +} + +public boolean equals(Object obj) { + if (!(obj instanceof CommandEnvironment)) { + return (false); + } + CommandEnvironment that = (CommandEnvironment)obj; + + if (!this.cmdpath.equals(that.cmdpath)) { + return (false); + } + + int len = this.argv.length; + if (len != that.argv.length) { + return (false); + } + for (int i = 0; i < len; i++) { + if (!this.argv[i].equals(that.argv[i])) { + return (false); + } + } + return (true); +} + +public int hashCode() { + return (cmdpath.hashCode()); // Not a very good hash code. +} + +} + +public ActivationGroupDesc(Properties overrides, ActivationGroupDesc.CommandEnvironment cmd) { + throw new Error("Not implemented"); +} + +public ActivationGroupDesc(String className, String location, MarshalledObject data, Properties overrides, ActivationGroupDesc.CommandEnvironment cmd) { + throw new Error("Not implemented"); +} + +public String getClassName() { + throw new Error("Not implemented"); +} + +public String getLocation() { + throw new Error("Not implemented"); +} + +public MarshalledObject getData() { + throw new Error("Not implemented"); +} + +public Properties getPropertyOverrides() { + throw new Error("Not implemented"); +} + +public ActivationGroupDesc.CommandEnvironment getCommandEnvironment() { + throw new Error("Not implemented"); +} + +public boolean equals(Object obj) { + throw new Error("Not implemented"); +} + +public int hashCode() { + throw new Error("Not implemented"); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroupID.java b/libjava/classpath/java/rmi/activation/ActivationGroupID.java new file mode 100644 index 00000000000..5e0b96442e8 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroupID.java @@ -0,0 +1,70 @@ +/* ActivationGroupID.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.io.Serializable; + +public class ActivationGroupID implements Serializable +{ + static final long serialVersionUID = -1648432278909740833L; + +private ActivationSystem system; + +public ActivationGroupID(ActivationSystem system) { + this.system = system; +} + +public ActivationSystem getSystem() { + return (system); +} + +public int hashCode() { + return (system.hashCode()); +} + +public boolean equals(Object obj) { + if (obj instanceof ActivationGroupID) { + ActivationGroupID that = (ActivationGroupID)obj; + if (this.system.equals(that.system)) { + return (true); + } + } + return (false); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationID.java b/libjava/classpath/java/rmi/activation/ActivationID.java new file mode 100644 index 00000000000..23ed853a478 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationID.java @@ -0,0 +1,72 @@ +/* ActivationID.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.io.Serializable; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public class ActivationID implements Serializable +{ + static final long serialVersionUID = -4608673054848209235L; + +private Activator activator; + +public ActivationID(Activator activator) { + this.activator = activator; +} + +public Remote activate(boolean force) throws ActivationException, UnknownObjectException, RemoteException { + throw new Error("Not implemented"); +} + +public int hashCode() { + return (activator.hashCode()); +} + +public boolean equals(Object obj) { + if (obj instanceof ActivationID) { + ActivationID that = (ActivationID)obj; + if (this.activator.equals(that.activator)) { + return (true); + } + } + return (false); +} + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationInstantiator.java b/libjava/classpath/java/rmi/activation/ActivationInstantiator.java new file mode 100644 index 00000000000..0aceb7a4ad6 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationInstantiator.java @@ -0,0 +1,50 @@ +/* ActivationInstantiator.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface ActivationInstantiator + extends Remote +{ + MarshalledObject newInstance (ActivationID id, ActivationDesc desc) + throws ActivationException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/ActivationMonitor.java b/libjava/classpath/java/rmi/activation/ActivationMonitor.java new file mode 100644 index 00000000000..1e64257edd1 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationMonitor.java @@ -0,0 +1,55 @@ +/* ActivationMonitor.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface ActivationMonitor extends Remote +{ + void inactiveObject (ActivationID id) + throws UnknownObjectException, RemoteException; + + void activeObject (ActivationID id, MarshalledObject obj) + throws UnknownObjectException, RemoteException; + + void inactiveGroup (ActivationGroupID id, long incarnation) + throws UnknownGroupException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/ActivationSystem.java b/libjava/classpath/java/rmi/activation/ActivationSystem.java new file mode 100644 index 00000000000..4b92d40cb44 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationSystem.java @@ -0,0 +1,78 @@ +/* ActivationSystem.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface ActivationSystem extends Remote +{ + int SYSTEM_PORT = 1098; + + ActivationID registerObject (ActivationDesc desc) + throws ActivationException, UnknownGroupException, RemoteException; + + void unregisterObject (ActivationID id) + throws ActivationException, UnknownObjectException, RemoteException; + + ActivationGroupID registerGroup (ActivationGroupDesc desc) + throws ActivationException, RemoteException; + + ActivationMonitor activeGroup (ActivationGroupID id, + ActivationInstantiator group, long incarnation) + throws UnknownGroupException, ActivationException, RemoteException; + + void unregisterGroup (ActivationGroupID id) + throws ActivationException, UnknownGroupException, RemoteException; + + void shutdown() + throws RemoteException; + + ActivationDesc setActivationDesc (ActivationID id, ActivationDesc desc) + throws ActivationException, UnknownObjectException, UnknownGroupException, + RemoteException; + + ActivationGroupDesc setActivationGroupDesc (ActivationGroupID id, + ActivationGroupDesc desc) + throws ActivationException, UnknownGroupException, RemoteException; + + ActivationDesc getActivationDesc (ActivationID id) throws ActivationException, UnknownObjectException, RemoteException; + + ActivationGroupDesc getActivationGroupDesc (ActivationGroupID id) throws ActivationException, UnknownGroupException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/Activator.java b/libjava/classpath/java/rmi/activation/Activator.java new file mode 100644 index 00000000000..2fb5a581002 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/Activator.java @@ -0,0 +1,50 @@ +/* Activator.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface Activator + extends Remote +{ + MarshalledObject activate (ActivationID id, boolean force) + throws ActivationException, UnknownObjectException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/UnknownGroupException.java b/libjava/classpath/java/rmi/activation/UnknownGroupException.java new file mode 100644 index 00000000000..91890a9073d --- /dev/null +++ b/libjava/classpath/java/rmi/activation/UnknownGroupException.java @@ -0,0 +1,69 @@ +/* UnknownGroupException.java -- thrown on an invalid ActivationGroupID + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * Thrown when an <code>ActivationGroupID</code> parameter is invalid or + * unknown. + * + * @author unknown + * @see Activatable + * @see ActivationGroup + * @see ActivationID + * @see ActivationMonitor + * @see ActivationSystem + * @since 1.2 + * @status updated to 1.4 + */ +public class UnknownGroupException extends ActivationException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7056094974750002460L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnknownGroupException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/activation/UnknownObjectException.java b/libjava/classpath/java/rmi/activation/UnknownObjectException.java new file mode 100644 index 00000000000..8dbeb0e6050 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/UnknownObjectException.java @@ -0,0 +1,69 @@ +/* UnknownObjectException.java -- thrown on an invalid ActivationID + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * Thrown when an <code>ActivationID</code> parameter is invalid or unknown. + * + * @author unknown + * @see Activatable + * @see ActivationGroup + * @see ActivationID + * @see ActivationMonitor + * @see ActivationSystem + * @see Activator + * @since 1.2 + * @status updated to 1.4 + */ +public class UnknownObjectException extends ActivationException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3425547551622251430L; + + /** + * Create an exception with an error message. + * + * @param s the message + */ + public UnknownObjectException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/activation/package.html b/libjava/classpath/java/rmi/activation/package.html new file mode 100644 index 00000000000..444782dd721 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.rmi.activation package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.rmi.activation</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/rmi/dgc/DGC.java b/libjava/classpath/java/rmi/dgc/DGC.java new file mode 100644 index 00000000000..e78ec2a3aa7 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/DGC.java @@ -0,0 +1,51 @@ +/* DGC.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.ObjID; + +public interface DGC extends Remote +{ + Lease dirty (ObjID[] ids, long sequenceNum, Lease lease) + throws RemoteException; + + void clean (ObjID[] ids, long sequenceNum, VMID vmid, boolean strong) + throws RemoteException; +} diff --git a/libjava/classpath/java/rmi/dgc/Lease.java b/libjava/classpath/java/rmi/dgc/Lease.java new file mode 100644 index 00000000000..d3d7f695216 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/Lease.java @@ -0,0 +1,67 @@ +/* Lease.java + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import java.io.Serializable; + +public final class Lease + implements Serializable { + +static final long serialVersionUID = -5713411624328831948L; + +private VMID vmid; +private long value; + +public Lease(VMID id, long duration) { + vmid = id; + value = duration; +} + +public VMID getVMID() { + return (vmid); +} + +public long getValue() { + return (value); +} + +public String toString() { + return ("[" + vmid.toString() + ", " + Long.toString(value) + "]"); +} + +} diff --git a/libjava/classpath/java/rmi/dgc/VMID.java b/libjava/classpath/java/rmi/dgc/VMID.java new file mode 100644 index 00000000000..f960d9ccd79 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/VMID.java @@ -0,0 +1,138 @@ +/* VMID.java + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.server.UID; + +public final class VMID implements Serializable +{ + static final long serialVersionUID = -538642295484486218L; + + static final boolean areWeUnique; + + static byte[] localAddr; + + private byte[] addr; + + private UID uid; + + static + { + byte[] addr; + boolean awu = true; + try { + addr = InetAddress.getLocalHost().getAddress(); + if (addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) { + awu = false; + } + } + catch (UnknownHostException _) { + addr = new byte[]{ 127, 0, 0, 1 }; + awu = false; + } + localAddr = addr; + areWeUnique = awu; + } + + public VMID() + { + addr = localAddr; + uid = new UID(); + } + + /** + * @deprecated + */ + public static boolean isUnique () + { + return areWeUnique; + } + + public int hashCode () + { + return super.hashCode(); + } + + public boolean equals (Object obj) + { + if (!(obj instanceof VMID)) + { + return false; + } + + VMID other = (VMID) obj; + if (addr.length != other.addr.length) + { + return false; + } + + for (int i = addr.length - 1; i >= 0; i--) + { + if (addr[i] != other.addr[i]) + { + return false; + } + } + + return uid.equals(other.uid); + } + + public String toString () + { + StringBuffer buf = new StringBuffer ("[VMID: "); + + for (int i = 0; i < addr.length; i++) + { + if (i > 0) + { + buf.append ("."); + } + + buf.append (Integer.toString (addr [i])); + } + + buf.append (" "); + buf.append (uid.toString ()); + buf.append ("]"); + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/rmi/dgc/package.html b/libjava/classpath/java/rmi/dgc/package.html new file mode 100644 index 00000000000..9458d4c25f9 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.rmi.dgc package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.rmi.dgc</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/rmi/package.html b/libjava/classpath/java/rmi/package.html new file mode 100644 index 00000000000..a7bf8502056 --- /dev/null +++ b/libjava/classpath/java/rmi/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.rmi package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.rmi</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/rmi/registry/LocateRegistry.java b/libjava/classpath/java/rmi/registry/LocateRegistry.java new file mode 100644 index 00000000000..3547a7ad0f4 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/LocateRegistry.java @@ -0,0 +1,87 @@ +/* LocateRegistry.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.registry; + +import gnu.java.rmi.registry.RegistryImpl; +import gnu.java.rmi.registry.RegistryImpl_Stub; +import gnu.java.rmi.server.UnicastRef; + +import java.rmi.RemoteException; +import java.rmi.server.ObjID; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RMISocketFactory; +import java.rmi.server.RemoteRef; + +public final class LocateRegistry { + /** + * This class isn't intended to be instantiated. + */ + private LocateRegistry() {} + +public static Registry getRegistry() throws RemoteException { + return (getRegistry("localhost", Registry.REGISTRY_PORT)); +} + +public static Registry getRegistry(int port) throws RemoteException { + return (getRegistry("localhost", port)); +} + +public static Registry getRegistry(String host) throws RemoteException { + return (getRegistry(host, Registry.REGISTRY_PORT)); +} + +public static Registry getRegistry(String host, int port) throws RemoteException { + return (getRegistry(host, port, RMISocketFactory.getSocketFactory())); +} + +public static Registry getRegistry(String host, int port, RMIClientSocketFactory csf) throws RemoteException { + RemoteRef ref = new UnicastRef(new ObjID(ObjID.REGISTRY_ID), host, port, csf); + return (new RegistryImpl_Stub(ref)); +} + +public static Registry createRegistry(int port) throws RemoteException { + return (createRegistry(port, RMISocketFactory.getSocketFactory(), RMISocketFactory.getSocketFactory())); +} + +public static Registry createRegistry(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { + return (new RegistryImpl(port, csf, ssf)); +} + +} diff --git a/libjava/classpath/java/rmi/registry/Registry.java b/libjava/classpath/java/rmi/registry/Registry.java new file mode 100644 index 00000000000..6cd2a04a685 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/Registry.java @@ -0,0 +1,65 @@ +/* Registry.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.registry; + +import java.rmi.AccessException; +import java.rmi.AlreadyBoundException; +import java.rmi.NotBoundException; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface Registry extends Remote +{ + int REGISTRY_PORT = 1099; + + Remote lookup(String name) + throws RemoteException, NotBoundException, AccessException; + + void bind(String name, Remote obj) + throws RemoteException, AlreadyBoundException, AccessException; + + void unbind(String name) + throws RemoteException, NotBoundException, AccessException; + + void rebind(String name, Remote obj) + throws RemoteException, AccessException; + + String[] list() + throws RemoteException, AccessException; +} diff --git a/libjava/classpath/java/rmi/registry/RegistryHandler.java b/libjava/classpath/java/rmi/registry/RegistryHandler.java new file mode 100644 index 00000000000..b9b45092305 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/RegistryHandler.java @@ -0,0 +1,58 @@ +/* RegistryHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.registry; + +import java.rmi.RemoteException; +import java.rmi.UnknownHostException; + +/** + * @deprecated + */ +public interface RegistryHandler +{ + /** + * @deprecated + */ + Registry registryStub (String host, int port) + throws RemoteException, UnknownHostException; + + /** + * @deprecated + */ + Registry registryImpl (int port) throws RemoteException; +} diff --git a/libjava/classpath/java/rmi/registry/package.html b/libjava/classpath/java/rmi/registry/package.html new file mode 100644 index 00000000000..4e16672709c --- /dev/null +++ b/libjava/classpath/java/rmi/registry/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.rmi.registry package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.rmi.registry</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/rmi/server/ExportException.java b/libjava/classpath/java/rmi/server/ExportException.java new file mode 100644 index 00000000000..b2d5bfca52e --- /dev/null +++ b/libjava/classpath/java/rmi/server/ExportException.java @@ -0,0 +1,78 @@ +/* ExportException.java -- an export attempt failed + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if an attempt to export a remote object fails. + * + * @author unknown + * @see UnicastRemoteObject + * @see Activatable + * @since 1.1 + * @status updated to 1.4 + */ +public class ExportException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -9155485338494060170L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public ExportException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public ExportException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/LoaderHandler.java b/libjava/classpath/java/rmi/server/LoaderHandler.java new file mode 100644 index 00000000000..189085710b4 --- /dev/null +++ b/libjava/classpath/java/rmi/server/LoaderHandler.java @@ -0,0 +1,66 @@ +/* LoaderHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * @deprecated + */ +public interface LoaderHandler +{ + String packagePrefix = ""; + + /** + * @deprecated + */ + Class loadClass(String name) + throws MalformedURLException, ClassNotFoundException; + + /** + * @deprecated + */ + Class loadClass(URL codebase, String name) + throws MalformedURLException, ClassNotFoundException; + + /** + * @deprecated + */ + Object getSecurityContext(ClassLoader loader); +} diff --git a/libjava/classpath/java/rmi/server/LogStream.java b/libjava/classpath/java/rmi/server/LogStream.java new file mode 100644 index 00000000000..a2dfbb47fcc --- /dev/null +++ b/libjava/classpath/java/rmi/server/LogStream.java @@ -0,0 +1,146 @@ +/* LogStream.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * @deprecated + */ +public class LogStream extends PrintStream +{ + public static final int SILENT = 0; + public static final int BRIEF = 10; + public static final int VERBOSE = 20; + + private static PrintStream defStream; + + private LogStream (OutputStream s) + { + super (s); + } + + /** + * @deprecated + */ + public static LogStream log (String name) + { + throw new Error ("Not implemented"); + } + + /** + * @deprecated + */ + public static PrintStream getDefaultStream () + { + return defStream; + } + + /** + * @deprecated + */ + public static void setDefaultStream (PrintStream s) + { + defStream = s; + } + + /** + * @deprecated + */ + public OutputStream getOutputStream () + { + return out; + } + + /** + * @deprecated + */ + public void setOutputStream (OutputStream s) + { + out = s; + } + + /** + * @deprecated + */ + public void write (int buffer) + { + super.write (buffer); + } + + /** + * @deprecated + */ + public void write (byte[] buffer, int offset, int len) + { + super.write (buffer, offset, len); + } + + /** + * @deprecated + */ + public String toString () + { + throw new Error ("Not implemented"); + } + + /** + * @deprecated + */ + public static int parseLevel (String s) + { + if (s.equalsIgnoreCase ("silent")) + { + return SILENT; + } + + if (s.equalsIgnoreCase ("brief")) + { + return BRIEF; + } + + if (s.equalsIgnoreCase ("verbose")) + { + return VERBOSE; + } + + return SILENT; + } +} diff --git a/libjava/classpath/java/rmi/server/ObjID.java b/libjava/classpath/java/rmi/server/ObjID.java new file mode 100644 index 00000000000..07cbbde3a62 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ObjID.java @@ -0,0 +1,103 @@ +/* ObjID.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; + +public final class ObjID implements Serializable +{ +static final long serialVersionUID = -6386392263968365220L; + +private static long next = 0x8000000000000000L; +private static final Object lock = ObjID.class; + +public static final int REGISTRY_ID = 0; +public static final int ACTIVATOR_ID = 1; +public static final int DGC_ID = 2; + +private long objNum; +private UID space; + +public ObjID() { + synchronized (lock) { + objNum = next++; + } + space = new UID(); +} + +public ObjID(int num) { + objNum = (long)num; + space = new UID((short)0); +} + +public void write(ObjectOutput out) throws IOException { + DataOutput dout = (DataOutput)out; + dout.writeLong(objNum); + space.write(dout); +} + +public static ObjID read(ObjectInput in) throws IOException { + DataInput din = (DataInput)in; + ObjID id = new ObjID(); + id.objNum = din.readLong(); + id.space = UID.read(din); + return (id); +} + +public int hashCode() { + return ((int)objNum); +} + +public boolean equals(Object obj) { + if (obj instanceof ObjID && this.objNum == ((ObjID)obj).objNum) { + return (true); + } + return (false); +} + +public String toString() { + return ("[objNum: " + objNum + ", " + space + "]"); +} + +} diff --git a/libjava/classpath/java/rmi/server/Operation.java b/libjava/classpath/java/rmi/server/Operation.java new file mode 100644 index 00000000000..64faf66e2f8 --- /dev/null +++ b/libjava/classpath/java/rmi/server/Operation.java @@ -0,0 +1,70 @@ +/* Operation.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * @deprecated + */ +public class Operation +{ + private String operation; + + /** + * @deprecated + */ + public Operation (String op) + { + operation = op; + } + + /** + * @deprecated + */ + public String getOperation () + { + return operation; + } + + /** + * @deprecated + */ + public String toString () + { + return operation; + } +} diff --git a/libjava/classpath/java/rmi/server/RMIClassLoader.java b/libjava/classpath/java/rmi/server/RMIClassLoader.java new file mode 100644 index 00000000000..1a2e2837dd8 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClassLoader.java @@ -0,0 +1,339 @@ +/* RMIClassLoader.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; +import java.util.StringTokenizer; + + +/** + * This class provides a set of public static utility methods for supporting + * network-based class loading in RMI. These methods are called by RMI's + * internal marshal streams to implement the dynamic class loading of types for + * RMI parameters and return values. + */ +public class RMIClassLoader +{ + /** + * This class isn't intended to be instantiated. + */ + private RMIClassLoader() {} + + private static class MyClassLoader extends URLClassLoader + { + // Package-private to avoid a trampoline constructor. + MyClassLoader (URL[] urls, ClassLoader parent, String annotation) + { + super (urls, parent); + this.annotation = annotation; + } + + private MyClassLoader (URL[] urls, ClassLoader parent) + { + super (urls, parent); + this.annotation = urlToAnnotation (urls); + } + + public static String urlToAnnotation (URL[] urls) + { + if (urls.length == 0) + return null; + + StringBuffer annotation = new StringBuffer (64 * urls.length); + + for (int i = 0; i < urls.length; i++) + { + annotation.append (urls [i].toExternalForm()); + annotation.append (' '); + } + + return annotation.toString(); + } + + public final String getClassAnnotation() + { + return annotation; + } + + private final String annotation; + } + + /** + * This class is used to identify a cached classloader by its codebase and + * the context classloader that is its parent. + */ + private static class CacheKey + { + private String mCodeBase; + private ClassLoader mContextClassLoader; + + public CacheKey (String theCodebase, ClassLoader theContextClassLoader) + { + mCodeBase = theCodebase; + mContextClassLoader = theContextClassLoader; + } + + /** + * @return true if the codebase and the context classloader are equal + */ + public boolean equals (Object theOther) + { + if (theOther instanceof CacheKey) + { + CacheKey key = (CacheKey) theOther; + + return (equals (this.mCodeBase,key.mCodeBase) + && equals (this.mContextClassLoader, key.mContextClassLoader)); + } + return false; + } + + /** + * Test if the two objects are equal or both null. + * @param theOne + * @param theOther + * @return + */ + private boolean equals (Object theOne, Object theOther) + { + return theOne != null ? theOne.equals (theOther) : theOther == null; + } + + /** + * @return hashCode + */ + public int hashCode() + { + return ((mCodeBase != null ? mCodeBase.hashCode() : 0) + ^(mContextClassLoader != null ? mContextClassLoader.hashCode() : -1)); + } + + public String toString() + { + return "[" + mCodeBase + "," + mContextClassLoader + "]"; + } + + } + + private static Map cacheLoaders; //map annotations to loaders + private static Map cacheAnnotations; //map loaders to annotations + + //defaultAnnotation is got from system property + // "java.rmi.server.defaultAnnotation" + private static String defaultAnnotation; + + //URL object for defaultAnnotation + private static URL defaultCodebase; + + //class loader for defaultAnnotation + private static MyClassLoader defaultLoader; + + static + { + // 89 is a nice prime number for Hashtable initial capacity + cacheLoaders = new Hashtable (89); + cacheAnnotations = new Hashtable (89); + + defaultAnnotation = System.getProperty ("java.rmi.server.defaultAnnotation"); + + try + { + if (defaultAnnotation != null) + defaultCodebase = new URL (defaultAnnotation); + } + catch (Exception _) + { + defaultCodebase = null; + } + + if (defaultCodebase != null) + { + defaultLoader = new MyClassLoader (new URL[] { defaultCodebase }, null, + defaultAnnotation); + cacheLoaders.put (new CacheKey (defaultAnnotation, + Thread.currentThread().getContextClassLoader()), + defaultLoader); + } + } + + /** + * @deprecated + */ + public static Class loadClass (String name) + throws MalformedURLException, ClassNotFoundException + { + return loadClass ("", name); + } + + public static Class loadClass (String codebases, String name) + throws MalformedURLException, ClassNotFoundException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + //try context class loader first + try + { + return loader.loadClass (name); + } + catch (ClassNotFoundException e) + { + // class not found in the local classpath + } + + if (codebases.length() == 0) //=="" + { + loader = defaultLoader; + } + else + { + loader = getClassLoader(codebases); + } + + if (loader == null) + { + //do not throw NullPointerException + throw new ClassNotFoundException ("Could not find class (" + name + + ") at codebase (" + codebases + ")"); + } + + return loader.loadClass (name); + } + + /** + * Gets a classloader for the given codebase and with the current + * context classloader as parent. + * + * @param codebases + * + * @return a classloader for the given codebase + * + * @throws MalformedURLException if the codebase contains a malformed URL + */ + public static ClassLoader getClassLoader (String codebases) + throws MalformedURLException + { + ClassLoader loader; + CacheKey loaderKey = new CacheKey + (codebases, Thread.currentThread().getContextClassLoader()); + loader = (ClassLoader) cacheLoaders.get (loaderKey); + + if (loader == null) + { + //create an entry in cacheLoaders mapping a loader to codebases. + // codebases are separated by " " + StringTokenizer tok = new StringTokenizer (codebases, " "); + ArrayList urls = new ArrayList(); + + while (tok.hasMoreTokens()) + urls.add (new URL (tok.nextToken())); + + loader = new MyClassLoader ((URL[]) urls.toArray (new URL [urls.size()]), + Thread.currentThread().getContextClassLoader(), + codebases); + cacheLoaders.put (loaderKey, loader); + } + + return loader; + } + + /** + * Returns a string representation of the network location where a remote + * endpoint can get the class-definition of the given class. + * + * @param cl + * + * @return a space seperated list of URLs where the class-definition + * of cl may be found + */ + public static String getClassAnnotation (Class cl) + { + ClassLoader loader = cl.getClassLoader(); + + if (loader == null + || loader == ClassLoader.getSystemClassLoader()) + { + return System.getProperty ("java.rmi.server.codebase"); + } + + if (loader instanceof MyClassLoader) + { + return ((MyClassLoader) loader).getClassAnnotation(); + } + + String s = (String) cacheAnnotations.get (loader); + + if (s != null) + return s; + + if (loader instanceof URLClassLoader) + { + URL[] urls = ((URLClassLoader) loader).getURLs(); + + if (urls.length == 0) + return null; + + StringBuffer annotation = new StringBuffer (64 * urls.length); + + for (int i = 0; i < urls.length; i++) + { + annotation.append (urls [i].toExternalForm()); + annotation.append (' '); + } + + s = annotation.toString(); + cacheAnnotations.put (loader, s); + return s; + } + + return System.getProperty ("java.rmi.server.codebase"); + } + + /** + * @deprecated + */ + public static Object getSecurityContext (ClassLoader loader) + { + throw new Error ("Not implemented"); + } +} diff --git a/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java b/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java new file mode 100644 index 00000000000..372d81879f9 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java @@ -0,0 +1,64 @@ +/* RMIClassLoaderSpi.java -- + Copyright (c) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.net.MalformedURLException; + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class RMIClassLoaderSpi +{ + public RMIClassLoaderSpi() + { + } + + public abstract Class loadClass (String codeBase, String name, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException; + + public abstract Class loadProxyClass (String codeBase, String[] interfaces, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException; + + public abstract ClassLoader getClassLoader (String codebase) + throws MalformedURLException; + + public abstract String getClassAnnotation (Class cl); +} diff --git a/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java b/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java new file mode 100644 index 00000000000..a54d11175e0 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java @@ -0,0 +1,47 @@ +/* RMIClientSocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.net.Socket; + +public interface RMIClientSocketFactory +{ + Socket createSocket (String host, int port) throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RMIFailureHandler.java b/libjava/classpath/java/rmi/server/RMIFailureHandler.java new file mode 100644 index 00000000000..3496cd6a073 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIFailureHandler.java @@ -0,0 +1,46 @@ +/* RMIFailureHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +public interface RMIFailureHandler +{ + /** + * @exception IOException If an error occurs + */ + boolean failure (Exception ex); +} diff --git a/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java b/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java new file mode 100644 index 00000000000..88eaff32dbc --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java @@ -0,0 +1,47 @@ +/* RMIServerSocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.net.ServerSocket; + +public interface RMIServerSocketFactory +{ + ServerSocket createServerSocket(int port) throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RMISocketFactory.java b/libjava/classpath/java/rmi/server/RMISocketFactory.java new file mode 100644 index 00000000000..953f1ef33ce --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMISocketFactory.java @@ -0,0 +1,108 @@ +/* RMISocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.RMIDefaultSocketFactory; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public abstract class RMISocketFactory + implements RMIClientSocketFactory, RMIServerSocketFactory +{ + private static RMISocketFactory defaultFactory; + private static RMISocketFactory currentFactory; + private static RMIFailureHandler currentHandler; + + static + { + defaultFactory = new RMIDefaultSocketFactory(); + currentFactory = defaultFactory; + } + + public RMISocketFactory () + { + } + + /** + * @exception IOException If an error occurs + */ + public abstract Socket createSocket (String host, int port) + throws IOException; + + /** + * @exception IOException If an error occurs + */ + public abstract ServerSocket createServerSocket (int port) + throws IOException; + + /** + * @exception IOException If an error occurs + * @exception SecurityException FIXME + */ + public static void setSocketFactory (RMISocketFactory fac) + throws IOException + { + currentFactory = fac; + } + + public static RMISocketFactory getSocketFactory () + { + return currentFactory; + } + + public static RMISocketFactory getDefaultSocketFactory () + { + return defaultFactory; + } + + /** + * @exception SecurityException FIXME + */ + public static void setFailureHandler (RMIFailureHandler fh) + { + currentHandler = fh; + } + + public static RMIFailureHandler getFailureHandler () + { + return currentHandler; + } +} diff --git a/libjava/classpath/java/rmi/server/RemoteCall.java b/libjava/classpath/java/rmi/server/RemoteCall.java new file mode 100644 index 00000000000..35f3bf1a655 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteCall.java @@ -0,0 +1,86 @@ +/* RemoteCall.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.StreamCorruptedException; + +/** + * @deprecated + */ +public interface RemoteCall +{ + /** + * @deprecated + */ + ObjectOutput getOutputStream () throws IOException; + + /** + * @deprecated + */ + void releaseOutputStream () throws IOException; + + /** + * @deprecated + */ + ObjectInput getInputStream () throws IOException; + + /** + * @deprecated + */ + void releaseInputStream () throws IOException; + + /** + * @deprecated + */ + ObjectOutput getResultStream (boolean success) + throws IOException, StreamCorruptedException; + + /** + * @deprecated + */ + void executeCall () throws Exception; + + /** + * @deprecated + */ + void done () throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RemoteObject.java b/libjava/classpath/java/rmi/server/RemoteObject.java new file mode 100644 index 00000000000..0b3c229c9d9 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteObject.java @@ -0,0 +1,160 @@ +/* RemoteObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.UnmarshalException; +import java.util.WeakHashMap; + +public abstract class RemoteObject + implements Remote, Serializable { + +private static final long serialVersionUID = -3215090123894869218l; + +protected transient RemoteRef ref; + +private static final WeakHashMap stubs = new WeakHashMap(); + +protected RemoteObject() { + this(null); +} + +protected RemoteObject(RemoteRef newref) { + ref = newref; +} + +public RemoteRef getRef() { + return (ref); +} + +synchronized static void addStub(Remote obj, Remote stub) +{ + stubs.put(obj, stub); +} + +synchronized static void deleteStub(Remote obj) +{ + stubs.remove(obj); +} + + public static Remote toStub(Remote obj) throws NoSuchObjectException + { + Remote stub = (Remote)stubs.get(obj); + + if (stub == null) + throw new NoSuchObjectException(obj.getClass().getName()); + + return stub; + } + +public int hashCode() { + if (ref == null) { + return (0); + } + else { + return (ref.hashCode()); + } +} + +public boolean equals(Object obj) { + // We only compare references. + return (this == obj); +} + + public String toString() + { + if (ref == null) + return getClass ().toString (); + return (ref.toString ()); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException + { + String cname = in.readUTF(); + if (!cname.equals("")) + { + if (cname.equals ("UnicastRef2")) + { + // hack for interoperating with JDK + cname = "UnicastRef"; + in.read (); //some unknown UnicastRef2 field + } + + cname = RemoteRef.packagePrefix + '.' + cname; + try + { + Class cls = Class.forName(cname); + ref = (RemoteRef)cls.newInstance(); + } + catch (InstantiationException e1) + { + throw new UnmarshalException("failed to create ref", e1); + } + catch (IllegalAccessException e2) + { + throw new UnmarshalException("failed to create ref", e2); + } + ref.readExternal(in); + } + else + { + ref = (RemoteRef)in.readObject(); + } + } + +private void writeObject(ObjectOutputStream out) throws IOException, ClassNotFoundException { + if (ref == null) { + throw new UnmarshalException("no ref to serialize"); + } + String cname = ref.getRefClass(out); + if (cname != null && cname.length() > 0) { + out.writeUTF(cname); + ref.writeExternal(out); + } + else { + out.writeUTF(""); + out.writeObject(ref); + } +} + +} diff --git a/libjava/classpath/java/rmi/server/RemoteRef.java b/libjava/classpath/java/rmi/server/RemoteRef.java new file mode 100644 index 00000000000..7e34db39e89 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteRef.java @@ -0,0 +1,79 @@ +/* RemoteRef.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.Externalizable; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface RemoteRef extends Externalizable +{ + long serialVersionUID = 3632638527362204081L; + + String packagePrefix = "gnu.java.rmi.server"; + + /** + * @deprecated + */ + void invoke (RemoteCall call) throws Exception; + + Object invoke (Remote obj, Method method, Object[] params, long opnum) + throws Exception; + + /** + * @deprecated + */ + RemoteCall newCall (RemoteObject obj, Operation[] op, int opnum, long hash) + throws RemoteException; + + /** + * @deprecated + */ + void done (RemoteCall call) throws RemoteException; + + boolean remoteEquals (RemoteRef ref); + + int remoteHashCode(); + + String getRefClass (ObjectOutput out); + + String remoteToString(); +} diff --git a/libjava/classpath/java/rmi/server/RemoteServer.java b/libjava/classpath/java/rmi/server/RemoteServer.java new file mode 100644 index 00000000000..9efb12a782d --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteServer.java @@ -0,0 +1,76 @@ +/* RemoteServer.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import gnu.java.rmi.server.RMIIncomingThread; + +import java.io.OutputStream; +import java.io.PrintStream; + +public abstract class RemoteServer extends RemoteObject +{ +private static final long serialVersionUID = -4100238210092549637L; + +protected RemoteServer() { + super(); +} + +protected RemoteServer(RemoteRef ref) { + super(ref); +} + +public static String getClientHost() throws ServerNotActiveException { + Thread currThread = Thread.currentThread(); + if (currThread instanceof RMIIncomingThread) { + RMIIncomingThread incomingThread = (RMIIncomingThread) currThread; + return incomingThread.getClientHost(); + } else { + throw new ServerNotActiveException( + "Unknown client host - current thread not instance of 'RMIIncomingThread'"); + } +} + +public static void setLog(OutputStream out) { + throw new Error("Not implemented"); +} + +public static PrintStream getLog() { + throw new Error("Not implemented"); +} + +} diff --git a/libjava/classpath/java/rmi/server/RemoteStub.java b/libjava/classpath/java/rmi/server/RemoteStub.java new file mode 100644 index 00000000000..18c614b54a8 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteStub.java @@ -0,0 +1,61 @@ +/* RemoteStub.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +public abstract class RemoteStub extends RemoteObject +{ + static final long serialVersionUID = -1585587260594494182l; + + protected RemoteStub () + { + super (); + } + + protected RemoteStub (RemoteRef ref) + { + super (ref); + } + + /** + * @deprecated + */ + protected static void setRef (RemoteStub stub, RemoteRef ref) + { + stub.ref = ref; + } +} // class RemoteSub diff --git a/libjava/classpath/java/rmi/server/ServerCloneException.java b/libjava/classpath/java/rmi/server/ServerCloneException.java new file mode 100644 index 00000000000..bda41b3ce91 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerCloneException.java @@ -0,0 +1,117 @@ +/* ServerCloneException.java -- a UnicastRemoteObject could not be cloned + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown if a remote exception occurs during the cloning process of a + * <code>UnicastRemoteObject</code>. + * + * @author unknown + * @see UnicastRemoteObject#clone() + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerCloneException extends CloneNotSupportedException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6617456357664815945L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Exception detail; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerCloneException(String s) + { + this(s, null); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerCloneException(String s, Exception e) + { + super(s); + initCause(e); + detail = e; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * <code>super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail)</code>. + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the <code>detail</code> field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if <code>detail == this</code>, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/server/ServerNotActiveException.java b/libjava/classpath/java/rmi/server/ServerNotActiveException.java new file mode 100644 index 00000000000..0581b63bc82 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerNotActiveException.java @@ -0,0 +1,72 @@ +/* ServerNotActiveException.java -- the method is not servicing a remote call + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown during <code>RemoteServer.getClientHost</code> if the host is + * not servicing a remote method call. + * + * @author unknown + * @see RemoteServer#getClientHost() + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerNotActiveException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4687940720827538231L; + + /** + * Create an exception with no message. + */ + public ServerNotActiveException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerNotActiveException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/server/ServerRef.java b/libjava/classpath/java/rmi/server/ServerRef.java new file mode 100644 index 00000000000..cf1013cf02b --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerRef.java @@ -0,0 +1,51 @@ +/* ServerRef.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface ServerRef extends RemoteRef +{ + long serialVersionUID = -4557750989390278438L; + + RemoteStub exportObject(Remote obj, Object data) throws RemoteException; + + String getClientHost() throws ServerNotActiveException; +} diff --git a/libjava/classpath/java/rmi/server/Skeleton.java b/libjava/classpath/java/rmi/server/Skeleton.java new file mode 100644 index 00000000000..82f5d3837f4 --- /dev/null +++ b/libjava/classpath/java/rmi/server/Skeleton.java @@ -0,0 +1,57 @@ +/* Skeleton.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.Remote; + +/** + * @deprecated + */ +public interface Skeleton +{ + /** + * @deprecated + */ + void dispatch (Remote obj, RemoteCall theCall, int opnum, long hash) + throws Exception; + + /** + * @deprecated + */ + Operation[] getOperations(); +} diff --git a/libjava/classpath/java/rmi/server/SkeletonMismatchException.java b/libjava/classpath/java/rmi/server/SkeletonMismatchException.java new file mode 100644 index 00000000000..9c0206ab3bc --- /dev/null +++ b/libjava/classpath/java/rmi/server/SkeletonMismatchException.java @@ -0,0 +1,68 @@ +/* SkeletonMismatchException.java -- thrown when stub class versions mismatch + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if a call is received that does not match a Skeleton. Note that + * Skeletons are no longer required. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement. Skeletons are no longer required. + * @status updated to 1.4 + */ +public class SkeletonMismatchException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -7780460454818859281l; + + /** + * Create an exception with the specified message. + * + * @param s the message + * @deprecated no longer needed + */ + public SkeletonMismatchException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java b/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java new file mode 100644 index 00000000000..596aae15417 --- /dev/null +++ b/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java @@ -0,0 +1,79 @@ +/* SkeletonNotFoundException.java -- thrown if a Skeleton is not found + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if a Skeleton corresponding to the remote object is not found. + * Note that Skeletons are no longer required. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement. Skeletons are no longer required. + * @status updated to 1.4 + */ +public class SkeletonNotFoundException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -7860299673822761231L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public SkeletonNotFoundException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public SkeletonNotFoundException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/SocketSecurityException.java b/libjava/classpath/java/rmi/server/SocketSecurityException.java new file mode 100644 index 00000000000..aaf7698f4bc --- /dev/null +++ b/libjava/classpath/java/rmi/server/SocketSecurityException.java @@ -0,0 +1,75 @@ +/* SocketSecurityException.java -- the socket could not be created + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown during remote object export if the code does not have permission + * to create a <code>java.net.ServerSocket</code> on the specified port. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class SocketSecurityException extends ExportException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7622072999407781979L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public SocketSecurityException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public SocketSecurityException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/UID.java b/libjava/classpath/java/rmi/server/UID.java new file mode 100644 index 00000000000..0f492bae77a --- /dev/null +++ b/libjava/classpath/java/rmi/server/UID.java @@ -0,0 +1,127 @@ +/* UID.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; + +public final class UID implements Serializable +{ +private static final long serialVersionUID = 1086053664494604050L; + +private static final Object lock = UID.class; +private static long baseTime = System.currentTimeMillis(); +private static short nextCount = Short.MIN_VALUE; +// This is sun's algorithm - don't ask me why ... +private static final int uniqueNr = (new Object()).hashCode(); + +private int unique; +private long time; +private short count; + +/** + * This is sun's algorithm - don't ask me why ... + */ +public UID() { + synchronized (lock) { + if (nextCount == Short.MAX_VALUE) { + long newtime; + for (;;) { + newtime = System.currentTimeMillis(); + if (newtime - baseTime > 1000) { + break; + } + try { + Thread.sleep(1000); + } + catch (InterruptedException _) { + } + } + baseTime = newtime; + nextCount = Short.MIN_VALUE; + } + count = nextCount++; + unique = uniqueNr; + time = baseTime; + } +} + +public UID(short num) { + unique = (int)num; + time = 0; + count = 0; +} + +public int hashCode() { + return (unique); +} + +public boolean equals(Object obj) { + if (obj instanceof UID) { + UID uid = (UID)obj; + if (this.unique == uid.unique && + this.time == uid.time && + this.count == uid.count) { + return (true); + } + } + return (false); +} + +public String toString() { + return ("[UID: " + unique + "," + time + "," + count + "]"); +} + +public void write(DataOutput out) throws IOException { + out.writeInt(unique); + out.writeLong(time); + out.writeShort(count); +} + +public static UID read(DataInput in) throws IOException { + UID id = new UID(); + id.unique = in.readInt(); + id.time = in.readLong(); + id.count = in.readShort(); + return (id); +} + +} diff --git a/libjava/classpath/java/rmi/server/UnicastRemoteObject.java b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java new file mode 100644 index 00000000000..dbe25bda3fd --- /dev/null +++ b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java @@ -0,0 +1,133 @@ +/* UnicastRemoteObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.UnicastServerRef; + +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public class UnicastRemoteObject extends RemoteServer +{ +private static final long serialVersionUID = 4974527148936298033L; +//The following serialized fields are from Java API Documentation "Serialized form" +private int port = 0; +private RMIClientSocketFactory csf = null; +private RMIServerSocketFactory ssf = null; + +protected UnicastRemoteObject() throws RemoteException { + this(0); +} + +protected UnicastRemoteObject(int port) throws RemoteException { + this(port, RMISocketFactory.getSocketFactory(), RMISocketFactory.getSocketFactory()); +} + +protected UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { + this.port = port; + //Is RMIXXXSocketFactory serializable + //this.csf = csf; + //this.ssf = ssf; + this.ref = new UnicastServerRef(new ObjID(), port, ssf); + exportObject(this); +} + +protected UnicastRemoteObject(RemoteRef ref) throws RemoteException { + super((UnicastServerRef)ref); + exportObject(this); +} + +public Object clone() throws CloneNotSupportedException { + throw new Error("Not implemented"); +} + +public static RemoteStub exportObject(Remote obj) throws RemoteException { + UnicastServerRef sref = (UnicastServerRef)((RemoteObject)obj).getRef(); + return (sref.exportObject(obj)); +} + + public static Remote exportObject(Remote obj, int port) throws RemoteException + { + return exportObject(obj, port, null); + } + + static Remote exportObject(Remote obj, int port, RMIServerSocketFactory ssf) + throws RemoteException + { + UnicastServerRef sref = null; + if (obj instanceof RemoteObject) + sref = (UnicastServerRef)((RemoteObject)obj).getRef (); + if(sref == null) + { + sref = new UnicastServerRef(new ObjID (), port, ssf); + } + Remote stub = sref.exportObject (obj); + addStub(obj, stub); + return stub; + } + + /** + * FIXME + */ + public static Remote exportObject(Remote obj, int port, RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) + throws RemoteException + { + return (exportObject(obj, port, ssf)); + } + + public static boolean unexportObject(Remote obj, boolean force) + throws NoSuchObjectException + { + if (obj instanceof RemoteObject) + { + deleteStub(obj); + UnicastServerRef sref = (UnicastServerRef)((RemoteObject)obj).getRef(); + return sref.unexportObject(obj, force); + } + else + { + //FIX ME + ; + } + return true; + } + +} diff --git a/libjava/classpath/java/rmi/server/Unreferenced.java b/libjava/classpath/java/rmi/server/Unreferenced.java new file mode 100644 index 00000000000..982aa64458f --- /dev/null +++ b/libjava/classpath/java/rmi/server/Unreferenced.java @@ -0,0 +1,43 @@ +/* Unreferenced.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +public interface Unreferenced +{ + void unreferenced(); +} diff --git a/libjava/classpath/java/rmi/server/package.html b/libjava/classpath/java/rmi/server/package.html new file mode 100644 index 00000000000..54055a72158 --- /dev/null +++ b/libjava/classpath/java/rmi/server/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.rmi.server package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.rmi.server</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/security/AccessControlContext.java b/libjava/classpath/java/security/AccessControlContext.java new file mode 100644 index 00000000000..9a6ad208144 --- /dev/null +++ b/libjava/classpath/java/security/AccessControlContext.java @@ -0,0 +1,176 @@ +/* AccessControlContext.java --- Access Control Context Class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.HashSet; + +/** + * AccessControlContext makes system resource access decsion + * based on permission rights. + * + * It is used for a specific context and has only one method + * checkPermission. It is similar to AccessController except + * that it makes decsions based on the current context instead + * of the the current thread. + * + * It is created by call AccessController.getContext method. + * + * @author Mark Benvenuto + * @since 1.2 + */ +public final class AccessControlContext +{ + private final ProtectionDomain[] protectionDomains; + private final DomainCombiner combiner; + + /** + * Construct a new AccessControlContext with the specified + * ProtectionDomains. <code>context</code> must not be + * null and duplicates will be removed. + * + * @param context The ProtectionDomains to use + */ + public AccessControlContext(ProtectionDomain[] context) + { + HashSet domains = new HashSet (context.length); + for (int i = 0; i < context.length; i++) + domains.add (context[i]); + protectionDomains = (ProtectionDomain[]) + domains.toArray (new ProtectionDomain[domains.size()]); + combiner = null; + } + + /** + * Construct a new AccessControlContext with the specified + * ProtectionDomains and DomainCombiner + * + * @since 1.3 + */ + public AccessControlContext(AccessControlContext acc, + DomainCombiner combiner) + { + // XXX check permission to call this. + AccessControlContext acc2 = AccessController.getContext(); + protectionDomains = combiner.combine (acc2.protectionDomains, + acc.protectionDomains); + this.combiner = combiner; + } + + AccessControlContext (ProtectionDomain[] domains, AccessControlContext acc, + DomainCombiner combiner) + { + protectionDomains = combiner.combine (domains, acc.protectionDomains); + this.combiner = combiner; + } + + /** + * Returns the Domain Combiner associated with the AccessControlContext + * + * @return the DomainCombiner + */ + public DomainCombiner getDomainCombiner() + { + return combiner; + } + + /** + * Determines whether or not the specific permission is granted + * depending on the context it is within. + * + * @param perm a permission to check + * + * @throws AccessControlException if the permssion is not permitted + */ + public void checkPermission(Permission perm) throws AccessControlException + { + if (protectionDomains.length == 0) + throw new AccessControlException ("permission not granted"); + for (int i = 0; i < protectionDomains.length; i++) + if (!protectionDomains[i].implies(perm)) + throw new AccessControlException ("permission not granted"); + } + + /** + * Checks if two AccessControlContexts are equal. + * + * It first checks if obj is an AccessControlContext class, and + * then checks if each ProtectionDomain matches. + * + * @param obj The object to compare this class to + * + * @return true if equal, false otherwise + */ + public boolean equals(Object obj) + { + if (obj instanceof AccessControlContext) + { + AccessControlContext acc = (AccessControlContext) obj; + + if (acc.protectionDomains.length != protectionDomains.length) + return false; + + int i, j; + for (i = 0; i < protectionDomains.length; i++) + { + for (j = 0; j < acc.protectionDomains.length; j++) + { + if (acc.protectionDomains[j].equals (protectionDomains[i])) + break; + } + if (j == acc.protectionDomains.length) + return false; + } + return true; + } + return false; + } + + /** + * Computes a hash code of this class + * + * @return a hash code representing this class + */ + public int hashCode() + { + int h = 0; + for (int i = 0; i < protectionDomains.length; i++) + h ^= protectionDomains[i].hashCode(); + + return h; + } +} diff --git a/libjava/classpath/java/security/AccessControlException.java b/libjava/classpath/java/security/AccessControlException.java new file mode 100644 index 00000000000..27aee7c869a --- /dev/null +++ b/libjava/classpath/java/security/AccessControlException.java @@ -0,0 +1,97 @@ +/* AccessControlException.java -- Permission is denied + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the <code>AccessController</code> denies + * an attempt to perform an operation. This often keeps track of the + * permission that was not granted. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AccessController + * @status updated to 1.4 + */ +public class AccessControlException extends SecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5138225684096988535L; + + /** + * The <code>Permission</code> associated with this exception. + * + * @serial the permission + */ + private final Permission perm; + + /** + * Create a new instance with a descriptive error message, and a null + * <code>Permission</code> object. + * + * @param msg the descriptive error message + */ + public AccessControlException(String msg) + { + this(msg, null); + } + + /** + * Create a new instance with a descriptive error message and an associated + * <code>Permission</code> object. + * + * @param msg the descriptive error message + * @param perm the permission that caused this + */ + public AccessControlException(String msg, Permission perm) + { + super(msg); + this.perm = perm; + } + + /** + * This method returns the <code>Permission</code> object that caused + * this exception to be thrown. + * + * @return the denied permission, or null + */ + public Permission getPermission() + { + return perm; + } +} diff --git a/libjava/classpath/java/security/AccessController.java b/libjava/classpath/java/security/AccessController.java new file mode 100644 index 00000000000..bc9c2deefbb --- /dev/null +++ b/libjava/classpath/java/security/AccessController.java @@ -0,0 +1,221 @@ +/* AccessController.java --- Access control context and permission checker + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Access control context and permission checker. + * Can check permissions in the access control context of the current thread + * through the <code>checkPermission()</code> method. + * Manipulates the access control context for code that needs to be executed + * the protection domain of the calling class (by explicitly ignoring the + * context of the calling code) in the <code>doPrivileged()</code> methods. + * And provides a <code>getContext()</code> method which gives the access + * control context of the current thread that can be used for checking + * permissions at a later time and/or in another thread. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.2 + */ +public final class AccessController +{ + /** + * This class only has static methods so there is no public contructor. + */ + private AccessController() + { + } + + /** + * Checks wether the access control context of the current thread allows + * the given Permission. Throws an <code>AccessControlException</code> + * when the permission is not allowed in the current context. Otherwise + * returns silently without throwing an exception. + * + * @param perm the permission to be checked. + * @exception AccessControlException thrown if the current context does not + * allow the given permission. + */ + public static void checkPermission(Permission perm) + throws AccessControlException + { + getContext().checkPermission(perm); + } + + /** + * Calls the <code>run()</code> method of the given action with as + * (initial) access control context only the protection domain of the + * calling class. Calls to <code>checkPermission()</code> in the + * <code>run()</code> method ignore all earlier protection domains of + * classes in the call chain. Note that the protection domains of classes + * called by the code in the <code>run()</code> method are not ignored. + * + * @param action the <code>PrivilegedAction</code> whose <code>run()</code> + * should be be called. + * @return the result of the <code>action.run()</code> method. + */ + public static Object doPrivileged(PrivilegedAction action) + { + VMAccessController.pushContext(null); + try + { + return action.run(); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the <code>run()</code> method of the given action with as + * (initial) access control context the given context combined with the + * protection domain of the calling class. Calls to + * <code>checkPermission()</code> in the <code>run()</code> method ignore + * all earlier protection domains of classes in the call chain, but add + * checks for the protection domains given in the supplied context. + * + * @param action the <code>PrivilegedAction</code> whose <code>run()</code> + * should be be called. + * @param context the <code>AccessControlContext</code> whose protection + * domains should be added to the protection domain of the calling class. + * @return the result of the <code>action.run()</code> method. + */ + public static Object doPrivileged(PrivilegedAction action, + AccessControlContext context) + { + VMAccessController.pushContext(context); + try + { + return action.run(); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the <code>run()</code> method of the given action with as + * (initial) access control context only the protection domain of the + * calling class. Calls to <code>checkPermission()</code> in the + * <code>run()</code> method ignore all earlier protection domains of + * classes in the call chain. Note that the protection domains of classes + * called by the code in the <code>run()</code> method are not ignored. + * If the <code>run()</code> method throws an exception then this method + * will wrap that exception in an <code>PrivilegedActionException</code>. + * + * @param action the <code>PrivilegedExceptionAction</code> whose + * <code>run()</code> should be be called. + * @return the result of the <code>action.run()</code> method. + * @exception PrivilegedActionException wrapped around any exception that + * is thrown in the <code>run()</code> method. + */ + public static Object doPrivileged(PrivilegedExceptionAction action) + throws PrivilegedActionException + { + VMAccessController.pushContext(null); + try + { + return action.run(); + } + catch (Exception e) + { + throw new PrivilegedActionException(e); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the <code>run()</code> method of the given action with as + * (initial) access control context the given context combined with the + * protection domain of the calling class. Calls to + * <code>checkPermission()</code> in the <code>run()</code> method ignore + * all earlier protection domains of classes in the call chain, but add + * checks for the protection domains given in the supplied context. + * If the <code>run()</code> method throws an exception then this method + * will wrap that exception in an <code>PrivilegedActionException</code>. + * + * @param action the <code>PrivilegedExceptionAction</code> whose + * <code>run()</code> should be be called. + * @param context the <code>AccessControlContext</code> whose protection + * domains should be added to the protection domain of the calling class. + * @return the result of the <code>action.run()</code> method. + * @exception PrivilegedActionException wrapped around any exception that + * is thrown in the <code>run()</code> method. + */ + public static Object doPrivileged(PrivilegedExceptionAction action, + AccessControlContext context) + throws PrivilegedActionException + { + VMAccessController.pushContext(context); + try + { + return action.run(); + } + catch (Exception e) + { + throw new PrivilegedActionException(e); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Returns the complete access control context of the current thread. + * The returned object encompasses all {@link ProtectionDomain} objects + * for all classes in the current call stack, or the set of protection + * domains until the last call to {@link + * #doPrivileged(java.security.PrivilegedAction)}. + * + * <p>Additionally, if a call was made to {@link + * #doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)} + * that supplied an {@link AccessControlContext}, then that context + * will be intersected with the calculated one. + * + * @return The context. + */ + public static AccessControlContext getContext() + { + return VMAccessController.getContext(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParameterGenerator.java b/libjava/classpath/java/security/AlgorithmParameterGenerator.java new file mode 100644 index 00000000000..5dc9e3bb274 --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameterGenerator.java @@ -0,0 +1,302 @@ +/* AlgorithmParameterGenerator.java --- Algorithm Parameter Generator + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * <p>The <code>AlgorithmParameterGenerator</code> class is used to generate a + * set of parameters to be used with a certain algorithm. Parameter generators + * are constructed using the <code>getInstance()</code> factory methods (static + * methods that return instances of a given class).</p> + * + * <p>The object that will generate the parameters can be initialized in two + * different ways: in an algorithm-independent manner, or in an + * algorithm-specific manner:</p> + * + * <ul> + * <li>The algorithm-independent approach uses the fact that all parameter + * generators share the concept of a <i>"size"</i> and a <i>source of + * randomness</i>. The measure of <i>size</i> is universally shared by all + * algorithm parameters, though it is interpreted differently for different + * algorithms. For example, in the case of parameters for the <i>DSA</i> + * algorithm, <i>"size"</i> corresponds to the size of the prime modulus (in + * bits). When using this approach, algorithm-specific parameter generation + * values - if any - default to some standard values, unless they can be + * derived from the specified size.</li> + * <li>The other approach initializes a parameter generator object using + * algorithm-specific semantics, which are represented by a set of + * algorithm-specific parameter generation values. To generate Diffie-Hellman + * system parameters, for example, the parameter generation values usually + * consist of the size of the prime modulus and the size of the random + * exponent, both specified in number of bits.</li> + * </ul> + * + * <p>In case the client does not explicitly initialize the + * <code>AlgorithmParameterGenerator</code> (via a call to an <code>init()</code> + * method), each provider must supply (and document) a default initialization. + * For example, the <b>GNU</b> provider uses a default modulus prime size of + * <code>1024</code> bits for the generation of <i>DSA</i> parameters. + * + * @author Mark Benvenuto + * @since 1.2 + * @see AlgorithmParameters + * @see AlgorithmParameterSpec + */ +public class AlgorithmParameterGenerator +{ + /** Service name for algorithm parameter generators. */ + private static final String ALGORITHM_PARAMETER_GENERATOR = + "AlgorithmParameterGenerator"; + + private AlgorithmParameterGeneratorSpi paramGenSpi; + private Provider provider; + private String algorithm; + + /** + * Creates an <code>AlgorithmParameterGenerator</code> object. + * + * @param paramGenSpi the delegate. + * @param provider the provider. + * @param algorithm the algorithm. + */ + protected AlgorithmParameterGenerator(AlgorithmParameterGeneratorSpi + paramGenSpi, Provider provider, + String algorithm) + { + this.paramGenSpi = paramGenSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Returns the standard name of the algorithm this parameter generator is + * associated with. + * + * @return the string name of the algorithm. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Generates an <code>AlgorithmParameterGenerator</code> object that + * implements the specified digest algorithm. If the default provider package + * provides an implementation of the requested digest algorithm, an instance + * of <code>AlgorithmParameterGenerator</code> containing that implementation + * is returned. If the algorithm is not available in the default package, + * other packages are searched. + * + * @param algorithm the string name of the algorithm this parameter generator + * is associated with. + * @return the new <code>AlgorithmParameterGenerator</code> object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * environment. + */ + public static AlgorithmParameterGenerator getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignore. + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Generates an <code>AlgorithmParameterGenerator</code> object for the + * requested algorithm, as supplied from the specified provider, if such a + * parameter generator is available from the provider. + * + * @param algorithm the string name of the algorithm. + * @param provider the string name of the provider. + * @return the new <code>AlgorithmParameterGenerator</code> object. + * @throws NoSuchAlgorithmException if the <code>algorithm</code> is not + * available from the <code>provider</code>. + * @throws NoSuchProviderException if the <code>provider</code> is not + * available in the environment. + * @throws IllegalArgumentException if the <code>provider</code> name is + * <code>null</code> or empty. + * @see Provider + */ + public static AlgorithmParameterGenerator getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates an AlgorithmParameterGenerator object for the requested + * algorithm, as supplied from the specified provider, if such a parameter + * generator is available from the provider. Note: the <code>provider</code> + * doesn't have to be registered. + * + * @param algorithm the string name of the algorithm. + * @param provider the provider. + * @return the new AlgorithmParameterGenerator object. + * @throws NoSuchAlgorithmException if the algorithm is not available from + * the provider. + * @throws IllegalArgumentException if the provider is null. + * @since 1.4 + * @see Provider + */ + public static AlgorithmParameterGenerator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + try + { + return new AlgorithmParameterGenerator( + (AlgorithmParameterGeneratorSpi) Engine.getInstance( + ALGORITHM_PARAMETER_GENERATOR, algorithm, provider), + provider, algorithm); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + /** + * Returns the provider of this algorithm parameter generator object. + * + * @return the provider of this algorithm parameter generator object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this parameter generator for a certain <i>size</i>. To create + * the parameters, the {@link SecureRandom} implementation of the + * highest-priority installed provider is used as the source of randomness. + * (If none of the installed providers supply an implementation of + * {@link SecureRandom}, a system-provided source of randomness is used.) + * + * @param size the size (number of bits). + */ + public final void init(int size) + { + init(size, new SecureRandom()); + } + + /** + * Initializes this parameter generator for a certain size and source of + * randomness. + * + * @param size the size (number of bits). + * @param random the source of randomness. + */ + public final void init(int size, SecureRandom random) + { + paramGenSpi.engineInit(size, random); + } + + /** + * Initializes this parameter generator with a set of algorithm-specific + * parameter generation values. To generate the parameters, the {@link + * SecureRandom} implementation of the highest-priority installed provider is + * used as the source of randomness. (If none of the installed providers + * supply an implementation of {@link SecureRandom}, a system-provided source + * of randomness is used.) + * + * @param genParamSpec the set of algorithm-specific parameter generation + * values. + * @throws InvalidAlgorithmParameterException if the given parameter + * generation values are inappropriate for this parameter generator. + */ + public final void init(AlgorithmParameterSpec genParamSpec) + throws InvalidAlgorithmParameterException + { + init(genParamSpec, new SecureRandom()); + } + + /** + * Initializes this parameter generator with a set of algorithm-specific + * parameter generation values. + * + * @param genParamSpec the set of algorithm-specific parameter generation + * values. + * @param random the source of randomness. + * @throws InvalidAlgorithmParameterException if the given parameter + * generation values are inappropriate for this parameter generator. + */ + public final void init(AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + paramGenSpi.engineInit(genParamSpec, random); + } + + /** + * Generates the parameters. + * + * @return the new {@link AlgorithmParameters} object. + */ + public final AlgorithmParameters generateParameters() + { + return paramGenSpi.engineGenerateParameters(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java b/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 00000000000..3143ea76b8b --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,94 @@ +/* AlgorithmParameterGeneratorSpi.java --- Algorithm Parameter Generator SPI + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.security.spec.AlgorithmParameterSpec; + +/** + AlgorithmParameterGeneratorSpi is the Service Provider + Interface for the AlgorithmParameterGenerator class. + This class is used to generate the algorithm parameters + for a specific algorithm. + + @since JDK 1.2 + @author Mark Benvenuto + */ +public abstract class AlgorithmParameterGeneratorSpi +{ + + /** + Constructs a new AlgorithmParameterGeneratorSpi + */ + public AlgorithmParameterGeneratorSpi() + { + } + + /** + Initializes the parameter generator with the specified size + and SecureRandom + + @param size the size( in number of bits) + @param random the SecureRandom class to use for randomness + */ + protected abstract void engineInit(int size, SecureRandom random); + + /** + Initializes the parameter generator with the specified + AlgorithmParameterSpec and SecureRandom classes. + + If genParamSpec is an invalid AlgorithmParameterSpec for this + AlgorithmParameterGeneratorSpi then it throws + InvalidAlgorithmParameterException + + @param genParamSpec the AlgorithmParameterSpec class to use + @param random the SecureRandom class to use for randomness + + @throws InvalidAlgorithmParameterException genParamSpec is invalid + */ + protected abstract void engineInit(AlgorithmParameterSpec genParamSpec, + SecureRandom random) throws + InvalidAlgorithmParameterException; + + + /** + Generate a new set of AlgorithmParameters. + + @returns a new set of algorithm parameters + */ + protected abstract AlgorithmParameters engineGenerateParameters(); + +} diff --git a/libjava/classpath/java/security/AlgorithmParameters.java b/libjava/classpath/java/security/AlgorithmParameters.java new file mode 100644 index 00000000000..038fbb4bd64 --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameters.java @@ -0,0 +1,340 @@ +/* AlgorithmParameters.java --- Algorithm Parameters Implementation Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * <p>This class is used as an opaque representation of cryptographic + * parameters.</p> + * + * <p>An <code>AlgorithmParameters</code> object for managing the parameters + * for a particular algorithm can be obtained by calling one of the + * <code>getInstance()</code> factory methods (static methods that return + * instances of a given class).</p> + * + * <p>There are two ways to request such an implementation: by specifying + * either just an algorithm name, or both an algorithm name and a package + * provider.</p> + * + * <ul> + * <li>If just an algorithm name is specified, the system will determine if + * there is an AlgorithmParameters implementation for the algorithm requested + * available in the environment, and if there is more than one, if there is + * a preferred one.</li> + * <li>If both an algorithm name and a package provider are specified, the + * system will determine if there is an implementation in the package + * requested, and throw an exception if there is not.</li> + * </ul> + * + * <p>Once an <code>AlgorithmParameters</code> object is returned, it must be + * initialized via a call to <code>init()</code>, using an appropriate + * parameter specification or parameter encoding.</p> + * + * <p>A transparent parameter specification is obtained from an + * <code>AlgorithmParameters</code> object via a call to + * <code>getParameterSpec()</code>, and a byte encoding of the parameters is + * obtained via a call to <code>getEncoded()</code>.</p> + * + * @author Mark Benvenuto + * @since 1.2 + * @see AlgorithmParameterSpec + * @see java.security.spec.DSAParameterSpec + * @see KeyPairGenerator + */ +public class AlgorithmParameters +{ + /** Service name for algorithm parameters. */ + private static final String ALGORITHM_PARAMETERS = "AlgorithmParameters"; + + private AlgorithmParametersSpi paramSpi; + private Provider provider; + private String algorithm; + + /** + * Creates an <code>AlgorithmParameters</code> object. + * + * @param paramSpi the delegate. + * @param provider the provider. + * @param algorithm the algorithm. + */ + protected AlgorithmParameters(AlgorithmParametersSpi paramSpi, + Provider provider, String algorithm) + { + this.paramSpi = paramSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Returns the name of the algorithm associated with this parameter object. + * + * @return the algorithm name. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * <p>Generates a parameter object for the specified algorithm.</p> + * + * <p>If the default provider package provides an implementation of the + * requested algorithm, an instance of <code>AlgorithmParameters</code> + * containing that implementation is returned. If the algorithm is not + * available in the default package, other packages are searched.</p> + * + * <p>The returned parameter object must be initialized via a call to + * <code>init()</code>, using an appropriate parameter specification or + * parameter encoding.</p> + * + * @param algorithm the name of the algorithm requested. + * @return the new parameter object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * environment. + */ + public static AlgorithmParameters getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + + for (int i = 0; i < p.length; i++) + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignore this. + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * <p>Generates a parameter object for the specified algorithm, as supplied + * by the specified provider, if such an algorithm is available from the + * provider.</p> + * + * <p>The returned parameter object must be initialized via a call to + * <code>init()</code>, using an appropriate parameter specification or + * parameter encoding.</p> + * + * @param algorithm the name of the algorithm requested. + * @param provider the name of the provider. + * @return the new parameter object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * package supplied by the requested provider. + * @throws NoSuchProviderException if the provider is not available in the + * environment. + * @throws IllegalArgumentException if the provider name is null or empty. + * @see Provider + */ + public static AlgorithmParameters getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates an <code>AlgorithmParameterGenerator</code> object for the + * requested algorithm, as supplied from the specified provider, if such a + * parameter generator is available from the provider. Note: the + * <code>provider</code> doesn't have to be registered. + * + * @param algorithm the string name of the algorithm. + * @param provider the provider. + * @return the new <code>AlgorithmParameterGenerator</code> object. + * @throws NoSuchAlgorithmException if the <code>algorithm</code> is not + * available from the <code>provider</code>. + * @throws IllegalArgumentException if the <code>provider</code> is + * <code>null</code>. + * @since 1.4 + */ + public static AlgorithmParameters getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + try + { + return new AlgorithmParameters((AlgorithmParametersSpi) + Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider), + provider, algorithm); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + /** + * Returns the provider of this parameter object. + * + * @return the provider of this parameter object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this parameter object using the parameters specified in + * <code>paramSpec</code>. + * + * @param paramSpec the parameter specification. + * @throws InvalidParameterSpecException if the given parameter specification + * is inappropriate for the initialization of this parameter object, or if + * this parameter object has already been initialized. + */ + public final void init(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + paramSpi.engineInit(paramSpec); + } + + /** + * Imports the specified parameters and decodes them according to the primary + * decoding format for parameters. The primary decoding format for parameters + * is ASN.1, if an ASN.1 specification for this type of parameters exists. + * + * @param params the encoded parameters. + * @throws IOException on decoding errors, or if this parameter object has + * already been initialized. + */ + public final void init(byte[]params) throws IOException + { + paramSpi.engineInit(params); + } + + /** + * Imports the parameters from params and decodes them according to the + * specified decoding scheme. If <code>format</code> is <code>null</code>, + * the primary decoding format for parameters is used. The primary decoding + * format is ASN.1, if an ASN.1 specification for these parameters exists. + * + * @param params the encoded parameters. + * @param format the name of the decoding scheme. + * @throws IOException on decoding errors, or if this parameter object has + * already been initialized. + */ + public final void init(byte[]params, String format) throws IOException + { + paramSpi.engineInit(params, format); + } + + /** + * Returns a (transparent) specification of this parameter object. + * <code>paramSpec</code> identifies the specification class in which the + * parameters should be returned. It could, for example, be + * <code>DSAParameterSpec.class</code>, to indicate that the parameters should + * be returned in an instance of the {@link java.security.spec.DSAParameterSpec} + * class. + * + * @param paramSpec the specification class in which the parameters should be + * returned. + * @return the parameter specification. + * @throws InvalidParameterSpecException if the requested parameter + * specification is inappropriate for this parameter object, or if this + * parameter object has not been initialized. + */ + public final AlgorithmParameterSpec getParameterSpec(Class paramSpec) + throws InvalidParameterSpecException + { + return paramSpi.engineGetParameterSpec(paramSpec); + } + + /** + * Returns the parameters in their primary encoding format. The primary + * encoding format for parameters is ASN.1, if an ASN.1 specification for + * this type of parameters exists. + * + * @return the parameters encoded using their primary encoding format. + * @throws IOException on encoding errors, or if this parameter object has not + * been initialized. + */ + public final byte[] getEncoded() throws IOException + { + return paramSpi.engineGetEncoded(); + } + + /** + * Returns the parameters encoded in the specified scheme. If format is + * <code>null</code>, the primary encoding format for parameters is used. The + * primary encoding format is ASN.1, if an ASN.1 specification for these + * parameters exists. + * + * @param format the name of the encoding format. + * @return the parameters encoded using the specified encoding scheme. + * @throws IOException on encoding errors, or if this parameter object has + * not been initialized. + */ + public final byte[] getEncoded(String format) throws IOException + { + return paramSpi.engineGetEncoded(format); + } + + /** + * Returns a formatted string describing the parameters. + * + * @return a formatted string describing the parameters, or <code>null</code> + * if this parameter object has not been initialized. + */ + public final String toString() + { + return paramSpi.engineToString(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParametersSpi.java b/libjava/classpath/java/security/AlgorithmParametersSpi.java new file mode 100644 index 00000000000..a9faa154374 --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParametersSpi.java @@ -0,0 +1,149 @@ +/* AlgorithmParametersSpi.java --- Algorithm Parameters SPI + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * AlgorithmParametersSpi is the Service Provider Interface + * for the Algorithm Parameters class. This class is used + * to manage the algorithm parameters. + * + * @since 1.2 + * @author Mark Benvenuto + */ +public abstract class AlgorithmParametersSpi +{ + /** + * Creates a new instance of AlgorithmParametersSpi + */ + public AlgorithmParametersSpi() + { + } + + /** + * Initializes the engine with the specified + * AlgorithmParameterSpec class. + * + * @param paramSpec A AlgorithmParameterSpec to initialize with + * + * @throws InvalidParameterSpecException For an inapporiate + * ParameterSpec class + */ + protected abstract void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException; + + /** + * Initializes the engine with the specified + * parameters stored in the byte array and decodes them + * according to the ASN.1 specification. If the ASN.1 + * specification exists then it succeeds or else it throws + * IOException. + * + * @param params Parameters to initialize with + * + * @throws IOException Decoding Error + */ + protected abstract void engineInit(byte[]params) throws IOException; + + /** + * Initializes the engine with the specified + * parameters stored in the byte array and decodes them + * according to the specified decoding specification. + * If format is null, then it is decoded using the ASN.1 + * specification if it exists or else it throws + * IOException. + * + * @param params Parameters to initialize with + * @param format Name of decoding format to use + * + * @throws IOException Decoding Error + */ + protected abstract void engineInit(byte[]params, String format) + throws IOException; + + + /** + * Returns a specification of this AlgorithmParameters object. + * paramSpec identifies the class to return the AlgortihmParameters + * in. + * + * @param paramSpec Class to return AlgorithmParameters in + * + * @return the parameter specification + * + * @throws InvalidParameterSpecException if the paramSpec is an + * invalid parameter class + */ + protected abstract AlgorithmParameterSpec engineGetParameterSpec(Class + paramSpec) + throws InvalidParameterSpecException; + + + /** + * Returns the parameters in the default encoding format. + * The primary encoding format is ASN.1 format if it exists + * for the specified type. + * + * @return byte array representing the parameters + */ + protected abstract byte[] engineGetEncoded() throws IOException; + + + /** + * Returns the parameters in the specified encoding format. + * If <code>format</code> is <code>null</code> then the + * primary encoding format is used, the ASN.1 format, + * if it exists for the specified type. + * + * @return byte array representing the parameters + */ + protected abstract byte[] engineGetEncoded(String format) + throws IOException; + + /** + * Returns a string describing the parameters in the + * AlgorithmParametersSpi class. + * + * @return A string representing the format of the parameters. + */ + protected abstract String engineToString(); +} diff --git a/libjava/classpath/java/security/AllPermission.java b/libjava/classpath/java/security/AllPermission.java new file mode 100644 index 00000000000..6adcd8c9c96 --- /dev/null +++ b/libjava/classpath/java/security/AllPermission.java @@ -0,0 +1,198 @@ +/* AllPermission.java -- Permission to do anything + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.util.EmptyEnumeration; + +import java.util.Collections; +import java.util.Enumeration; + +/** + * This class is a permission that implies all other permissions. Granting + * this permission effectively grants all others. Extreme caution should + * be exercised in granting this permission. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see AccessController + * @see Permissions + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public final class AllPermission extends Permission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2916474571451318075L; + + /** + * Create a new AllPermission object. + */ + public AllPermission() + { + super("*"); + } + + /** + * Create a new AllPermission object. The parameters are ignored, as all + * permission implies ALL PERMISSION. + * + * @param name ignored + * @param actions ignored + */ + public AllPermission(String name, String actions) + { + super("*"); + } + + /** + * This method always returns <code>true</code> to indicate that this + * permission always implies that any other permission is also granted. + * + * @param perm ignored + * @return true, the permission is implied + */ + public boolean implies(Permission perm) + { + return true; + } + + /** + * Checks an object for equality. All AllPermissions are equal. + * + * @param obj the <code>Object</code> to test for equality + */ + public boolean equals(Object obj) + { + return obj instanceof AllPermission; + } + + /** + * This method returns a hash code for this object. This returns 1. + * + * @return a hash value for this object + */ + public int hashCode() + { + return 1; + } + + /** + * This method returns the list of actions associated with this object. + * This will always be the empty string ("") for this class. + * + * @return the action list + */ + public String getActions() + { + return ""; + } + + /** + * Returns a PermissionCollection which can hold AllPermission. + * + * @return a permission collection + */ + public PermissionCollection newPermissionCollection() + { + return new AllPermissionCollection(); + } + + /** + * Implements AllPermission.newPermissionCollection, and obeys serialization + * of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class AllPermissionCollection extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4023755556366636806L; + + /** + * Whether an AllPermission has been added to the collection. + * + * @serial if all permission is in the collection yet + */ + private boolean all_allowed; + + /** + * Add an AllPermission. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not an AllPermission + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException(); + if (! (perm instanceof AllPermission)) + throw new IllegalArgumentException(); + all_allowed = true; + } + + /** + * Returns true if this collection implies a permission. + * + * @param perm the permission to check + * @return true if this collection contains an AllPermission + */ + public boolean implies(Permission perm) + { + return all_allowed; + } + + /** + * Returns an enumeration of the elements in the collection. + * + * @return the elements in the collection + */ + public Enumeration elements() + { + return all_allowed + ? Collections.enumeration(Collections.singleton(new AllPermission())) + : EmptyEnumeration.getInstance(); + } + } // class AllPermissionCollection +} // class AllPermission diff --git a/libjava/classpath/java/security/BasicPermission.java b/libjava/classpath/java/security/BasicPermission.java new file mode 100644 index 00000000000..267a6e292ab --- /dev/null +++ b/libjava/classpath/java/security/BasicPermission.java @@ -0,0 +1,308 @@ +/* BasicPermission.java -- implements a simple named permission + Copyright (C) 1998, 1999, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * This class implements a simple model for named permissions without an + * associated action list. That is, either the named permission is granted + * or it is not. + * + * <p>It also supports trailing wildcards to allow the easy granting of + * permissions in a hierarchical fashion. (For example, the name "org.gnu.*" + * might grant all permissions under the "org.gnu" permissions hierarchy). + * The only valid wildcard character is a '*' which matches anything. It + * must be the rightmost element in the permission name and must follow a + * '.' or else the Permission name must consist of only a '*'. Any other + * occurrence of a '*' is not valid. + * + * <p>This class ignores the action list. Subclasses can choose to implement + * actions on top of this class if desired. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Permission + * @see Permissions + * @see PermissionCollection + * @see RuntimePermission + * @see SecurityPermission + * @see PropertyPermission + * @see AWTPermission + * @see NetPermission + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class BasicPermission extends java.security.Permission + implements Serializable + // FIXME extends with fully qualified classname as workaround for gcj 3.3. +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6279438298436773498L; + + /** + * Create a new instance with the specified permission name. If the + * name is empty an exception is thrown. + * + * @param name the name of this permission + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public BasicPermission(String name) + { + super(name); + + // This routine used to check for illegal wildcards, but no such + // requirement exists in the specification and Sun's runtime + // doesn't appear to do it. + + if ("".equals(name)) + throw new IllegalArgumentException("Empty name"); + } + + /** + * Create a new instance with the specified permission name. If the name + * is empty, or contains an illegal wildcard character, an exception is + * thrown. The actions parameter is ignored. + * + * @param name the name of this permission + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public BasicPermission(String name, String actions) + { + this(name); + } + + /** + * This method tests to see if the specified permission is implied by this + * permission. This will be true if the following conditions are met:<ul> + * <li>The specified object is an instance of the same class as this + * object.</li> + * <li>The name of the specified permission is implied by this permission's + * name based on wildcard matching. For example, "a.*" implies "a.b".</li> + * </ul> + * + * @param perm the <code>Permission</code> object to test against + * @return true if the specified permission is implied + */ + public boolean implies(Permission perm) + { + if (! getClass().isInstance(perm)) + return false; + + String otherName = perm.getName(); + String name = getName(); + + if (name.equals(otherName)) + return true; + + int last = name.length() - 1; + return name.charAt(last) == '*' + && otherName.startsWith(name.substring(0, last)); + } + + /** + * This method tests to see if this object is equal to the specified + * <code>Object</code>. This will be true if and only if the specified + * object meets the following conditions:<ul> + * <li>It is an instance of the same class as this.</li> + * <li>It has the same name as this permission.</li> + * </ul> + * + * @param obj the <code>Object</code> to test for equality + * @return true if obj is semantically equal to this + */ + public boolean equals(Object obj) + { + return getClass().isInstance(obj) + && getName().equals(((BasicPermission) obj).getName()); + } + + /** + * This method returns a hash code for this permission object. The hash + * code returned is the value returned by calling the <code>hashCode</code> + * method on the <code>String</code> that is the name of this permission. + * + * @return a hash value for this object + */ + public int hashCode() + { + return getName().hashCode(); + } + + /** + * This method returns a list of the actions associated with this + * permission. This method always returns the empty string ("") since + * this class ignores actions. + * + * @return the action list + */ + public String getActions() + { + return ""; + } + + /** + * This method returns an instance of <code>PermissionCollection</code> + * suitable for storing <code>BasicPermission</code> objects. The + * collection returned can only store objects of the same type as this. + * Subclasses which use actions must override this method; but a class with + * no actions will work fine with this. + * + * @return a new empty <code>PermissionCollection</code> object + */ + public PermissionCollection newPermissionCollection() + { + return new BasicPermissionCollection(getClass()); + } + + /** + * Implements AllPermission.newPermissionCollection, and obeys serialization + * of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class BasicPermissionCollection extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 739301742472979399L; + + /** + * The permissions in the collection. + * + * @serial a hash mapping name to permissions, all of type permClass + */ + private final Hashtable permissions = new Hashtable(); + + /** + * If "*" is in the collection. + * + * @serial true if a permission named "*" is in the collection + */ + private boolean all_allowed; + + /** + * The runtime class which all entries in the table must belong to. + * + * @serial the limiting subclass of this collection + */ + private final Class permClass; + + /** + * Construct a collection over the given runtime class. + * + * @param c the class + */ + BasicPermissionCollection(Class c) + { + permClass = c; + } + + /** + * Add a Permission. It must be of the same type as the permission which + * created this collection. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not the correct type + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException("readonly"); + if (! permClass.isInstance(perm)) + throw new IllegalArgumentException("Expecting instance of " + permClass); + BasicPermission bp = (BasicPermission) perm; + String name = bp.getName(); + if (name.equals("*")) + all_allowed = true; + permissions.put(name, bp); + } + + /** + * Returns true if this collection implies the given permission. + * + * @param permission the permission to check + * @return true if it is implied by this + */ + public boolean implies(Permission permission) + { + if (! permClass.isInstance(permission)) + return false; + if (all_allowed) + return true; + BasicPermission toImply = (BasicPermission) permission; + String name = toImply.getName(); + if (name.equals("*")) + return false; + int prefixLength = name.length(); + if (name.endsWith("*")) + prefixLength -= 2; + + while (true) + { + if (permissions.get(name) != null) + return true; + prefixLength = name.lastIndexOf('.', prefixLength); + if (prefixLength < 0) + return false; + name = name.substring(0, prefixLength + 1) + '*'; + } + } + + /** + * Enumerate over the collection. + * + * @return an enumeration of the collection contents + */ + public Enumeration elements() + { + return permissions.elements(); + } + } // class BasicPermissionCollection +} // class BasicPermission diff --git a/libjava/classpath/java/security/Certificate.java b/libjava/classpath/java/security/Certificate.java new file mode 100644 index 00000000000..5cdba6e101b --- /dev/null +++ b/libjava/classpath/java/security/Certificate.java @@ -0,0 +1,125 @@ +/* Certificate.java -- deprecated interface for modeling digital certificates + Copyright (C) 1998, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This interface models a digital certificate which verifies the + * authenticity of a party. This class simply allows certificate + * information to be queried, it does not guarantee that the certificate + * is valid. + * + * <p>This class is deprecated in favor of the new java.security.cert package. + * It exists for backward compatibility only. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @deprecated use {@link java.security.cert} instead + * @status updated to 1.4 + */ +public interface Certificate +{ + /** + * This method returns the <code>Principal</code> that is guaranteeing + * this certificate. + * + * @return the <code>Principal</code> guaranteeing the certificate + */ + Principal getGuarantor(); + + /** + * This method returns the <code>Principal</code> being guaranteed by + * this certificate. + * + * @return the <code>Principal</code> guaranteed by this certificate + */ + Principal getPrincipal(); + + /** + * This method returns the public key for the <code>Principal</code> that + * is being guaranteed. + * + * @return the <code>PublicKey</code> of the Principal being guaranteed + */ + PublicKey getPublicKey(); + + /** + * This method writes the certificate to an <code>OutputStream</code> in + * a format that can be understood by the <code>decode</code> method. + * + * @param out the <code>OutputStream</code> to write to + * @throws KeyException if there is a problem with the certificate + * @throws IOException if an error occurs writing to the stream + * @see #decode(InputStream) + * @see #getFormat() + */ + void encode(OutputStream out) throws KeyException, IOException; + + /** + * This method reads an encoded certificate from an <code>InputStream</code>. + * + * @param in the <code>InputStream</code> to read from + * @throws KeyException if there is a problem with the certificate data + * @throws IOException if an error occurs reading from the stream + * @see #encode(OutputStream) + * @see #getFormat() + */ + void decode(InputStream in) throws KeyException, IOException; + + /** + * This method returns the encoding format of the certificate (e.g., "PGP", + * "X.509"). This format is used by the <code>encode</code> and + * <code>decode</code> methods. + * + * @return the encoding format being used + */ + String getFormat(); + + /** + * This method returns a <code>String</code> representation of the contents + * of this certificate. + * + * @param detail true to provided more detailed information + * @return the string representation + */ + String toString(boolean detail); +} // interface Certificate diff --git a/libjava/classpath/java/security/CodeSource.java b/libjava/classpath/java/security/CodeSource.java new file mode 100644 index 00000000000..b516170281c --- /dev/null +++ b/libjava/classpath/java/security/CodeSource.java @@ -0,0 +1,354 @@ +/* CodeSource.java -- Code location and certifcates + Copyright (C) 1998, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.net.SocketPermission; +import java.net.URL; +// Note that this overrides Certificate in this package. +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; + +/** + * This class represents a location from which code is loaded (as + * represented by a URL), and the list of certificates that are used to + * check the signatures of signed code loaded from this source. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class CodeSource implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4977541819976013951L; + + /** + * This is the URL that represents the code base from which code will + * be loaded. + * + * @serial the code location + */ + private final URL location; + + /** The set of certificates for this code base. */ + private transient HashSet certs; + + /** + * This creates a new instance of <code>CodeSource</code> that loads code + * from the specified URL location and which uses the specified certificates + * for verifying signatures. + * + * @param location the location from which code will be loaded + * @param certs the list of certificates + */ + public CodeSource(URL location, Certificate[] certs) + { + this.location = location; + if (certs != null) + this.certs = new HashSet(Arrays.asList(certs)); + } + + /** + * This method returns a hash value for this object. + * + * @return a hash value for this object + */ + public int hashCode() + { + return (location == null ? 0 : location.hashCode()) + ^ (certs == null ? 0 : certs.hashCode()); + } + + /** + * This method tests the specified <code>Object</code> for equality with + * this object. This will be true if and only if the locations are equal + * and the certificate sets are identical (ignoring order). + * + * @param obj the <code>Object</code> to test against + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof CodeSource)) + return false; + CodeSource cs = (CodeSource) obj; + return (certs == null ? cs.certs == null : certs.equals(cs.certs)) + && (location == null ? cs.location == null + : location.equals(cs.location)); + } + + /** + * This method returns the URL specifying the location from which code + * will be loaded under this <code>CodeSource</code>. + * + * @return the code location for this <code>CodeSource</code> + */ + public final URL getLocation() + { + return location; + } + + /** + * This method returns the list of digital certificates that can be used + * to verify the signatures of code loaded under this + * <code>CodeSource</code>. + * + * @return the certifcate list for this <code>CodeSource</code> + */ + public final Certificate[] getCertificates() + { + if (certs == null) + return null; + Certificate[] c = new Certificate[certs.size()]; + certs.toArray(c); + return c; + } + + /** + * This method tests to see if a specified <code>CodeSource</code> is + * implied by this object. Effectively, to meet this test, the specified + * object must have all the certifcates this object has (but may have more), + * and must have a location that is a subset of this object's. In order + * for this object to imply the specified object, the following must be + * true: + * + * <ol> + * <li><em>codesource</em> must not be <code>null</code>.</li> + * <li>If <em>codesource</em> has a certificate list, all of it's + * certificates must be present in the certificate list of this + * code source.</li> + * <li>If this object does not have a <code>null</code> location, then + * the following addtional tests must be passed. + * + * <ol> + * <li><em>codesource</em> must not have a <code>null</code> + * location.</li> + * <li><em>codesource</em>'s location must be equal to this object's + * location, or + * <ul> + * <li><em>codesource</em>'s location protocol, port, and ref (aka, + * anchor) must equal this objects</li> + * <li><em>codesource</em>'s location host must imply this object's + * location host, as determined by contructing + * <code>SocketPermission</code> objects from each with no + * action list and using that classes's <code>implies</code> + * method</li> + * <li>If this object's location file ends with a '/', then the + * specified object's location file must start with this + * object's location file. Otherwise, the specified object's + * location file must start with this object's location file + * with the '/' character appended to it.</li> + * </ul></li> + * </ol></li> + * </ol> + * + * <p>For example, each of these locations imply the location + * "http://java.sun.com/classes/foo.jar":</p> + * + * <pre> + * http: + * http://*.sun.com/classes/* + * http://java.sun.com/classes/- + * http://java.sun.com/classes/foo.jar + * </pre> + * + * <p>Note that the code source with null location and null certificates implies + * all other code sources.</p> + * + * @param cs the <code>CodeSource</code> to test against this object + * @return true if this specified <code>CodeSource</code> is implied + */ + public boolean implies(CodeSource cs) + { + if (cs == null) + return false; + // First check the certificate list. + if (certs != null && (cs.certs == null || ! certs.containsAll(cs.certs))) + return false; + // Next check the location. + if (location == null) + return true; + if (cs.location == null + || ! location.getProtocol().equals(cs.location.getProtocol()) + || (location.getPort() != -1 + && location.getPort() != cs.location.getPort()) + || (location.getRef() != null + && ! location.getRef().equals(cs.location.getRef()))) + return false; + if (location.getHost() != null) + { + String their_host = cs.location.getHost(); + if (their_host == null) + return false; + SocketPermission our_sockperm = + new SocketPermission(location.getHost(), "accept"); + SocketPermission their_sockperm = + new SocketPermission(their_host, "accept"); + if (! our_sockperm.implies(their_sockperm)) + return false; + } + String our_file = location.getFile(); + if (our_file != null) + { + if (! our_file.endsWith("/")) + our_file += "/"; + String their_file = cs.location.getFile(); + if (their_file == null + || ! their_file.startsWith(our_file)) + return false; + } + return true; + } + + /** + * This method returns a <code>String</code> that represents this object. + * The result is in the format <code>"(" + getLocation()</code> followed + * by a space separated list of certificates (or "<no certificates>"), + * followed by <code>")"</code>. + * + * @return a <code>String</code> for this object + */ + public String toString() + { + StringBuffer sb = new StringBuffer("(").append(location); + if (certs == null || certs.isEmpty()) + sb.append(" <no certificates>"); + else + { + Iterator iter = certs.iterator(); + for (int i = certs.size(); --i >= 0; ) + sb.append(' ').append(iter.next()); + } + return sb.append(")").toString(); + } + + /** + * Reads this object from a serialization stream. + * + * @param s the input stream + * @throws IOException if reading fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads the location, then expects an int indicating the + * number of certificates. Each certificate is a String type + * followed by an int encoding length, then a byte[] encoding + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int count = s.readInt(); + certs = new HashSet(); + while (--count >= 0) + { + String type = (String) s.readObject(); + int bytes = s.readInt(); + byte[] encoded = new byte[bytes]; + for (int i = 0; i < bytes; i++) + encoded[i] = s.readByte(); + ByteArrayInputStream stream = new ByteArrayInputStream(encoded); + try + { + CertificateFactory factory = CertificateFactory.getInstance(type); + certs.add(factory.generateCertificate(stream)); + } + catch (CertificateException e) + { + // XXX Should we ignore this certificate? + } + } + } + + /** + * Writes this object to a serialization stream. + * + * @param s the output stream + * @throws IOException if writing fails + * @serialData this writes the location, then writes an int indicating the + * number of certificates. Each certificate is a String type + * followed by an int encoding length, then a byte[] encoding + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (certs == null) + s.writeInt(0); + else + { + int count = certs.size(); + s.writeInt(count); + Iterator iter = certs.iterator(); + while (--count >= 0) + { + Certificate c = (Certificate) iter.next(); + s.writeObject(c.getType()); + byte[] encoded; + try + { + encoded = c.getEncoded(); + } + catch (CertificateEncodingException e) + { + // XXX Should we ignore this certificate? + encoded = null; + } + if (encoded == null) + s.writeInt(0); + else + { + s.writeInt(encoded.length); + for (int i = 0; i < encoded.length; i++) + s.writeByte(encoded[i]); + } + } + } + } +} // class CodeSource diff --git a/libjava/classpath/java/security/DigestException.java b/libjava/classpath/java/security/DigestException.java new file mode 100644 index 00000000000..6393e0cc834 --- /dev/null +++ b/libjava/classpath/java/security/DigestException.java @@ -0,0 +1,70 @@ +/* DigestException.java -- A generic message digest exception + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception indicates that a generic message digest exception has + * occurred. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class DigestException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5821450303093652515L; + + /** + * Create a new instance with no descriptive message. + */ + public DigestException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive message + */ + public DigestException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/DigestInputStream.java b/libjava/classpath/java/security/DigestInputStream.java new file mode 100644 index 00000000000..0d4a9d05dc6 --- /dev/null +++ b/libjava/classpath/java/security/DigestInputStream.java @@ -0,0 +1,167 @@ +/* DigestInputStream.java --- An Input stream tied to a message digest + Copyright (C) 1999, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * DigestInputStream is a class that ties an InputStream with a + * MessageDigest. The Message Digest is used by the class to + * update it self as bytes are read from the InputStream. + * + * The updating to the digest depends on the on flag which is set + * to true by default to tell the class to update the data + * in the message digest. + * + * @version 0.0 + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public class DigestInputStream extends FilterInputStream +{ + /** + * The message digest for the DigestInputStream + */ + protected MessageDigest digest; + + //Manages the on flag + private boolean state = true; + + /** + * Constructs a new DigestInputStream. + * It associates a MessageDigest with the stream to + * compute the stream as data is written. + * + * @param stream An InputStream to associate this stream with + * @param digest A MessageDigest to hash the stream with + */ + public DigestInputStream(InputStream stream, MessageDigest digest) + { + super(stream); + //this.in = stream; + this.digest = digest; + } + + /** + * Returns the MessageDigest associated with this DigestInputStream + * + * @return The MessageDigest used to hash this stream + */ + public MessageDigest getMessageDigest() + { + return digest; + } + + /** + * Sets the current MessageDigest to current parameter + * + * @param digest A MessageDigest to associate with this stream + */ + public void setMessageDigest(MessageDigest digest) + { + this.digest = digest; + } + + /** + * Reads a byte from the input stream and updates the digest. + * This method reads the underlying input stream and if the + * on flag is true then updates the message digest. + * + * @return Returns a byte from the input stream, -1 is returned to indicate that + * the end of stream was reached before this read call + * + * @throws IOException if an IO error occurs in the underlying input stream, + * this error is thrown + */ + public int read() throws IOException + { + int temp = in.read(); + + if (state == true && temp != -1) + digest.update((byte) temp); + + return temp; + } + + /** + * Reads bytes from the input stream and updates the digest. + * This method reads the underlying input stream and if the + * on flag is true then updates the message digest. + * + * @param b a byte array to store the data from the input stream + * @param off an offset to start at in the array + * @param len length of data to read + * @return Returns count of bytes read, -1 is returned to indicate that + * the end of stream was reached before this read call + * + * @throws IOException if an IO error occurs in the underlying input stream, + * this error is thrown + */ + public int read(byte[]b, int off, int len) throws IOException + { + int temp = in.read(b, off, len); + + if (state == true && temp != -1) + digest.update(b, off, temp); + + return temp; + } + + /** + * Sets the flag specifing if this DigestInputStream updates the + * digest in the write() methods. The default is on; + * + * @param on True means it digests stream, false means it does not + */ + public void on(boolean on) + { + state = on; + } + + /** + * Converts the input stream and underlying message digest to a string. + * + * @return A string representing the input stream and message digest. + */ + public String toString() + { + return "[Digest Input Stream] " + digest.toString(); + } +} diff --git a/libjava/classpath/java/security/DigestOutputStream.java b/libjava/classpath/java/security/DigestOutputStream.java new file mode 100644 index 00000000000..037b39e789c --- /dev/null +++ b/libjava/classpath/java/security/DigestOutputStream.java @@ -0,0 +1,158 @@ +/* DigestOutputStream.java --- An output stream tied to a message digest + Copyright (C) 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * DigestOutputStream is a class that ties an OutputStream with a + * MessageDigest. The Message Digest is used by the class to update it + * self as bytes are written to the OutputStream. + * + * The updating to the digest depends on the on flag which is set to + * true by default that tells the class to update the data in the + * message digest. + * + * @version 0.0 + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public class DigestOutputStream extends FilterOutputStream +{ + /** + * The message digest for the DigestOutputStream + */ + protected MessageDigest digest; + + //Manages the on flag + private boolean state = true; + + /** + * Constructs a new DigestOutputStream. It associates a + * MessageDigest with the stream to compute the stream as data is + * written. + * + * @param stream An OutputStream to associate this stream with + * @param digest A MessageDigest to hash the stream with + */ + public DigestOutputStream(OutputStream stream, MessageDigest digest) + { + super(stream); + this.digest = digest; + } + + /** + * Returns the MessageDigest associated with this DigestOutputStream + * + * @return The MessageDigest used to hash this stream + */ + public MessageDigest getMessageDigest() + { + return digest; + } + + /** + * Sets the current MessageDigest to current parameter + * + * @param digest A MessageDigest to associate with this stream + */ + public void setMessageDigest(MessageDigest digest) + { + this.digest = digest; + } + + + /** + * Updates the hash if the on flag is true and then writes a byte to + * the underlying output stream. + * + * @param b A byte to write to the output stream + * + * @exception IOException if the underlying output stream + * cannot write the byte, this is thrown. + */ + public void write(int b) throws IOException + { + if (state) + digest.update((byte) b); + + out.write(b); + } + + /** + * Updates the hash if the on flag is true and then writes the bytes + * to the underlying output stream. + * + * @param b Bytes to write to the output stream + * @param off Offset to start to start at in array + * @param len Length of data to write + * + * @exception IOException if the underlying output stream + * cannot write the bytes, this is thrown. + */ + public void write(byte[]b, int off, int len) throws IOException + { + if (state) + digest.update(b, off, len); + + out.write(b, off, len); + } + + /** + * Sets the flag specifying if this DigestOutputStream updates the + * digest in the write() methods. The default is on; + * + * @param on True means it digests stream, false means it does not + */ + public void on(boolean on) + { + state = on; + } + + /** + * Converts the output stream and underlying message digest to a string. + * + * @return A string representing the output stream and message digest. + */ + public String toString() + { + return "[Digest Output Stream] " + digest.toString(); + } +} diff --git a/libjava/classpath/java/security/DomainCombiner.java b/libjava/classpath/java/security/DomainCombiner.java new file mode 100644 index 00000000000..9ec680c63af --- /dev/null +++ b/libjava/classpath/java/security/DomainCombiner.java @@ -0,0 +1,67 @@ +/* DomainCombiner.java -- Combines ProtectionDomains + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * A public interface used to combine two ProtectionDomains in a new + * ProtectionDomain and update the current Protection Domains + * associated with the current AccessControlContext. + * + * It can add, subtract, or update ProtectionDomains or possibly + * remove duplicates or any possible complex action but just not add + * ones that do not already exist in either array. + * + * @author Mark Benvenuto + * @see AccessControlContext + * @see AccessController + * @since 1.3 + * @status updated to 1.4 + */ +public interface DomainCombiner +{ + /** + * Combines the current ProtectionDomains of the Thread with new + * ProtectionDomains. + * + * @param currentDomains - the ProtectionDomains for the current thread. + * @param assignedDomains - ProtectionsDomains to add + * @return a new array of all the ProtectionDomains + */ + ProtectionDomain[] combine(ProtectionDomain[] currentDomains, + ProtectionDomain[] assignedDomains); +} // interface DomainCombiner diff --git a/libjava/classpath/java/security/DummyKeyPairGenerator.java b/libjava/classpath/java/security/DummyKeyPairGenerator.java new file mode 100644 index 00000000000..da8c362eb71 --- /dev/null +++ b/libjava/classpath/java/security/DummyKeyPairGenerator.java @@ -0,0 +1,75 @@ +/* DummyKeyPairGenerator.java - Wrapper for KeyPairGeneratorSpi + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.security.spec.AlgorithmParameterSpec; + +final class DummyKeyPairGenerator extends KeyPairGenerator +{ + private KeyPairGeneratorSpi kpgSpi = null; + + public DummyKeyPairGenerator(KeyPairGeneratorSpi kpgSpi, String algorithm) + { + super(algorithm); + this.kpgSpi = kpgSpi; + } + + public Object clone() throws CloneNotSupportedException + { + KeyPairGenerator result = new DummyKeyPairGenerator + ((KeyPairGeneratorSpi) kpgSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + public void initialize(int keysize, SecureRandom random) + { + kpgSpi.initialize(keysize, random); + } + + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + kpgSpi.initialize(params, random); + } + + public KeyPair generateKeyPair() + { + return kpgSpi.generateKeyPair(); + } +} diff --git a/libjava/classpath/java/security/DummyMessageDigest.java b/libjava/classpath/java/security/DummyMessageDigest.java new file mode 100644 index 00000000000..6cecdcf6801 --- /dev/null +++ b/libjava/classpath/java/security/DummyMessageDigest.java @@ -0,0 +1,90 @@ +/* DummyMessageDigest.java - Wrapper for MessageDigestSpi + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +final class DummyMessageDigest extends MessageDigest +{ + private MessageDigestSpi mdSpi = null; + + public DummyMessageDigest(MessageDigestSpi mdSpi, String algorithm) + { + super(algorithm); + this.mdSpi = mdSpi; + } + + public Object clone() throws CloneNotSupportedException + { + MessageDigest result = new DummyMessageDigest + ((MessageDigestSpi) mdSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + // java.security.MessageDigestSpi abstract methods implementation --------- + + public byte[] engineDigest() + { + return mdSpi.engineDigest(); + } + + public int engineDigest(byte[] buf, int offset, int len) + throws DigestException + { + return mdSpi.engineDigest(buf, offset, len); + } + + public int engineGetDigestLength() + { + return mdSpi.engineGetDigestLength(); + } + + public void engineReset() + { + mdSpi.engineReset(); + } + + public void engineUpdate(byte input) + { + mdSpi.engineUpdate(input); + } + + public void engineUpdate(byte[] input, int offset, int len) + { + mdSpi.engineUpdate(input, offset, len); + } +} diff --git a/libjava/classpath/java/security/DummySignature.java b/libjava/classpath/java/security/DummySignature.java new file mode 100644 index 00000000000..b74885c9973 --- /dev/null +++ b/libjava/classpath/java/security/DummySignature.java @@ -0,0 +1,102 @@ +/* DummySignature.java - Signature wrapper for SignatureSpi. + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +final class DummySignature extends Signature +{ + private SignatureSpi sigSpi = null; + + public DummySignature(SignatureSpi sigSpi, String algorithm) + { + super(algorithm); + this.sigSpi = sigSpi; + } + + public Object clone() throws CloneNotSupportedException + { + Signature result = new DummySignature + ((SignatureSpi) sigSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + sigSpi.engineInitVerify(publicKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + sigSpi.engineInitSign(privateKey); + } + + protected void engineUpdate(byte b) throws SignatureException + { + sigSpi.engineUpdate(b); + } + + protected void engineUpdate(byte[]b, int off, int len) + throws SignatureException + { + sigSpi.engineUpdate(b, off, len); + } + + protected byte[] engineSign() throws SignatureException + { + return sigSpi.engineSign(); + } + + protected boolean engineVerify(byte[]sigBytes) throws SignatureException + { + return sigSpi.engineVerify(sigBytes); + } + + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException + { + sigSpi.engineSetParameter(param, value); + } + + protected Object engineGetParameter(String param) + throws InvalidParameterException + { + return sigSpi.engineGetParameter(param); + } +} diff --git a/libjava/classpath/java/security/GeneralSecurityException.java b/libjava/classpath/java/security/GeneralSecurityException.java new file mode 100644 index 00000000000..72453ee8cbf --- /dev/null +++ b/libjava/classpath/java/security/GeneralSecurityException.java @@ -0,0 +1,75 @@ +/* GeneralSecurityException.java -- Common superclass of security exceptions + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This class is the common superclass of all security exceptions. All + * exceptions in java.security extend this class with the exception (no + * pun intended) of <code>AccessControlException</code> and + * <code>CertificateException</code> (which extend + * <code>SecurityException</code>), <code>ProviderException</code> + * (<code>RuntimeException</code>), and <code>InvalidParamterException</code> + * (<code>IllegalArgumentException</code>). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class GeneralSecurityException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 894798122053539237L; + + /** + * Create a new instance with no descriptive error message. + */ + public GeneralSecurityException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public GeneralSecurityException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/Guard.java b/libjava/classpath/java/security/Guard.java new file mode 100644 index 00000000000..4f22360a424 --- /dev/null +++ b/libjava/classpath/java/security/Guard.java @@ -0,0 +1,60 @@ +/* Guard.java -- Check access to a guarded object + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specifies a mechanism for querying whether or not + * access is allowed to a guarded object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see GuardedObject + * @since 1.1 + * @status updated to 1.4 + */ +public interface Guard +{ + /** + * This method tests whether or not access is allowed to the specified + * guarded object. Access is allowed if this method returns silently. If + * access is denied, an exception is generated. + * + * @param obj the <code>Object</code> to test + * @throws SecurityException if access to the object is denied + */ + void checkGuard(Object obj); +} // interface Guard diff --git a/libjava/classpath/java/security/GuardedObject.java b/libjava/classpath/java/security/GuardedObject.java new file mode 100644 index 00000000000..5ca08835dc5 --- /dev/null +++ b/libjava/classpath/java/security/GuardedObject.java @@ -0,0 +1,121 @@ +/* GuardedObject.java -- An object protected by a Guard + Copyright (C) 1998, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class is an object that is guarded by a <code>Guard</code> object. + * The object that is being guarded is retrieved by a call to the only + * method in this class - <code>getObject</code>. That method returns the + * guarded <code>Object</code> after first checking with the + * <code>Guard</code>. If the <code>Guard</code> disallows access, an + * exception will be thrown. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class GuardedObject implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5240450096227834308L; + + /** + * This is the Guard that is protecting the object. + * + * @serial the guard + */ + private final Guard guard; + + /** + * This is the object that is being guarded. + * + * @serial the protected object + */ + private final Object object; + + /** + * This method initializes a new instance of <code>GuardedObject</code> + * that protects the specified <code>Object</code> using the specified + * <code>Guard</code>. A null guard means there are no restrictions on + * accessing the object. + * + * @param object the <code>Object</code> to guard + * @param guard the <code>Guard</code> that is protecting the object + */ + public GuardedObject(Object object, Guard guard) + { + this.object = object; + this.guard = guard; + } + + /** + * This method first call the <code>checkGuard</code> method on the + * <code>Guard</code> object protecting the guarded object. If the + * <code>Guard</code> disallows access, an exception is thrown, otherwise + * the <code>Object</code> is returned. + * + * @return The object being guarded + * @throws SecurityException if access is denied + */ + public Object getObject() + { + if (guard != null) + guard.checkGuard(object); + return object; + } + + /** + * Ensures that serialization is legal, by checking the guard. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + if (guard != null) + guard.checkGuard(object); + s.defaultWriteObject(); + } +} // class GuardedObject diff --git a/libjava/classpath/java/security/Identity.java b/libjava/classpath/java/security/Identity.java new file mode 100644 index 00000000000..26b01a50a6b --- /dev/null +++ b/libjava/classpath/java/security/Identity.java @@ -0,0 +1,407 @@ +/* Identity.java --- Identity Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; +import java.util.Vector; + +/** + * <p>This class represents identities: real-world objects such as people, + * companies or organizations whose identities can be authenticated using their + * public keys. Identities may also be more abstract (or concrete) constructs, + * such as daemon threads or smart cards.</p> + * + * <p>All Identity objects have a <i>name</i> and a <i>public key</i>. Names + * are immutable. <i>Identities</i> may also be <b>scoped</b>. That is, if an + * <i>Identity</i> is specified to have a particular <i>scope</i>, then the + * <i>name</i> and <i>public key</i> of the <i>Identity</i> are unique within + * that <i>scope</i>.</p> + * + * <p>An <i>Identity</i> also has a <i>set of certificates</i> (all certifying + * its own <i>public key</i>). The <i>Principal</i> names specified in these + * certificates need not be the same, only the key.</p> + * + * <p>An <i>Identity</i> can be subclassed, to include postal and email + * addresses, telephone numbers, images of faces and logos, and so on.</p> + * + * @author Mark Benvenuto + * @see IdentityScope + * @see Signer + * @see Principal + * @deprecated This class is no longer used. Its functionality has been replaced + * by <code>java.security.KeyStore</code>, the <code>java.security.cert</code> + * package, and <code>java.security.Principal</code>. + */ +public abstract class Identity implements Principal, Serializable +{ + private static final long serialVersionUID = 3609922007826600659L; + + private String name; + private IdentityScope scope; + private PublicKey publicKey; + private String info; + private Vector certificates; + + /** Constructor for serialization only. */ + protected Identity() + { + } + + /** + * Constructs an identity with the specified name and scope. + * + * @param name the identity name. + * @param scope the scope of the identity. + * @throws KeyManagementException if there is already an identity with the + * same name in the scope. + */ + public Identity(String name, IdentityScope scope) + throws KeyManagementException + { + this.name = name; + this.scope = scope; + } + + /** + * Constructs an identity with the specified name and no scope. + * + * @param name the identity name. + */ + public Identity(String name) + { + this.name = name; + this.scope = null; + } + + /** + * Returns this identity's name. + * + * @return the name of this identity. + */ + public final String getName() + { + return name; + } + + /** + * Returns this identity's scope. + * + * @return the scope of this identity. + */ + public final IdentityScope getScope() + { + return scope; + } + + /** + * Returns this identity's public key. + * + * @return the public key for this identity. + * @see #setPublicKey(java.security.PublicKey) + */ + public PublicKey getPublicKey() + { + return publicKey; + } + + /** + * <p>Sets this identity's public key. The old key and all of this identity's + * certificates are removed by this operation.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"setIdentityPublicKey"</code> as its + * argument to see if it's ok to set the public key.</p> + * + * @param key the public key for this identity. + * @throws KeyManagementException if another identity in the identity's scope + * has the same public key, or if another exception occurs. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow setting the public + * key. + * @see #getPublicKey() + * @see SecurityManager#checkSecurityAccess(String) + */ + public void setPublicKey(PublicKey key) throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setIdentityPublicKey"); + + this.publicKey = key; + } + + /** + * <p>Specifies a general information string for this identity.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"setIdentityInfo"</code> as its + * argument to see if it's ok to specify the information string.</p> + * + * @param info the information string. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow setting the + * information string. + * @see #getInfo() + * @see SecurityManager#checkSecurityAccess(String) + */ + public void setInfo(String info) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setIdentityInfo"); + + this.info = info; + } + + /** + * Returns general information previously specified for this identity. + * + * @return general information about this identity. + * @see #setInfo(String) + */ + public String getInfo() + { + return info; + } + + /** + * <p>Adds a certificate for this identity. If the identity has a public key, + * the public key in the certificate must be the same, and if the identity + * does not have a public key, the identity's public key is set to be that + * specified in the certificate.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"addIdentityCertificate"</code> as its + * argument to see if it's ok to add a certificate.</p> + * + * @param certificate the certificate to be added. + * @throws KeyManagementException if the certificate is not valid, if the + * public key in the certificate being added conflicts with this identity's + * public key, or if another exception occurs. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow adding a + * certificate. + * @see SecurityManager#checkSecurityAccess(String) + */ + public void addCertificate(Certificate certificate) + throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("addIdentityCertificate"); + + // Check public key of this certificate against the first one in the vector + if (certificates.size() > 0) + { + if (((Certificate) certificates.firstElement()).getPublicKey() != publicKey) + throw new KeyManagementException("Public key does not match"); + } + certificates.addElement(certificate); + } + + /** + * <p>Removes a certificate from this identity.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"removeIdentityCertificate"</code> as + * its argument to see if it's ok to remove a certificate.</p> + * + * @param certificate the certificate to be removed. + * @throws KeyManagementException if the certificate is missing, or if + * another exception occurs. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow removing a + * certificate. + * @see SecurityManager#checkSecurityAccess(String) + */ + public void removeCertificate(Certificate certificate) + throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeIdentityCertificate"); + + if (certificates.contains(certificate) == false) + throw new KeyManagementException("Certificate not found"); + + certificates.removeElement(certificate); + } + + /** + * Returns a copy of all the certificates for this identity. + * + * @return a copy of all the certificates for this identity. + */ + public Certificate[] certificates() + { + Certificate[] certs = new Certificate[certificates.size()]; + int max = certificates.size(); + for (int i = 0; i < max; i++) + certs[i] = (Certificate) certificates.elementAt(i); + + return certs; + } + + /** + * Tests for equality between the specified object and this identity. This + * first tests to see if the entities actually refer to the same object, in + * which case it returns <code>true</code>. Next, it checks to see if the + * entities have the same <i>name</i> and the same <i>scope</i>. If they do, + * the method returns <code>true</code>. Otherwise, it calls + * <code>identityEquals()</code>, which subclasses should override. + * + * @param identity the object to test for equality with this identity. + * @return <code>true</code> if the objects are considered equal, <code>false + * </code>otherwise. + * @see #identityEquals(Identity) + */ + public final boolean equals(Object identity) + { + if (identity instanceof Identity) + { + if (identity == this) + return true; + + if ((((Identity) identity).getName() == this.name) && + (((Identity) identity).getScope() == this.scope)) + return true; + + return identityEquals((Identity) identity); + } + return false; + } + + /** + * Tests for equality between the specified <code>identity</code> and this + * <i>identity</i>. This method should be overriden by subclasses to test for + * equality. The default behavior is to return <code>true</code> if the names + * and public keys are equal. + * + * @param identity the identity to test for equality with this identity. + * @return <code>true</code> if the identities are considered equal, + * <code>false</code> otherwise. + * @see #equals(Object) + */ + protected boolean identityEquals(Identity identity) + { + return ((identity.getName() == this.name) && + (identity.getPublicKey() == this.publicKey)); + } + + /** + * <p>Returns a short string describing this identity, telling its name and + * its scope (if any).</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"printIdentity"</code> as its argument + * to see if it's ok to return the string.</p> + * + * @return information about this identity, such as its name and the name of + * its scope (if any). + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow returning a string + * describing this identity. + * @see SecurityManager#checkSecurityAccess(String) + */ + public String toString() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("printIdentity"); + + /* TODO: Insert proper format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + + /** + * <p>Returns a string representation of this identity, with optionally more + * details than that provided by the <code>toString()</code> method without + * any arguments.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"printIdentity"</code> as its argument + * to see if it's ok to return the string.</p> + * + * @param detailed whether or not to provide detailed information. + * @return information about this identity. If detailed is <code>true</code>, + * then this method returns more information than that provided by the + * <code>toString()</code> method without any arguments. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow returning a string + * describing this identity. + * @see #toString() + * @see SecurityManager#checkSecurityAccess(String) + */ + public String toString(boolean detailed) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("printIdentity"); + + if (detailed) + { + /* TODO: Insert proper detailed format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + else + { + /* TODO: Insert proper format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + } + + /** + * Returns a hashcode for this identity. + * + * @return a hashcode for this identity. + */ + public int hashCode() + { + int ret = name.hashCode(); + if (publicKey != null) + ret |= publicKey.hashCode(); + if (scope != null) + ret |= scope.hashCode(); + if (info != null) + ret |= info.hashCode(); + if (certificates != null) + ret |= certificates.hashCode(); + + return ret; + } +} diff --git a/libjava/classpath/java/security/IdentityScope.java b/libjava/classpath/java/security/IdentityScope.java new file mode 100644 index 00000000000..34dd011e280 --- /dev/null +++ b/libjava/classpath/java/security/IdentityScope.java @@ -0,0 +1,226 @@ +/* IdentityScope.java --- IdentityScope Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.Enumeration; + +/** + * <p>This class represents a scope for identities. It is an Identity itself, + * and therefore has a name and can have a scope. It can also optionally have a + * public key and associated certificates.</p> + * + * <p>An <code>IdentityScope</code> can contain {@link Identity} objects of all + * kinds, including {@link Signer}s. All types of <code>Identity</code> objects + * can be retrieved, added, and removed using the same methods. Note that it is + * possible, and in fact expected, that different types of identity scopes will + * apply different policies for their various operations on the various types of + * Identities.</p> + * + * <p>There is a one-to-one mapping between keys and identities, and there can + * only be one copy of one key per scope. For example, suppose Acme Software, + * Inc is a software publisher known to a user. Suppose it is an <i>Identity</i>, + * that is, it has a public key, and a set of associated certificates. It is + * named in the scope using the name "Acme Software". No other named <i>Identity + * </i> in the scope has the same public key. Of course, none has the same name + * as well.</p> + * + * @author Mark Benvenuto + * @see Identity + * @see Signer + * @see Principal + * @see Key + * @deprecated This class is no longer used. Its functionality has been replaced + * by <code>java.security.KeyStore</code>, the <code>java.security.cert</code> + * package, and <code>java.security.Principal</code>. + */ +public abstract class IdentityScope extends Identity +{ + private static final long serialVersionUID = -2337346281189773310L; + private static IdentityScope systemScope; + + /** + * This constructor is used for serialization only and should not be used by + * subclasses. + */ + protected IdentityScope() + { + super(); + } + + /** + * Constructs a new identity scope with the specified name. + * + * @param name the scope name. + */ + public IdentityScope(String name) + { + super(name); + } + + /** + * Constructs a new identity scope with the specified name and scope. + * + * @param name the scope name. + * @param scope the scope for the new identity scope. + * @throws KeyManagementException if there is already an identity with the + * same name in the scope. + */ + public IdentityScope(String name, IdentityScope scope) + throws KeyManagementException + { + super(name, scope); + } + + /** + * Returns the system's identity scope. + * + * @return the system's identity scope. + * @see #setSystemScope(IdentityScope) + */ + public static IdentityScope getSystemScope() + { + if (systemScope == null) + { + //Load it + //systemScope; + } + return systemScope; + } + + /** + * Sets the system's identity scope. + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"setSystemScope"</code> as its argument + * to see if it's ok to set the identity scope.</p> + * + * @param scope the scope to set. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow setting the + * identity scope. + * @see #getSystemScope() + * @see SecurityManager#checkSecurityAccess(String) + */ + protected static void setSystemScope(IdentityScope scope) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setSystemScope"); + + systemScope = scope; + } + + /** + * Returns the number of identities within this identity scope. + * + * @return the number of identities within this identity scope. + */ + public abstract int size(); + + /** + * Returns the identity in this scope with the specified name (if any). + * + * @param name the name of the identity to be retrieved. + * @return the identity named name, or <code>null</code> if there are no + * identities named name in this scope. + */ + public abstract Identity getIdentity(String name); + + /** + * Retrieves the identity whose name is the same as that of the specified + * principal. (Note: <code>Identity</code> implements <code>Principal</code>.) + * + * @param principal the principal corresponding to the identity to be + * retrieved. + * @return the identity whose name is the same as that of the principal, or + * <code>null</code> if there are no identities of the same name in this scope. + */ + public Identity getIdentity(Principal principal) + { + return getIdentity(principal.getName()); + } + + /** + * Retrieves the identity with the specified public key. + * + * @param key the public key for the identity to be returned. + * @return the identity with the given key, or <code>null</code> if there are + * no identities in this scope with that key. + */ + public abstract Identity getIdentity(PublicKey key); + + /** + * Adds an identity to this identity scope. + * + * @param identity the identity to be added. + * @throws KeyManagementException if the identity is not valid, a name + * conflict occurs, another identity has the same public key as the identity + * being added, or another exception occurs. + */ + public abstract void addIdentity(Identity identity) + throws KeyManagementException; + + /** + * Removes an identity from this identity scope. + * + * @param identity the identity to be removed. + * @throws KeyManagementException if the identity is missing, or another + * exception occurs. + */ + public abstract void removeIdentity(Identity identity) + throws KeyManagementException; + + /** + * Returns an enumeration of all identities in this identity scope. + * + * @return an enumeration of all identities in this identity scope. + */ + public abstract Enumeration identities(); + + /** + * Returns a string representation of this identity scope, including its name, + * its scope name, and the number of identities in this identity scope. + * + * @return a string representation of this identity scope. + * @see SecurityManager#checkSecurityAccess(String) + */ + public String toString() + { + return (super.getName() + " " + super.getScope().getName() + " " + size()); + } +} diff --git a/libjava/classpath/java/security/IntersectingDomainCombiner.java b/libjava/classpath/java/security/IntersectingDomainCombiner.java new file mode 100644 index 00000000000..2bfcfb44233 --- /dev/null +++ b/libjava/classpath/java/security/IntersectingDomainCombiner.java @@ -0,0 +1,82 @@ +/* IntersectingDomainCombiner.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.util.HashSet; + +/** + * A trivial implementation of {@link DomainCombiner} that produces the + * intersection of the supplied {@link ProtectionDomain} objects. + */ +final class IntersectingDomainCombiner implements DomainCombiner +{ + + // Contstant. + // ------------------------------------------------------------------------- + + static final IntersectingDomainCombiner SINGLETON = new IntersectingDomainCombiner(); + + // Constructor. + // ------------------------------------------------------------------------- + + private IntersectingDomainCombiner() + { + } + + // Methods. + // ------------------------------------------------------------------------- + + public ProtectionDomain[] combine (ProtectionDomain[] currentDomains, + ProtectionDomain[] assignedDomains) + { + HashSet newDomains = new HashSet (); + for (int i = 0; i < currentDomains.length; i++) + { + if (currentDomains[i] == null) + continue; + for (int j = 0; j < assignedDomains.length; j++) + { + if (currentDomains[i].equals (assignedDomains[j])) + newDomains.add (currentDomains[i]); + } + } + return (ProtectionDomain[]) + newDomains.toArray(new ProtectionDomain[newDomains.size()]); + } +} diff --git a/libjava/classpath/java/security/InvalidAlgorithmParameterException.java b/libjava/classpath/java/security/InvalidAlgorithmParameterException.java new file mode 100644 index 00000000000..9b726199521 --- /dev/null +++ b/libjava/classpath/java/security/InvalidAlgorithmParameterException.java @@ -0,0 +1,73 @@ +/* InvalidAlgorithmParameterException.java -- an invalid parameter to a + security algorithm + Copyright (C) 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown for an invalid security algorithm parameter. + * + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class InvalidAlgorithmParameterException + extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 2864672297499471472L; + + /** + * Construct an exception with no message. + */ + public InvalidAlgorithmParameterException() + { + super(); + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidAlgorithmParameterException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/InvalidKeyException.java b/libjava/classpath/java/security/InvalidKeyException.java new file mode 100644 index 00000000000..cd5845a6181 --- /dev/null +++ b/libjava/classpath/java/security/InvalidKeyException.java @@ -0,0 +1,69 @@ +/* InvalidKeyException -- thrown for an invalid key + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown for an invalid key. + * + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class InvalidKeyException extends KeyException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5698479920593359816L; + + /** + * Construct an exception with no message. + */ + public InvalidKeyException() + { + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidKeyException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/InvalidParameterException.java b/libjava/classpath/java/security/InvalidParameterException.java new file mode 100644 index 00000000000..c5218a04917 --- /dev/null +++ b/libjava/classpath/java/security/InvalidParameterException.java @@ -0,0 +1,70 @@ +/* InvalidParameterException.java -- an invalid parameter in the JCA/JCE engine + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown when an invalid parameter is passed to a method of the JCA/JCE + * engine classes. + * + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class InvalidParameterException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -857968536935667808L; + + /** + * Construct an exception with no message. + */ + public InvalidParameterException() + { + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidParameterException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/Key.java b/libjava/classpath/java/security/Key.java new file mode 100644 index 00000000000..23652b6e7c4 --- /dev/null +++ b/libjava/classpath/java/security/Key.java @@ -0,0 +1,94 @@ +/* Key.java -- A abstract representation of a digital key + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; + +/** + * This interfaces models the base characteristics that all keys must + * have. These are: a key algorithm, an encoded form, and a format used + * to encode the key. Specific key types inherit from this interface. + * Note that since this interface extends <code>Serializable</code>, all + * keys may be serialized. Keys are generally obtained through key generators, + * including {@link KeyFactory}. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see PublicKey + * @see PrivateKey + * @see KeyPair + * @see KeyPairGenerator + * @see KeyFactory + * @see KeySpec + * @see Identity + * @see Signer + * @since 1.1 + * @status updated to 1.4 + */ +public interface Key extends Serializable +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 6603384152749567654L; + + /** + * This method returns the name of the algorithm for this key. This is a + * <code>String</code> such as "RSA". + * + * @return the name of the algorithm in use + */ + String getAlgorithm(); + + /** + * This method returns the name of the encoding format for this key. This + * is the name of the ASN.1 data format used for this key, such as + * "X.509" or "PKCS#8". This method returns <code>null</code> if this key + * does not have an encoding format. + * + * @return the name of the encoding format for this key, or null + */ + String getFormat(); + + /** + * This method returns the encoded form of the key. If this key does not + * support encoding, this method returns <code>null</code>. + * + * @return the encoded form of the key, or null + */ + byte[] getEncoded(); +} // interface Key diff --git a/libjava/classpath/java/security/KeyException.java b/libjava/classpath/java/security/KeyException.java new file mode 100644 index 00000000000..feaf0249a95 --- /dev/null +++ b/libjava/classpath/java/security/KeyException.java @@ -0,0 +1,72 @@ +/* KeyException.java -- Thrown when there is a problem with a key + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when there is a problem with a key. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @status updated to 1.4 + */ +public class KeyException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7483676942812432108L; + + /** + * This method initializes a new instance of <code>KeyException</code> + * with no descriptive message. + */ + public KeyException() + { + } + + /** + * This method initializes a new instance of <code>KeyException</code> + * with a descriptive message. + * + * @param msg the descriptive message + */ + public KeyException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/KeyFactory.java b/libjava/classpath/java/security/KeyFactory.java new file mode 100644 index 00000000000..64ce841fae8 --- /dev/null +++ b/libjava/classpath/java/security/KeyFactory.java @@ -0,0 +1,297 @@ +/* KeyFactory.java --- Key Factory Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * <p>Key factories are used to convert keys (opaque cryptographic keys of type + * {@link Key}) into key specifications (transparent representations of the + * underlying key material), and vice versa.</p> + * + * <p>Key factories are bi-directional. That is, they allow you to build an + * opaque key object from a given key specification (key material), or to + * retrieve the underlying key material of a key object in a suitable format.</p> + * + * <p>Multiple compatible key specifications may exist for the same key. For + * example, a <i>DSA</i> public key may be specified using {@link + * java.security.spec.DSAPublicKeySpec} or {@link + * java.security.spec.X509EncodedKeySpec}. A key factory can be used to + * translate between compatible key specifications.</p> + * + * <p>The following is an example of how to use a key factory in order to + * instantiate a <i>DSA</i> public key from its encoding. Assume Alice has + * received a digital signature from Bob. Bob also sent her his public key (in + * encoded format) to verify his signature. Alice then performs the following + * actions: + * + * <pre> + * X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey); + * KeyFactory keyFactory = KeyFactory.getInstance("DSA"); + * PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec); + * Signature sig = Signature.getInstance("DSA"); + * sig.initVerify(bobPubKey); + * sig.update(data); + * sig.verify(signature); + * </pre> + * + * @since 1.2 + * @see Key + * @see PublicKey + * @see PrivateKey + * @see KeySpec + * @see java.security.spec.DSAPublicKeySpec + * @see java.security.spec.X509EncodedKeySpec + @author Mark Benvenuto + */ +public class KeyFactory +{ + /** The service name for key factories. */ + private static final String KEY_FACTORY = "KeyFactory"; + + private KeyFactorySpi keyFacSpi; + private Provider provider; + private String algorithm; + + /** + * Creates a <code>KeyFactory</code> object. + * + * @param keyFacSpi the delegate. + * @param provider the provider. + * @param algorithm the name of the algorithm to associate with this + * <code>KeyFactory</code>. + */ + protected KeyFactory(KeyFactorySpi keyFacSpi, Provider provider, + String algorithm) + { + this.keyFacSpi = keyFacSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Generates a <code>KeyFactory</code> object that implements the specified + * algorithm. If the default provider package provides an implementation of + * the requested algorithm, an instance of <code>KeyFactory</code> containing + * that implementation is returned. If the algorithm is not available in the + * default package, other packages are searched. + * + * @param algorithm the name of the requested key algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference + * for information about standard algorithm names. + * @return a <code>KeyFactory</code> object for the specified algorithm. + * @throws NoSuchAlgorithmException if the requested algorithm is not + * available in the default provider package or any of the other provider + * packages that were searched. + */ + public static KeyFactory getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignore. + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Generates a <code>KeyFactory</code> object for the specified algorithm + * from the specified provider. + * + * @param algorithm the name of the requested key algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference + * for information about standard algorithm names. + * @param provider the name of the provider. + * @return a <code>KeyFactory</code> object for the specified algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not available from + * the specified provider. + * @throws NoSuchProviderException if the provider has not been configured. + * @throws IllegalArgumentException if the provider name is null or empty. + * @see Provider + */ + public static KeyFactory getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates a <code>KeyFactory</code> object for the specified algorithm from + * the specified provider. Note: the <code>provider</code> doesn't have to be + * registered. + * + * @param algorithm the name of the requested key algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the provider. + * @return a <code>KeyFactory</code> object for the specified algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not available from + * the specified provider. + * @throws IllegalArgumentException if the <code>provider</code> is + * <code>null</code>. + * @since 1.4 + * @see Provider + */ + public static KeyFactory getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + try + { + return new KeyFactory((KeyFactorySpi) + Engine.getInstance(KEY_FACTORY, algorithm, provider), + provider, algorithm); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + /** + * Returns the provider of this key factory object. + * + * @return the provider of this key factory object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Gets the name of the algorithm associated with this <code>KeyFactory</code>. + * + * @return the name of the algorithm associated with this + * <code>KeyFactory</code>. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Generates a public key object from the provided key specification (key + * material). + * + * @param keySpec the specification (key material) of the public key. + * @return the public key. + * @throws InvalidKeySpecException if the given key specification is + * inappropriate for this key factory to produce a public key. + */ + public final PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGeneratePublic(keySpec); + } + + /** + * Generates a private key object from the provided key specification (key + * material). + * + * @param keySpec the specification (key material) of the private key. + * @return the private key. + * @throws InvalidKeySpecException if the given key specification is + * inappropriate for this key factory to produce a private key. + */ + public final PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGeneratePrivate(keySpec); + } + + /** + * Returns a specification (key material) of the given key object. + * <code>keySpec</code> identifies the specification class in which the key + * material should be returned. It could, for example, be + * <code>DSAPublicKeySpec.class</code>, to indicate that the key material + * should be returned in an instance of the {@link + * java.security.spec.DSAPublicKeySpec} class. + * + * @param key the key. + * @param keySpec the specification class in which the key material should be + * returned. + * @return the underlying key specification (key material) in an instance of + * the requested specification class. + * @throws InvalidKeySpecException if the requested key specification is + * inappropriate for the given key, or the given key cannot be processed + * (e.g., the given key has an unrecognized algorithm or format). + */ + public final KeySpec getKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGetKeySpec(key, keySpec); + } + + /** + * Translates a key object, whose provider may be unknown or potentially + * untrusted, into a corresponding key object of this key factory. + * + * @param key the key whose provider is unknown or untrusted. + * @return the translated key. + * @throws InvalidKeyException if the given key cannot be processed by this + * key factory. + */ + public final Key translateKey(Key key) throws InvalidKeyException + { + return keyFacSpi.engineTranslateKey(key); + } +} diff --git a/libjava/classpath/java/security/KeyFactorySpi.java b/libjava/classpath/java/security/KeyFactorySpi.java new file mode 100644 index 00000000000..1894fad08df --- /dev/null +++ b/libjava/classpath/java/security/KeyFactorySpi.java @@ -0,0 +1,133 @@ +/* KeyFactorySpi.java --- Key Factory Service Provider Interface + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * KeyFactorySpi is the Service Provider Interface (SPI) for the + * KeyFactory class. This is the interface for providers to + * supply to implement a key factory for an algorithm. + * + * Key factories are used to convert keys (opaque cryptographic + * keys of type Key) into key specifications (transparent + * representations of the underlying key material). + * + * Key factories are bi-directional. They allow a key class + * to be converted into a key specification (key material) and + * back again. + * + * For example DSA public keys can be specified as + * DSAPublicKeySpec or X509EncodedKeySpec. The key factory + * translate these key specifications. + * + * @since JDK 1.2 + * @author Mark Benvenuto + */ +public abstract class KeyFactorySpi +{ + /** + * Constucts a new KeyFactorySpi. + */ + public KeyFactorySpi() + { + } + + /** + * Generates a public key from the provided key specification. + * + * @param keySpec key specification + * + * @return the public key + * + * @throws InvalidKeySpecException invalid key specification for + * this key factory to produce a public key + */ + protected abstract PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException; + + + /** + * Generates a private key from the provided key specification. + * + * @param keySpec key specification + * + * @return the private key + * + * @throws InvalidKeySpecException invalid key specification for + * this key factory to produce a private key + */ + protected abstract PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException; + + /** + * Returns a key specification for the given key. keySpec + * identifies the specification class to return the key + * material in. + * + * @param key the key + * @param keySpec the specification class to return the + * key material in. + * + * @return the key specification in an instance of the requested + * specification class + * + * @throws InvalidKeySpecException the requested key specification + * is inappropriate for this key or the key is + * unrecognized. + */ + protected abstract KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException; + + + /** + * Translates the key from an unknown or untrusted provider + * into a key for this key factory. + * + * @param the key from an unknown or untrusted provider + * + * @return the translated key + * + * @throws InvalidKeySpecException if the key cannot be + * processed by this key factory + */ + protected abstract Key engineTranslateKey(Key key) + throws InvalidKeyException; +} diff --git a/libjava/classpath/java/security/KeyManagementException.java b/libjava/classpath/java/security/KeyManagementException.java new file mode 100644 index 00000000000..694b4c242b6 --- /dev/null +++ b/libjava/classpath/java/security/KeyManagementException.java @@ -0,0 +1,71 @@ +/* KeyManagementException.java -- an exception in key management + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown whenever a problem related to the management of + * security keys is encountered. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @status updated to 1.4 + */ +public class KeyManagementException extends KeyException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 947674216157062695L; + + /** + * Create a new instance with no descriptive error message. + */ + public KeyManagementException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public KeyManagementException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/KeyPair.java b/libjava/classpath/java/security/KeyPair.java new file mode 100644 index 00000000000..bf1a40a23ab --- /dev/null +++ b/libjava/classpath/java/security/KeyPair.java @@ -0,0 +1,87 @@ +/* KeyPair.java --- Key Pair Class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.io.Serializable; + +/** + KeyPair serves as a simple container for public and private keys. + If properly initialized, this class should be treated like the + private key since it contains it and take approriate security + measures. + + @author Mark Benvenuto + */ +public final class KeyPair implements Serializable +{ + private static final long serialVersionUID = -7565189502268009837L; + + private PublicKey publicKey; + private PrivateKey privateKey; + + /** + Initializes the KeyPair with a pubilc and private key. + + @param publicKey Public Key to store + @param privateKey Private Key to store + */ + public KeyPair(PublicKey publicKey, PrivateKey privateKey) + { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + /** + Returns the public key stored in the KeyPair + + @return The public key + */ + public PublicKey getPublic() + { + return publicKey; + } + + /** + Returns the private key stored in the KeyPair + + @return The private key + */ + public PrivateKey getPrivate() + { + return privateKey; + } +} diff --git a/libjava/classpath/java/security/KeyPairGenerator.java b/libjava/classpath/java/security/KeyPairGenerator.java new file mode 100644 index 00000000000..e6f926e2bf0 --- /dev/null +++ b/libjava/classpath/java/security/KeyPairGenerator.java @@ -0,0 +1,401 @@ +/* KeyPairGenerator.java --- Key Pair Generator Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * <p>The <code>KeyPairGenerator</code> class is used to generate pairs of + * public and private keys. Key pair generators are constructed using the + * <code>getInstance()</code> factory methods (static methods that return + * instances of a given class).</p> + * + * <p>A Key pair generator for a particular algorithm creates a public/private + * key pair that can be used with this algorithm. It also associates + * algorithm-specific parameters with each of the generated keys.</p> + * + * <p>There are two ways to generate a key pair: in an algorithm-independent + * manner, and in an algorithm-specific manner. The only difference between the + * two is the initialization of the object:</p> + * + * <ul> + * <li><b>Algorithm-Independent Initialization</b><br/> + * All key pair generators share the concepts of a <i>keysize</i> and a + * <i>source of randomness</i>. The <i>keysize</i> is interpreted differently + * for different algorithms (e.g., in the case of the <i>DSA</i> algorithm, + * the <i>keysize</i> corresponds to the length of the modulus). There is an + * <code>initialize()</code> method in this <code>KeyPairGenerator</code> + * class that takes these two universally shared types of arguments. There + * is also one that takes just a <i>keysize</i> argument, and uses the + * {@link SecureRandom} implementation of the highest-priority installed + * provider as the <i>source of randomness</i>. (If none of the installed + * providers supply an implementation of {@link SecureRandom}, a + * system-provided source of randomness is used.) + * + * <p>Since no other parameters are specified when you call the above + * algorithm-independent initialize methods, it is up to the provider what + * to do about the algorithm-specific parameters (if any) to be associated + * with each of the keys.</p> + * + * <p>If the algorithm is the <i>DSA</i> algorithm, and the <i>keysize</i> + * (modulus size) is <code>512</code>, <code>768</code>, or <code>1024</code>, + * then the <b>GNU</b> provider uses a set of precomputed values for the + * <code>p</code>, <code>q</code>, and <code>g</code> parameters. If the + * <i>modulus size</i> is not one of the above values, the <b>GNU</b> + * provider creates a new set of parameters. Other providers might have + * precomputed parameter sets for more than just the three modulus sizes + * mentioned above. Still others might not have a list of precomputed + * parameters at all and instead always create new parameter sets.</p></li> + * <li><b>Algorithm-Specific Initialization</b><br/> + * For situations where a set of algorithm-specific parameters already + * exists (e.g., so-called <i>community parameters</i> in <i>DSA</i>), there + * are two initialize methods that have an {@link AlgorithmParameterSpec} + * argument. One also has a {@link SecureRandom} argument, while the the + * other uses the {@link SecureRandom} implementation of the highest-priority + * installed provider as the source of randomness. (If none of the installed + * providers supply an implementation of {@link SecureRandom}, a + * system-provided source of randomness is used.)</li> + * </ul> + * + * <p>In case the client does not explicitly initialize the + * <code>KeyPairGenerator</code> (via a call to an initialize method), each + * provider must supply (and document) a default initialization. For example, + * the <b>GNU</b> provider uses a default modulus size (keysize) of + * <code>1024</code> bits.</p> + * + * <p>Note that this class is abstract and extends from {@link + * KeyPairGeneratorSpi} for historical reasons. Application developers should + * only take notice of the methods defined in this <code>KeyPairGenerator</code> + * class; all the methods in the superclass are intended for cryptographic + * service providers who wish to supply their own implementations of key pair + * generators.</p> + * + * @see Signature + * @see KeyPair + * @see AlgorithmParameterSpec + * @author Mark Benvenuto + * @author Casey Marshall + */ +public abstract class KeyPairGenerator extends KeyPairGeneratorSpi +{ + /** The service name for key pair generators. */ + private static final String KEY_PAIR_GENERATOR = "KeyPairGenerator"; + + Provider provider; + private String algorithm; + + /** + * Creates a <code>KeyPairGenerator</code> object for the specified + * algorithm. + * + * @param algorithm the standard string name of the algorithm. + * See Appendix A in the Java Cryptography Architecture API + * Specification & Reference for information about standard + * algorithm names. + */ + protected KeyPairGenerator(String algorithm) + { + this.algorithm = algorithm; + this.provider = null; + } + + /** + * Returns the standard name of the algorithm for this key pair generator. + * See Appendix A in the Java Cryptography Architecture API Specification + * & Reference for information about standard algorithm names. + * + * @return the standard string name of the algorithm. + */ + public String getAlgorithm() + { + return algorithm; + } + + /** + * Generates a <code>KeyPairGenerator</code> object that implements the + * specified digest algorithm. If the default provider package provides an + * implementation of the requested digest algorithm, an instance of + * <code>KeyPairGenerator</code> containing that implementation is returned. + * If the algorithm is not available in the default package, other packages + * are searched. + * + * @param algorithm the standard string name of the algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @return the new <code>KeyPairGenerator</code> object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * environment. + */ + public static KeyPairGenerator getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignored. + } + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Generates a <code>KeyPairGenerator</code> object implementing the + * specified algorithm, as supplied from the specified provider, if + * such an algorithm is available from the provider. + * + * @param algorithm the standard string name of the algorithm. See + * Appendix A in the Java Cryptography Architecture API Specification + * & Reference for information about standard algorithm names. + * @param provider the string name of the provider. + * @return the new <code>KeyPairGenerator</code> object. + * @throws NoSuchAlgorithmException if the algorithm is not available + * from the provider. + * @throws NoSuchProviderException if the provider is not available in the + * environment. + * @throws IllegalArgumentException if the provider name is <code>null</code> + * or empty. + * @see Provider + */ + public static KeyPairGenerator getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates a <code>KeyPairGenerator</code> object implementing the specified + * algorithm, as supplied from the specified provider, if such an algorithm is + * available from the provider. Note: the provider doesn't have to be + * registered. + * + * @param algorithm the standard string name of the algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the provider. + * @return the new <code>KeyPairGenerator</code> object. + * @throws NoSuchAlgorithmException if the <code>algorithm</code> is not + * available from the <code>provider</code>. + * @throws IllegalArgumentException if the <code>provider</code> is + * <code>null</code>. + * @since 1.4 + * @see Provider + */ + public static KeyPairGenerator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + Object o = null; + try + { + o = Engine.getInstance(KEY_PAIR_GENERATOR, algorithm, provider); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + + KeyPairGenerator result = null; + if (o instanceof KeyPairGeneratorSpi) + { + result = new DummyKeyPairGenerator((KeyPairGeneratorSpi) o, algorithm); + } + else if (o instanceof KeyPairGenerator) + { + result = (KeyPairGenerator) o; + result.algorithm = algorithm; + } + result.provider = provider; + return result; + } + + /** + * Returns the provider of this key pair generator object. + * + * @return the provider of this key pair generator object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes the key pair generator for a certain keysize using a default + * parameter set and the {@link SecureRandom} implementation of the + * highest-priority installed provider as the source of randomness. (If none + * of the installed providers supply an implementation of {@link SecureRandom}, + * a system-provided source of randomness is used.) + * + * @param keysize the keysize. This is an algorithm-specific metric, such as + * modulus length, specified in number of bits. + * @throws InvalidParameterException if the keysize is not supported by this + * <code>KeyPairGenerator</code> object. + */ + public void initialize(int keysize) + { + initialize(keysize, new SecureRandom()); + } + + /** + * Initializes the key pair generator for a certain keysize with the given + * source of randomness (and a default parameter set). + * + * @param keysize the keysize. This is an algorithm-specific metric, such as + * modulus length, specified in number of bits. + * @param random the source of randomness. + * @throws InvalidParameterException if the <code>keysize</code> is not + * supported by this <code>KeyPairGenerator</code> object. + * @since 1.2 + */ + public void initialize(int keysize, SecureRandom random) + { + initialize(keysize, random); + } + + /** + * <p>Initializes the key pair generator using the specified parameter set and + * the {@link SecureRandom} implementation of the highest-priority installed + * provider as the source of randomness. (If none of the installed providers + * supply an implementation of {@link SecureRandom}, a system-provided source + * of randomness is used.)</p> + * + * <p>This concrete method has been added to this previously-defined abstract + * class. This method calls the + * {@link KeyPairGeneratorSpi#initialize(AlgorithmParameterSpec, SecureRandom)} + * initialize method, passing it <code>params</code> and a source of + * randomness (obtained from the highest-priority installed provider or + * system-provided if none of the installed providers supply one). That + * initialize method always throws an {@link UnsupportedOperationException} + * if it is not overridden by the provider.</p> + * + * @param params the parameter set used to generate the keys. + * @throws InvalidAlgorithmParameterException if the given parameters are + * inappropriate for this key pair generator. + * @since 1.2 + */ + public void initialize(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + initialize(params, new SecureRandom()); + } + + /** + * <p>Initializes the key pair generator with the given parameter set and + * source of randomness.</p> + * + * <p>This concrete method has been added to this previously-defined abstract + * class. This method calls the + * {@link KeyPairGeneratorSpi#initialize(AlgorithmParameterSpec, SecureRandom)} + * initialize method, passing it <code>params</code> and <code>random</code>. + * That initialize method always throws an {@link UnsupportedOperationException} + * if it is not overridden by the provider.</p> + * + * @param params the parameter set used to generate the keys. + * @param random the source of randomness. + * @throws InvalidAlgorithmParameterException if the given parameters are + * inappropriate for this key pair generator. + * @since 1.2 + */ + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + super.initialize(params, random); + } + + /** + * <p>Generates a key pair.</p> + * + * <p>If this <code>KeyPairGenerator</code> has not been initialized + * explicitly, provider-specific defaults will be used for the size and other + * (algorithm-specific) values of the generated keys.</p> + * + * <p>This will generate a new key pair every time it is called.</p> + * + * <p>This method is functionally equivalent to {@link #generateKeyPair()}.</p> + * + * @return the generated key pair. + * @since 1.2 + */ + public final KeyPair genKeyPair() + { + try + { + return getInstance("DSA", "GNU").generateKeyPair(); + } + catch (Exception e) + { + System.err.println("genKeyPair failed: " + e); + e.printStackTrace(); + return null; + } + } + + /** + * <p>Generates a key pair.</p> + * + * <p>If this <code>KeyPairGenerator</code> has not been initialized + * explicitly, provider-specific defaults will be used for the size and other + * (algorithm-specific) values of the generated keys.</p> + * + * <p>This will generate a new key pair every time it is called.</p> + * + * <p>This method is functionally equivalent to {@link #genKeyPair()}.</p> + * + * @return the generated key pair. + */ + public KeyPair generateKeyPair() + { + return genKeyPair(); + } +} diff --git a/libjava/classpath/java/security/KeyPairGeneratorSpi.java b/libjava/classpath/java/security/KeyPairGeneratorSpi.java new file mode 100644 index 00000000000..689fbec128e --- /dev/null +++ b/libjava/classpath/java/security/KeyPairGeneratorSpi.java @@ -0,0 +1,102 @@ +/* KeyPairGeneratorSpi.java --- Key Pair Generator SPI Class + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.security.spec.AlgorithmParameterSpec; + +/** + KeyPairGeneratorSpi is the interface used to generate key pairs + for security algorithms. + + @author Mark Benvenuto + */ +public abstract class KeyPairGeneratorSpi +{ + /** + Constructs a new KeyPairGeneratorSpi + */ + public KeyPairGeneratorSpi() + { + } + + /** + Initialize the KeyPairGeneratorSpi with the specified + key size and source of randomness + + @param keysize size of the key to generate + @param random A SecureRandom source of randomness + */ + public abstract void initialize(int keysize, SecureRandom random); + + /** + Initialize the KeyPairGeneratorSpi with the specified + AlgorithmParameterSpec and source of randomness + + This is a concrete method. It may be overridden by the provider + and if the AlgorithmParameterSpec class is invalid + throw InvalidAlgorithmParameterException. By default this + method just throws UnsupportedOperationException. + + @param params A AlgorithmParameterSpec to intialize with + @param random A SecureRandom source of randomness + + @throws InvalidAlgorithmParameterException + */ + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new java.lang.UnsupportedOperationException(); + } + + /** + Generates a KeyPair according the rules for the algorithm. + Unless intialized, algorithm defaults will be used. It + creates a unique key pair each time. + + @return a key pair + */ + public abstract KeyPair generateKeyPair(); + + /** + * We override clone here to make it accessible for use by + * DummyKeyPairGenerator. + */ + protected Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/KeyStore.java b/libjava/classpath/java/security/KeyStore.java new file mode 100644 index 00000000000..696448728ff --- /dev/null +++ b/libjava/classpath/java/security/KeyStore.java @@ -0,0 +1,507 @@ +/* KeyStore.java --- Key Store Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Enumeration; + +/** + * Keystore represents an in-memory collection of keys and + * certificates. There are two types of entries: + * + * <dl> + * <dt>Key Entry</dt> + * + * <dd><p>This type of keystore entry store sensitive crytographic key + * information in a protected format.Typically this is a secret + * key or a private key with a certificate chain.</p></dd> + * + * <dt>Trusted Ceritificate Entry</dt> + * + * <dd><p>This type of keystore entry contains a single public key + * certificate belonging to annother entity. It is called trusted + * because the keystore owner trusts that the certificates + * belongs to the subject (owner) of the certificate.</p></dd> + * </dl> + * + * <p>Entries in a key store are referred to by their "alias": a simple + * unique string. + * + * <p>The structure and persistentence of the key store is not + * specified. Any method could be used to protect sensitive + * (private or secret) keys. Smart cards or integrated + * cryptographic engines could be used or the keystore could + * be simply stored in a file.</p> + * + * @see java.security.cert.Certificate + * @see Key + */ +public class KeyStore +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for key stores. */ + private static final String KEY_STORE = "KeyStore"; + + private KeyStoreSpi keyStoreSpi; + private Provider provider; + private String type; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + Creates an instance of KeyStore + + @param keyStoreSpi A KeyStore engine to use + @param provider A provider to use + @param type The type of KeyStore + */ + protected KeyStore(KeyStoreSpi keyStoreSpi, Provider provider, String type) + { + this.keyStoreSpi = keyStoreSpi; + this.provider = provider; + this.type = type; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Gets an instance of the KeyStore class representing + * the specified keystore. If the type is not + * found then, it throws KeyStoreException. + * + * @param type the type of keystore to choose + * @return a KeyStore repesenting the desired type + * @throws KeyStoreException if the type of keystore is not implemented + * by providers or the implementation cannot be instantiated. + */ + public static KeyStore getInstance(String type) throws KeyStoreException + { + Provider[] p = Security.getProviders(); + + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(type, p[i]); + } + catch (KeyStoreException e) + { + // Ignore. + } + } + + throw new KeyStoreException(type); + } + + /** + * Gets an instance of the KeyStore class representing + * the specified key store from the specified provider. + * If the type is not found then, it throws KeyStoreException. + * If the provider is not found, then it throws + * NoSuchProviderException. + * + * @param type the type of keystore to choose + * @param provider the provider name + * @return a KeyStore repesenting the desired type + * @throws KeyStoreException if the type of keystore is not + * implemented by the given provider + * @throws NoSuchProviderException if the provider is not found + * @throws IllegalArgumentException if the provider string is + * null or empty + */ + public static KeyStore getInstance(String type, String provider) + throws KeyStoreException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(type, p); + } + + /** + * Gets an instance of the KeyStore class representing + * the specified key store from the specified provider. + * If the type is not found then, it throws KeyStoreException. + * If the provider is not found, then it throws + * NoSuchProviderException. + * + * @param type the type of keystore to choose + * @param provider the keystore provider + * @return a KeyStore repesenting the desired type + * @throws KeyStoreException if the type of keystore is not + * implemented by the given provider + * @throws IllegalArgumentException if the provider object is null + * @since 1.4 + */ + public static KeyStore getInstance(String type, Provider provider) + throws KeyStoreException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + try + { + return new KeyStore( + (KeyStoreSpi) Engine.getInstance(KEY_STORE, type, provider), + provider, type); + } + catch (NoSuchAlgorithmException nsae) + { + throw new KeyStoreException(type); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new KeyStoreException(type); + } + catch (ClassCastException cce) + { + throw new KeyStoreException(type); + } + } + + /** + * Returns the default KeyStore type. This method looks up the + * type in <JAVA_HOME>/lib/security/java.security with the + * property "keystore.type" or if that fails then "jks" . + */ + public static final String getDefaultType() + { + // Security reads every property in java.security so it + // will return this property if it exists. + String tmp = Security.getProperty("keystore.type"); + + if (tmp == null) + tmp = "jks"; + + return tmp; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + Gets the provider that the class is from. + + @return the provider of this class + */ + public final Provider getProvider() + { + return provider; + } + + /** + Returns the type of the KeyStore supported + + @return A string with the type of KeyStore + */ + public final String getType() + { + return type; + } + + /** + Returns the key associated with given alias using the + supplied password. + + @param alias an alias for the key to get + @param password password to access key with + + @return the requested key, or null otherwise + + @throws NoSuchAlgorithmException if there is no algorithm + for recovering the key + @throws UnrecoverableKeyException key cannot be reocovered + (wrong password). + */ + public final Key getKey(String alias, char[]password) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException + { + return keyStoreSpi.engineGetKey(alias, password); + } + + /** + Gets a Certificate chain for the specified alias. + + @param alias the alias name + + @return a chain of Certificates ( ordered from the user's + certificate to the Certificate Authority's ) or + null if the alias does not exist or there is no + certificate chain for the alias ( the alias refers + to a trusted certificate entry or there is no entry). + */ + public final java.security.cert. + Certificate[] getCertificateChain(String alias) throws KeyStoreException + { + return keyStoreSpi.engineGetCertificateChain(alias); + } + + /** + Gets a Certificate for the specified alias. + + If there is a trusted certificate entry then that is returned. + it there is a key entry with a certificate chain then the + first certificate is return or else null. + + @param alias the alias name + + @return a Certificate or null if the alias does not exist + or there is no certificate for the alias + */ + public final java.security.cert.Certificate getCertificate(String alias) + throws KeyStoreException + { + return keyStoreSpi.engineGetCertificate(alias); + } + + /** + Gets entry creation date for the specified alias. + + @param alias the alias name + + @returns the entry creation date or null + */ + public final Date getCreationDate(String alias) throws KeyStoreException + { + return keyStoreSpi.engineGetCreationDate(alias); + } + + /** + Assign the key to the alias in the keystore, protecting it + with the given password. It will overwrite an existing + entry and if the key is a PrivateKey, also add the + certificate chain representing the corresponding public key. + + @param alias the alias name + @param key the key to add + @password the password to protect with + @param chain the certificate chain for the corresponding + public key + + @throws KeyStoreException if it fails + */ + public final void setKeyEntry(String alias, Key key, char[]password, + java.security.cert. + Certificate[]chain) throws KeyStoreException + { + keyStoreSpi.engineSetKeyEntry(alias, key, password, chain); + } + + /** + Assign the key to the alias in the keystore. It will overwrite + an existing entry and if the key is a PrivateKey, also + add the certificate chain representing the corresponding + public key. + + @param alias the alias name + @param key the key to add + @param chain the certificate chain for the corresponding + public key + + @throws KeyStoreException if it fails + */ + public final void setKeyEntry(String alias, byte[]key, + java.security.cert. + Certificate[]chain) throws KeyStoreException + { + keyStoreSpi.engineSetKeyEntry(alias, key, chain); + } + + /** + Assign the certificate to the alias in the keystore. It + will overwrite an existing entry. + + @param alias the alias name + @param cert the certificate to add + + @throws KeyStoreException if it fails + */ + public final void setCertificateEntry(String alias, + java.security.cert. + Certificate cert) throws + KeyStoreException + { + keyStoreSpi.engineSetCertificateEntry(alias, cert); + } + + /** + Deletes the entry for the specified entry. + + @param alias the alias name + + @throws KeyStoreException if it fails + */ + public final void deleteEntry(String alias) throws KeyStoreException + { + keyStoreSpi.engineDeleteEntry(alias); + } + + /** + Generates a list of all the aliases in the keystore. + + @return an Enumeration of the aliases + */ + public final Enumeration aliases() throws KeyStoreException + { + return keyStoreSpi.engineAliases(); + } + + /** + Determines if the keystore contains the specified alias. + + @param alias the alias name + + @return true if it contains the alias, false otherwise + */ + public final boolean containsAlias(String alias) throws KeyStoreException + { + return keyStoreSpi.engineContainsAlias(alias); + } + + /** + Returns the number of entries in the keystore. + + @returns the number of keystore entries. + */ + public final int size() throws KeyStoreException + { + return keyStoreSpi.engineSize(); + } + + /** + Determines if the keystore contains a key entry for + the specified alias. + + @param alias the alias name + + @return true if it is a key entry, false otherwise + */ + public final boolean isKeyEntry(String alias) throws KeyStoreException + { + return keyStoreSpi.engineIsKeyEntry(alias); + } + + + /** + Determines if the keystore contains a certificate entry for + the specified alias. + + @param alias the alias name + + @return true if it is a certificate entry, false otherwise + */ + public final boolean isCertificateEntry(String alias) + throws KeyStoreException + { + return keyStoreSpi.engineIsCertificateEntry(alias); + } + + /** + Determines if the keystore contains the specified certificate + entry and returns the alias. + + It checks every entry and for a key entry checks only the + first certificate in the chain. + + @param cert Certificate to look for + + @return alias of first matching certificate, null if it + does not exist. + */ + public final String getCertificateAlias(java.security.cert.Certificate cert) + throws KeyStoreException + { + return keyStoreSpi.engineGetCertificateAlias(cert); + } + + /** + Stores the keystore in the specified output stream and it + uses the specified key it keep it secure. + + @param stream the output stream to save the keystore to + @param password the password to protect the keystore integrity with + + @throws IOException if an I/O error occurs. + @throws NoSuchAlgorithmException the data integrity algorithm + used cannot be found. + @throws CertificateException if any certificates could not be + stored in the output stream. + */ + public final void store(OutputStream stream, char[]password) + throws KeyStoreException, IOException, NoSuchAlgorithmException, + CertificateException + { + keyStoreSpi.engineStore(stream, password); + } + + /** + Loads the keystore from the specified input stream and it + uses the specified password to check for integrity if supplied. + + @param stream the input stream to load the keystore from + @param password the password to check the keystore integrity with + + @throws IOException if an I/O error occurs. + @throws NoSuchAlgorithmException the data integrity algorithm + used cannot be found. + @throws CertificateException if any certificates could not be + stored in the output stream. + */ + public final void load(InputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + keyStoreSpi.engineLoad(stream, password); + } + +} diff --git a/libjava/classpath/java/security/KeyStoreException.java b/libjava/classpath/java/security/KeyStoreException.java new file mode 100644 index 00000000000..9a0a5354d2c --- /dev/null +++ b/libjava/classpath/java/security/KeyStoreException.java @@ -0,0 +1,70 @@ +/* KeyStoreException.java -- Indicates a problem with the key store + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Indicates a problem with the key store. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class KeyStoreException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1119353179322377262L; + + /** + * Create a new instance detailed error message. + */ + public KeyStoreException() + { + } + + /** + * Create a new instance with a detailed error message. + * + * @param msg the descriptive error message + */ + public KeyStoreException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/KeyStoreSpi.java b/libjava/classpath/java/security/KeyStoreSpi.java new file mode 100644 index 00000000000..a16008f9960 --- /dev/null +++ b/libjava/classpath/java/security/KeyStoreSpi.java @@ -0,0 +1,275 @@ +/* KeyStoreSpi.java --- Key Store Service Provider Interface + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Enumeration; + +/** + * KeyStoreSpi is the Service Provider Interface (SPI) for the + * KeyStore class. This is the interface for providers to + * supply to implement a keystore for a particular keystore + * type. + * + * @since 1.2 + * @author Mark Benvenuto + */ +public abstract class KeyStoreSpi +{ + /** + * Constructs a new KeyStoreSpi + */ + public KeyStoreSpi() + { + } + + /** + * Returns the key associated with given alias using the + * supplied password. + * + * @param alias an alias for the key to get + * @param password password to access key with + * + * @return the requested key, or null otherwise + * + * @throws NoSuchAlgorithmException if there is no algorithm + * for recovering the key + * @throws UnrecoverableKeyException key cannot be reocovered + * (wrong password). + */ + public abstract Key engineGetKey(String alias, char[]password) + throws NoSuchAlgorithmException, UnrecoverableKeyException; + + /** + * Gets a Certificate chain for the specified alias. + * + * @param alias the alias name + * + * @return a chain of Certificates ( ordered from the user's + * certificate to the Certificate Authority's ) or + * null if the alias does not exist or there is no + * certificate chain for the alias ( the alias refers + * to a trusted certificate entry or there is no entry). + */ + public abstract java.security.cert. + Certificate[] engineGetCertificateChain(String alias); + + + /** + * Gets a Certificate for the specified alias. + * + * If there is a trusted certificate entry then that is returned. + * it there is a key entry with a certificate chain then the + * first certificate is return or else null. + * + * @param alias the alias name + * + * @return a Certificate or null if the alias does not exist + * or there is no certificate for the alias + */ + public abstract java.security.cert. + Certificate engineGetCertificate(String alias); + + /** + * Gets entry creation date for the specified alias. + * + * @param alias the alias name + * + * @returns the entry creation date or null + */ + public abstract Date engineGetCreationDate(String alias); + + /** + * Assign the key to the alias in the keystore, protecting it + * with the given password. It will overwrite an existing + * entry and if the key is a PrivateKey, also add the + * certificate chain representing the corresponding public key. + * + * @param alias the alias name + * @param key the key to add + * @password the password to protect with + * @param chain the certificate chain for the corresponding + * public key + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetKeyEntry(String alias, Key key, + char[]password, + java.security.cert. + Certificate[]chain) throws + KeyStoreException; + + /** + * Assign the key to the alias in the keystore. It will overwrite + * an existing entry and if the key is a PrivateKey, also + * add the certificate chain representing the corresponding + * public key. + * + * @param alias the alias name + * @param key the key to add + * @param chain the certificate chain for the corresponding + * public key + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetKeyEntry(String alias, byte[]key, + java.security.cert. + Certificate[]chain) throws + KeyStoreException; + + + /** + * Assign the certificate to the alias in the keystore. It + * will overwrite an existing entry. + * + * @param alias the alias name + * @param cert the certificate to add + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetCertificateEntry(String alias, + java.security.cert. + Certificate cert) throws + KeyStoreException; + + /** + * Deletes the entry for the specified entry. + * + * @param alias the alias name + * + * @throws KeyStoreException if it fails + */ + public abstract void engineDeleteEntry(String alias) + throws KeyStoreException; + + /** + * Generates a list of all the aliases in the keystore. + * + * @return an Enumeration of the aliases + */ + public abstract Enumeration engineAliases(); + + /** + * Determines if the keystore contains the specified alias. + * + * @param alias the alias name + * + * @return true if it contains the alias, false otherwise + */ + public abstract boolean engineContainsAlias(String alias); + + /** + * Returns the number of entries in the keystore. + * + * @returns the number of keystore entries. + */ + public abstract int engineSize(); + + /** + * Determines if the keystore contains a key entry for + * the specified alias. + * + * @param alias the alias name + * + * @return true if it is a key entry, false otherwise + */ + public abstract boolean engineIsKeyEntry(String alias); + + /** + * Determines if the keystore contains a certificate entry for + * the specified alias. + * + * @param alias the alias name + * + * @return true if it is a certificate entry, false otherwise + */ + public abstract boolean engineIsCertificateEntry(String alias); + + /** + * Determines if the keystore contains the specified certificate + * entry and returns the alias. + * + * It checks every entry and for a key entry checks only the + * first certificate in the chain. + * + * @param cert Certificate to look for + * + * @return alias of first matching certificate, null if it + * does not exist. + */ + public abstract String engineGetCertificateAlias(java.security.cert. + Certificate cert); + + /** + * Stores the keystore in the specified output stream and it + * uses the specified key it keep it secure. + * + * @param stream the output stream to save the keystore to + * @param password the password to protect the keystore integrity with + * + * @throws IOException if an I/O error occurs. + * @throws NoSuchAlgorithmException the data integrity algorithm + * used cannot be found. + * @throws CertificateException if any certificates could not be + * stored in the output stream. + */ + public abstract void engineStore(OutputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException; + + + /** + * Loads the keystore from the specified input stream and it + * uses the specified password to check for integrity if supplied. + * + * @param stream the input stream to load the keystore from + * @param password the password to check the keystore integrity with + * + * @throws IOException if an I/O error occurs. + * @throws NoSuchAlgorithmException the data integrity algorithm + * used cannot be found. + * @throws CertificateException if any certificates could not be + * stored in the output stream. + */ + public abstract void engineLoad(InputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException; +} diff --git a/libjava/classpath/java/security/MessageDigest.java b/libjava/classpath/java/security/MessageDigest.java new file mode 100644 index 00000000000..8684f2083b1 --- /dev/null +++ b/libjava/classpath/java/security/MessageDigest.java @@ -0,0 +1,413 @@ +/* MessageDigest.java --- The message digest interface. + Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.java.security.Engine; + +/** + * <p>This <code>MessageDigest</code> class provides applications the + * functionality of a message digest algorithm, such as <i>MD5</i> or <i>SHA</i>. + * Message digests are secure one-way hash functions that take arbitrary-sized + * data and output a fixed-length hash value.</p> + * + * <p>A <code>MessageDigest</code> object starts out initialized. The data is + * processed through it using the <code>update()</code> methods. At any point + * <code>reset()</code> can be called to reset the digest. Once all the data to + * be updated has been updated, one of the <code>digest()</code> methods should + * be called to complete the hash computation.</p> + * + * <p>The <code>digest()</code> method can be called <b>once</b> for a given + * number of updates. After <code>digest()</code> has been called, the + * <code>MessageDigest</code> object is <b>reset</b> to its initialized state. + * </p> + * + * <p>Implementations are free to implement the {@link Cloneable} interface. + * Client applications can test cloneability by attempting cloning and catching + * the {@link CloneNotSupportedException}: + * + * <pre> + * MessageDigest md = MessageDigest.getInstance("SHA"); + * try + * { + * md.update(toChapter1); + * MessageDigest tc1 = md.clone(); + * byte[] toChapter1Digest = tc1.digest(); + * md.update(toChapter2); + * // ... + * } + * catch (CloneNotSupportedException x) + * { + * throw new DigestException("couldn't make digest of partial content"); + * } + * </pre> + * + * <p>Note that if a given implementation is not cloneable, it is still possible + * to compute intermediate digests by instantiating several instances, if the + * number of digests is known in advance.</p> + * + * <p>Note that this class is abstract and extends from {@link MessageDigestSpi} + * for historical reasons. Application developers should only take notice of the + * methods defined in this <code>MessageDigest</code> class; all the methods in + * the superclass are intended for cryptographic service providers who wish to + * supply their own implementations of message digest algorithms.</p> + * + * @see MessageDigestSpi + * @see Provider + * @since JDK 1.1 + */ +public abstract class MessageDigest extends MessageDigestSpi +{ + /** The service name for message digests. */ + private static final String MESSAGE_DIGEST = "MessageDigest"; + + private String algorithm; + Provider provider; + private byte[] lastDigest; + + /** + * Creates a message digest with the specified algorithm name. + * + * @param algorithm the standard name of the digest algorithm. + * See Appendix A in the Java Cryptography Architecture API + * Specification & Reference for information about standard + * algorithm names. + */ + protected MessageDigest(String algorithm) + { + this.algorithm = algorithm; + provider = null; + } + + /** + * Generates a <code>MessageDigest</code> object that implements the specified + * digest algorithm. If the default provider package provides an + * implementation of the requested digest algorithm, an instance of + * <code>MessageDigest</code> containing that implementation is returned. If + * the algorithm is not available in the default package, other packages are + * searched. + * + * @param algorithm the name of the algorithm requested. See Appendix A in the + * Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @return a Message Digest object implementing the specified algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * caller's environment. + */ + public static MessageDigest getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException ignored) + { + // Ignore. + } + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Generates a <code>MessageDigest</code> object implementing the specified + * algorithm, as supplied from the specified provider, if such an algorithm is + * available from the provider. + * + * @param algorithm the name of the algorithm requested. See Appendix A in the + * Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the name of the provider. + * @return a Message Digest object implementing the specified algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * package supplied by the requested provider. + * @throws NoSuchProviderException if the provider is not available in the + * environment. + * @throws IllegalArgumentException if the provider name is null or empty. + * @see Provider + */ + public static MessageDigest getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates a <code>MessageDigest</code> object implementing the specified + * algorithm, as supplied from the specified provider, if such an algorithm + * is available from the provider. Note: the provider doesn't have to be + * registered. + * + * @param algorithm the name of the algorithm requested. See Appendix A in + * the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the provider. + * @return a Message Digest object implementing the specified algorithm. + * @throws NoSuchAlgorithmException if the <code>algorithm</code> is not + * available in the package supplied by the requested <code>provider</code>. + * @throws IllegalArgumentException if the <code>provider</code> is + * <code>null</code>. + * @since 1.4 + * @see Provider + */ + public static MessageDigest getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + MessageDigest result = null; + Object o = null; + try + { + o = Engine.getInstance(MESSAGE_DIGEST, algorithm, provider); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + + if (o instanceof MessageDigestSpi) + { + result = new DummyMessageDigest((MessageDigestSpi) o, algorithm); + } + else if (o instanceof MessageDigest) + { + result = (MessageDigest) o; + result.algorithm = algorithm; + } + else + { + throw new NoSuchAlgorithmException(algorithm); + } + result.provider = provider; + return result; + } + + /** + * Returns the provider of this message digest object. + * + * @return the provider of this message digest object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Updates the digest using the specified byte. + * + * @param input the byte with which to update the digest. + */ + public void update(byte input) + { + engineUpdate(input); + } + + /** + * Updates the digest using the specified array of bytes, starting at the + * specified offset. + * + * @param input the array of bytes. + * @param offset the offset to start from in the array of bytes. + * @param len the number of bytes to use, starting at offset. + */ + public void update(byte[] input, int offset, int len) + { + engineUpdate(input, offset, len); + } + + /** + * Updates the digest using the specified array of bytes. + * + * @param input the array of bytes. + */ + public void update(byte[] input) + { + engineUpdate(input, 0, input.length); + } + + /** + * Completes the hash computation by performing final operations such as + * padding. The digest is reset after this call is made. + * + * @return the array of bytes for the resulting hash value. + */ + public byte[] digest() + { + return lastDigest = engineDigest(); + } + + /** + * Completes the hash computation by performing final operations such as + * padding. The digest is reset after this call is made. + * + * @param buf An output buffer for the computed digest. + * @param offset The offset into the output buffer to begin storing the digest. + * @param len The number of bytes within buf allotted for the digest. + * @return The number of bytes placed into buf. + * @throws DigestException if an error occurs. + */ + public int digest(byte[] buf, int offset, int len) throws DigestException + { + return engineDigest(buf, offset, len); + } + + /** + * Performs a final update on the digest using the specified array of bytes, + * then completes the digest computation. That is, this method first calls + * <code>update(input)</code>, passing the input array to the <code>update() + * </code> method, then calls <code>digest()</code>. + * + * @param input the input to be updated before the digest is completed. + * @return the array of bytes for the resulting hash value. + */ + public byte[] digest(byte[] input) + { + update(input); + return digest(); + } + + /** + * Returns a string representation of this message digest object. + * + * @return a string representation of the object. + */ + public String toString() + { + return (getClass()).getName() + " Message Digest <" + digestToString() + ">"; + } + + /** + * Compares two digests for equality. Does a simple byte compare. + * + * @param digesta one of the digests to compare. + * @param digestb the other digest to compare. + * @return <code>true</code> if the digests are equal, <code>false</code> + * otherwise. + */ + public static boolean isEqual(byte[] digesta, byte[] digestb) + { + if (digesta.length != digestb.length) + return false; + + for (int i = digesta.length - 1; i >= 0; --i) + if (digesta[i] != digestb[i]) + return false; + + return true; + } + + /** Resets the digest for further use. */ + public void reset() + { + engineReset(); + } + + /** + * Returns a string that identifies the algorithm, independent of + * implementation details. The name should be a standard Java Security name + * (such as <code>"SHA"</code>, <code>"MD5"</code>, and so on). See Appendix + * A in the Java Cryptography Architecture API Specification & Reference + * for information about standard algorithm names. + * + * @return the name of the algorithm. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns the length of the digest in bytes, or <code>0</code> if this + * operation is not supported by the provider and the implementation is not + * cloneable. + * + * @return the digest length in bytes, or <code>0</code> if this operation is + * not supported by the provider and the implementation is not cloneable. + * @since 1.2 + */ + public final int getDigestLength() + { + return engineGetDigestLength(); + } + + /** + * Returns a clone if the implementation is cloneable. + * + * @return a clone if the implementation is cloneable. + * @throws CloneNotSupportedException if this is called on an implementation + * that does not support {@link Cloneable}. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } + + private String digestToString() + { + byte[] digest = lastDigest; + + if (digest == null) + return "incomplete"; + + StringBuffer buf = new StringBuffer(); + int len = digest.length; + for (int i = 0; i < len; ++i) + { + byte b = digest[i]; + byte high = (byte) ((b & 0xff) >>> 4); + byte low = (byte) (b & 0xf); + + buf.append(high > 9 ? ('a' - 10) + high : '0' + high); + buf.append(low > 9 ? ('a' - 10) + low : '0' + low); + } + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/MessageDigestSpi.java b/libjava/classpath/java/security/MessageDigestSpi.java new file mode 100644 index 00000000000..df3bd3ead02 --- /dev/null +++ b/libjava/classpath/java/security/MessageDigestSpi.java @@ -0,0 +1,155 @@ +/* MessageDigestSpi.java --- The message digest service provider interface. + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + This is the Service Provider Interface (SPI) for MessageDigest + class in java.security. It provides the back end functionality + for the MessageDigest class so that it can compute message + hashes. The default hashes are SHA-1 and MD5. A message hash + takes data of arbitrary length and produces a unique number + representing it. + + Cryptography service providers who want to implement their + own message digest hashes need only to subclass this class. + + The implementation of a Cloneable interface is left to up to + the programmer of a subclass. + + @version 0.0 + + @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class MessageDigestSpi +{ + /** + Default constructor of the MessageDigestSpi class + */ + public MessageDigestSpi() + { + } + + /** + Returns the length of the digest. It may be overridden by the + provider to return the length of the digest. Default is to + return 0. It is concrete for backwards compatibility with JDK1.1 + message digest classes. + + @return Length of Digest in Bytes + + @since 1.2 + */ + protected int engineGetDigestLength() + { + return 0; + } + + /** + Updates the digest with the specified byte. + + @param input the byte to update digest with + */ + protected abstract void engineUpdate(byte input); + + + /** + Updates the digest with the specified bytes starting with the + offset and proceeding for the specified length. + + @param input the byte array to update digest with + @param offset the offset of the byte to start with + @param len the number of the bytes to update with + */ + protected abstract void engineUpdate(byte[]input, int offset, int len); + + /** + Computes the final digest of the stored bytes and returns + them. It performs any necessary padding. The message digest + should reset sensitive data after performing the digest. + + @return An array of bytes containing the digest + */ + protected abstract byte[] engineDigest(); + + /** + Computes the final digest of the stored bytes and returns + them. It performs any necessary padding. The message digest + should reset sensitive data after performing the digest. This + method is left concrete for backwards compatibility with JDK1.1 + message digest classes. + + @param buf An array of bytes to store the digest + @param offset An offset to start storing the digest at + @param len The length of the buffer + @return Returns the length of the buffer + + @since 1.2 + */ + protected int engineDigest(byte[]buf, int offset, int len) + throws DigestException + { + if (engineGetDigestLength() > len) + throw new DigestException("Buffer is too small."); + + byte[] tmp = engineDigest(); + if (tmp.length > len) + throw new DigestException("Buffer is too small"); + + System.arraycopy(tmp, 0, buf, offset, tmp.length); + return tmp.length; + } + + /** + Resets the digest engine. Reinitializes internal variables + and clears sensitive data. + */ + protected abstract void engineReset(); + + /** + Returns a clone of this class. + + If cloning is not supported, then by default the class throws a + CloneNotSupportedException. The MessageDigestSpi provider + implementation has to overload this class in order to be + cloneable. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/NoSuchAlgorithmException.java b/libjava/classpath/java/security/NoSuchAlgorithmException.java new file mode 100644 index 00000000000..412d14a16d7 --- /dev/null +++ b/libjava/classpath/java/security/NoSuchAlgorithmException.java @@ -0,0 +1,70 @@ +/* NoSuchAlgorithmException.java -- an algorithm was not available + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the requested security algorithm is + * not available + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NoSuchAlgorithmException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7443947487218346562L; + + /** + * Create a new instance with no descriptive error message. + */ + public NoSuchAlgorithmException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public NoSuchAlgorithmException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/NoSuchProviderException.java b/libjava/classpath/java/security/NoSuchProviderException.java new file mode 100644 index 00000000000..bd26df5ef07 --- /dev/null +++ b/libjava/classpath/java/security/NoSuchProviderException.java @@ -0,0 +1,70 @@ +/* NoSuchProviderException.java -- thrown when a provider is not found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the requested security provider is + * not available. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NoSuchProviderException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8488111756688534474L; + + /** + * Create a new instance with no descriptive error message. + */ + public NoSuchProviderException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public NoSuchProviderException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/Permission.java b/libjava/classpath/java/security/Permission.java new file mode 100644 index 00000000000..48f4d52a18c --- /dev/null +++ b/libjava/classpath/java/security/Permission.java @@ -0,0 +1,187 @@ +/* Permission.java -- The superclass for all permission objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; + +/** + * This class is the abstract superclass of all classes that implement + * the concept of a permission. A permission consists of a permission name + * and optionally a list of actions that relate to the permission. The + * actual meaning of the name of the permission is defined only in the + * context of a subclass. It may name a resource to which access permissions + * are granted (for example, the name of a file) or it might represent + * something else entirely. Similarly, the action list only has meaning + * within the context of a subclass. Some permission names may have no + * actions associated with them. That is, you either have the permission + * or you don't. + * + * <p>The most important method in this class is <code>implies</code>. This + * checks whether if one has this permission, then the specified + * permission is also implied. As a conceptual example, consider the + * permissions "Read All Files" and "Read File foo". The permission + * "Read All Files" implies that the caller has permission to read the + * file foo. + * + * <p><code>Permission</code>'s must be immutable - do not change their + * state after creation. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permissions + * @see PermissionCollection + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class Permission implements Guard, Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5636570222231596674L; + + /** + * This is the name assigned to this permission object. + * + * @serial the name of the permission + */ + private String name; + + /** + * Create an instance with the specified name. + * + * @param name the permission name + */ + public Permission(String name) + { + this.name = name; + } + + /** + * This method implements the <code>Guard</code> interface for this class. + * It calls the <code>checkPermission</code> method in + * <code>SecurityManager</code> with this <code>Permission</code> as its + * argument. This method returns silently if the security check succeeds + * or throws an exception if it fails. + * + * @param obj the <code>Object</code> being guarded - ignored by this class + * @throws SecurityException if the security check fails + * @see GuardedObject + * @see SecurityManager#checkPermission(Permission) + */ + public void checkGuard(Object obj) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(this); + } + + /** + * This method tests whether this <code>Permission</code> implies that the + * specified <code>Permission</code> is also granted. + * + * @param perm the <code>Permission</code> to test against + * @return true if perm is implied by this + */ + public abstract boolean implies(Permission perm); + + /** + * Check to see if this object equals obj. Use <code>implies</code>, rather + * than <code>equals</code>, when making access control decisions. + * + * @param obj the object to compare to + */ + public abstract boolean equals(Object obj); + + /** + * This method returns a hash code for this <code>Permission</code>. It + * must satisfy the contract of <code>Object.hashCode</code>: it must be + * the same for all objects that equals considers to be the same. + * + * @return a hash value + */ + public abstract int hashCode(); + + /** + * Get the name of this <code>Permission</code>. + * + * @return the name + */ + public final String getName() + { + return name; + } + + /** + * This method returns the list of actions for this <code>Permission</code> + * as a <code>String</code>. The string should be in canonical order, for + * example, both <code>new FilePermission(f, "write,read")</code> and + * <code>new FilePermission(f, "read,write")</code> have the action list + * "read,write". + * + * @return the action list for this <code>Permission</code> + */ + public abstract String getActions(); + + /** + * This method returns an empty <code>PermissionCollection</code> object + * that can store permissions of this type, or <code>null</code> if no + * such collection is defined. Subclasses must override this to provide + * an appropriate collection when one is needed to accurately calculate + * <code>implies</code>. + * + * @return a new <code>PermissionCollection</code> + */ + public PermissionCollection newPermissionCollection() + { + return null; + } + + /** + * This method returns a <code>String</code> representation of this + * <code>Permission</code> object. This is in the format: + * <code>'(' + getClass().getName() + ' ' + getName() + ' ' + getActions + * + ')'</code>. + * + * @return this object as a <code>String</code> + */ + public String toString() + { + return '(' + getClass().getName() + ' ' + getName() + ' ' + + getActions() + ')'; + } +} // class Permission diff --git a/libjava/classpath/java/security/PermissionCollection.java b/libjava/classpath/java/security/PermissionCollection.java new file mode 100644 index 00000000000..4e8ffe57948 --- /dev/null +++ b/libjava/classpath/java/security/PermissionCollection.java @@ -0,0 +1,167 @@ +/* PermissionCollection.java -- A collection of permission objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; +import java.util.Enumeration; + +/** + * This class models a group of Java permissions. It has convenient + * methods for determining whether or not a given permission is implied + * by any of the permissions in this collection. + * + * <p>Some care must be taken in storing permissions. First, a collection of + * the appropriate type must be created. This is done by calling the + * <code>newPermissionCollection</code> method on an object of the + * permission class you wish to add to the collection. If this method + * returns <code>null</code>, any type of <code>PermissionCollection</code> + * can be used to store permissions of that type. However, if a + * <code>PermissionCollection</code> collection object is returned, that + * type must be used. + * + * <p>A <code>PermissionCollection</code> returned by the + * <code>newPermissionCollection</code> method in a subclass of + * <code>Permission</code> is a homogeneous collection. It only will + * hold permissions of one specified type - instances of the class that + * created it. Not all <code>PermissionCollection</code> subclasses + * have to hold permissions of only one type however. For example, + * the <code>Permissions</code> class holds permissions of many types. + * + * <p>Since the <code>newPermissionCollection</code> in <code>Permission</code> + * itself returns <code>null</code>, by default a permission can be stored + * in any type of collection unless it overrides that method to create its + * own collection type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Permission + * @see Permissions + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class PermissionCollection implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -6727011328946861783L; + + /** + * Indicates whether or not this collection is read only. + * + * @serial if the collection is read-only + */ + private boolean readOnly; + + /** + * Create a new collection. + */ + public PermissionCollection() + { + } + + /** + * This method adds a new <code>Permission</code> object to the collection. + * + * @param perm the <code>Permission</code> to add + * + * @throws SecurityException if the collection is marked read only + * @throws IllegalArgumentException if perm is of the wrong type + */ + public abstract void add(Permission perm); + + /** + * This method tests whether the specified <code>Permission</code> object is + * implied by this collection of <code>Permission</code> objects. + * + * @param perm the <code>Permission</code> object to test + * @return true if the collection implies perm + */ + public abstract boolean implies(Permission perm); + + /** + * This method returns an <code>Enumeration</code> of all the objects in + * this collection. + * + * @return an <code>Enumeration</code> of this collection's objects + */ + public abstract Enumeration elements(); + + /** + * This method sets this <code>PermissionCollection</code> object to be + * read only. No further permissions can be added to it after calling this + * method. + */ + public void setReadOnly() + { + readOnly = true; + } + + /** + * This method tests whether or not this <code>PermissionCollection</code> + * object is read only. + * + * @return true if this collection is read only + */ + public boolean isReadOnly() + { + return readOnly; + } + + /** + * This method returns a <code>String</code> representation of this + * collection. It is formed by: + * <pre> + * super.toString()" (\n" + * // enumerate all permissions, one per line + * ")\n" + * </pre> + * + * @return a <code>String</code> representing this object + */ + public String toString() + { + StringBuffer sb = new StringBuffer(super.toString()); + + sb.append(" (\n"); + Enumeration e = elements(); + while (e.hasMoreElements()) + sb.append(' ').append(e.nextElement()).append('\n'); + return sb.append(")\n").toString(); + } +} // class PermissionCollection diff --git a/libjava/classpath/java/security/Permissions.java b/libjava/classpath/java/security/Permissions.java new file mode 100644 index 00000000000..e3fd06970ab --- /dev/null +++ b/libjava/classpath/java/security/Permissions.java @@ -0,0 +1,254 @@ +/* Permissions.java -- a collection of permission collections + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; + +/** + * This class is a heterogeneous collection of permissions. It is + * organized as a collection of <code>PermissionCollection</code>'s stored + * in a hashtable. Each individual <code>PermissionCollection</code> + * contains permissions of a single type. If a specific type of + * <code>Permission</code> does not provide a collection type to use + * via its <code>newPermissionCollection</code> method, then a default + * collection type which stores its permissions in a hash table will be + * used. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + */ +public final class Permissions extends PermissionCollection + implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4858622370623524688L; + + /** + * Holds instances of <code>AllPermission</code>. + * + * @serial the permission collection for AllPermission + */ + private PermissionCollection allPermission; + + // Package-private to avoid a trampoline. + /** + * This is the <code>Hashtable</code> that contains our collections. + * + * @serial maps Class to PermissionCollection + */ + final Hashtable perms = new Hashtable(); + + /** + * This method initializes a new instance of <code>Permissions</code>. + */ + public Permissions() + { + } + + /** + * This method adds a new <code>Permission</code> to this collection. It + * will be stored in a <code>PermissionCollection</code> of the appropriate + * type, as determined by calling <code>newPermissionCollection</code> on + * the specified permission (if an appropriate collection does not already + * exist). If this object does not specify a particular type of collection, + * a default collection, which stores in permissions in a hash table, will + * be used. + * + * @param perm the <code>Permission</code> to add + * @throws SecurityException if this collection is marked as read only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException("PermissionCollection is read only"); + if (perm instanceof AllPermission) + { + if (allPermission == null) + { + allPermission = perm.newPermissionCollection(); + allPermission.add(perm); + perms.put(perm.getClass(), allPermission); + } + } + else + { + PermissionCollection pc + = (PermissionCollection) perms.get(perm.getClass()); + if (pc == null) + { + pc = perm.newPermissionCollection(); + if (pc == null) + pc = new PermissionsHash(); + perms.put(perm.getClass(), pc); + } + pc.add(perm); + } + } + + /** + * This method tests whether or not the specified <code>Permission</code> + * is implied by this <code>PermissionCollection</code>. + * + * @param perm the <code>Permission</code> to test + * @return true if the specified permission is implied by this + */ + public boolean implies(Permission perm) + { + if (allPermission != null) + return true; + PermissionCollection pc + = (PermissionCollection) perms.get(perm.getClass()); + return pc == null ? false : pc.implies(perm); + } + + /** + * This method returns an <code>Enumeration</code> which contains a + * list of all <code>Permission</code> objects contained in this + * collection. + * + * @return an <code>Enumeration</code> of this collection's elements + */ + public Enumeration elements() + { + return new Enumeration() + { + Enumeration main_enum = perms.elements(); + Enumeration sub_enum; + + public boolean hasMoreElements() + { + if (sub_enum == null) + { + if (main_enum == null) + return false; + if (! main_enum.hasMoreElements()) + { + main_enum = null; + return false; + } + PermissionCollection pc = + (PermissionCollection) main_enum.nextElement(); + sub_enum = pc.elements(); + } + if (! sub_enum.hasMoreElements()) + { + sub_enum = null; + return hasMoreElements(); + } + return true; + } + + public Object nextElement() + { + if (! hasMoreElements()) + throw new NoSuchElementException(); + return sub_enum.nextElement(); + } + }; + } + + /** + * Implements the permission collection for all permissions without one of + * their own, and obeys serialization of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class PermissionsHash extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8491988220802933440L; + + /** + * Hashtable where we store permissions. + * + * @serial the stored permissions, both as key and value + */ + private final Hashtable perms = new Hashtable(); + + /** + * Add a permission. We don't need to check for read-only, as this + * collection is never exposed outside of Permissions, which has already + * done that check. + * + * @param perm the permission to add + */ + public void add(Permission perm) + { + perms.put(perm, perm); + } + + /** + * Returns true if perm is in the collection. + * + * @param perm the permission to check + * @return true if it is implied + */ + // FIXME: Should this method be synchronized? + public boolean implies(Permission perm) + { + Enumeration elements = elements(); + + while (elements.hasMoreElements()) + { + Permission p = (Permission)elements.nextElement(); + if (p.implies(perm)) + return true; + } + return false; + } + + /** + * Return the elements. + * + * @return the elements + */ + public Enumeration elements() + { + return perms.elements(); + } + } // class PermissionsHash +} // class Permissions diff --git a/libjava/classpath/java/security/Policy.java b/libjava/classpath/java/security/Policy.java new file mode 100644 index 00000000000..03d9bbb4ed6 --- /dev/null +++ b/libjava/classpath/java/security/Policy.java @@ -0,0 +1,310 @@ +/* Policy.java --- Policy Manager Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * <p>This is an abstract class for representing the system security policy for + * a Java application environment (specifying which permissions are available + * for code from various sources). That is, the security policy is represented + * by a <code>Policy</code> subclass providing an implementation of the abstract + * methods in this <code>Policy</code> class.</p> + * + * <p>There is only one <code>Policy</code> object in effect at any given time. + * </p> + * + * <p>The source location for the policy information utilized by the + * <code>Policy</code> object is up to the <code>Policy</code> implementation. + * The policy configuration may be stored, for example, as a flat ASCII file, as + * a serialized binary file of the <code>Policy</code> class, or as a database. + * </p> + * + * <p>The currently-installed <code>Policy</code> object can be obtained by + * calling the <code>getPolicy()</code> method, and it can be changed by a call + * to the <code>setPolicy()</code> method (by code with permission to reset the + * <code>Policy</code>).</p> + * + * <p>The <code>refresh()</code> method causes the policy object to refresh / + * reload its current configuration.</p> + * + * <p>This is implementation-dependent. For example, if the policy object stores + * its policy in configuration files, calling <code>refresh()</code> will cause + * it to re-read the configuration policy files. The refreshed policy may not + * have an effect on classes in a particular {@link ProtectionDomain}. This is + * dependent on the <code>Policy</code> provider's implementation of the + * <code>implies()</code> method and the {@link PermissionCollection} caching + * strategy.</p> + * + * <p>The default <code>Policy</code> implementation can be changed by setting + * the value of the <code>"policy.provider"</code> security property (in the + * Java security properties file) to the fully qualified name of the desired + * <code>Policy</code> implementation class. The Java security properties file + * is located in the file named <code><JAVA_HOME>/lib/security/java.security + * </code>, where <code><JAVA_HOME></code> refers to the directory where the + * SDK was installed.</p> + * + * <p><b>IMPLEMENTATION NOTE:</b> This implementation attempts to read the + * System property named <code>policy.provider</code> to find the concrete + * implementation of the <code>Policy</code>. If/when this fails, it falls back + * to a default implementation, which <b>allows everything</b>. + * + * @author Mark Benvenuto + * @see CodeSource + * @see PermissionCollection + * @see SecureClassLoader + * @since 1.2 + */ +public abstract class Policy +{ + private static Policy currentPolicy; + + /** Map of ProtectionDomains to PermissionCollections for this instance. */ + private Map pd2pc = null; + + /** Constructs a new <code>Policy</code> object. */ + public Policy() + { + } + + /** + * Returns the installed <code>Policy</code> object. This value should not be + * cached, as it may be changed by a call to <code>setPolicy()</code>. This + * method first calls {@link SecurityManager#checkPermission(Permission)} with + * a <code>SecurityPermission("getPolicy")</code> permission to ensure it's ok + * to get the <code>Policy</code> object. + * + * @return the installed <code>Policy</code>. + * @throws SecurityException if a security manager exists and its + * <code>checkPermission()</code> method doesn't allow getting the + * <code>Policy</code> object. + * @see SecurityManager#checkPermission(Permission) + * @see #setPolicy(Policy) + */ + public static Policy getPolicy() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SecurityPermission("getPolicy")); + + return getCurrentPolicy(); + } + + /** + * Sets the system-wide <code>Policy</code> object. This method first calls + * {@link SecurityManager#checkPermission(Permission)} with a + * <code>SecurityPermission("setPolicy")</code> permission to ensure it's ok + * to set the <code>Policy</code>. + * + * @param policy the new system <code>Policy</code> object. + * @throws SecurityException if a security manager exists and its + * <code>checkPermission()</code> method doesn't allow setting the + * <code>Policy</code>. + * @see SecurityManager#checkPermission(Permission) + * @see #getPolicy() + */ + public static void setPolicy(Policy policy) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SecurityPermission("setPolicy")); + + setup(policy); + currentPolicy = policy; + } + + private static void setup(final Policy policy) + { + if (policy.pd2pc == null) + policy.pd2pc = Collections.synchronizedMap(new LinkedHashMap()); + + ProtectionDomain pd = policy.getClass().getProtectionDomain(); + if (pd.getCodeSource() != null) + { + PermissionCollection pc = null; + if (currentPolicy != null) + pc = currentPolicy.getPermissions(pd); + + if (pc == null) // assume it has all + { + pc = new Permissions(); + pc.add(new AllPermission()); + } + + policy.pd2pc.put(pd, pc); // add the mapping pd -> pc + } + } + + /** + * Ensures/forces loading of the configured policy provider, while bypassing + * the {@link SecurityManager} checks for <code>"getPolicy"</code> security + * permission. Needed by {@link ProtectionDomain}. + */ + static Policy getCurrentPolicy() + { + // FIXME: The class name of the Policy provider should really be sourced + // from the "java.security" configuration file. For now, just hard-code + // a stub implementation. + if (currentPolicy == null) + { + String pp = System.getProperty ("policy.provider"); + if (pp != null) + try + { + currentPolicy = (Policy) Class.forName(pp).newInstance(); + } + catch (Exception e) + { + // Ignored. + } + + if (currentPolicy == null) + currentPolicy = new gnu.java.security.provider.DefaultPolicy(); + } + return currentPolicy; + } + + /** + * Tests if <code>currentPolicy</code> is not <code>null</code>, + * thus allowing clients to not force loading of any policy + * provider; needed by {@link ProtectionDomain}. + */ + static boolean isLoaded() + { + return currentPolicy != null; + } + + /** + * Evaluates the global policy and returns a {@link PermissionCollection} + * object specifying the set of permissions allowed for code from the + * specified code source. + * + * @param codesource the {@link CodeSource} associated with the caller. This + * encapsulates the original location of the code (where the code came from) + * and the public key(s) of its signer. + * @return the set of permissions allowed for code from codesource according + * to the policy. The returned set of permissions must be a new mutable + * instance and it must support heterogeneous {@link Permission} types. + */ + public abstract PermissionCollection getPermissions(CodeSource codesource); + + /** + * Evaluates the global policy and returns a {@link PermissionCollection} + * object specifying the set of permissions allowed given the characteristics + * of the protection domain. + * + * @param domain the {@link ProtectionDomain} associated with the caller. + * @return the set of permissions allowed for the domain according to the + * policy. The returned set of permissions must be a new mutable instance and + * it must support heterogeneous {@link Permission} types. + * @since 1.4 + * @see ProtectionDomain + * @see SecureClassLoader + */ + public PermissionCollection getPermissions(ProtectionDomain domain) + { + if (domain == null) + return new Permissions(); + + if (pd2pc == null) + setup(this); + + PermissionCollection result = (PermissionCollection) pd2pc.get(domain); + if (result != null) + { + Permissions realResult = new Permissions(); + for (Enumeration e = result.elements(); e.hasMoreElements(); ) + realResult.add((Permission) e.nextElement()); + + return realResult; + } + + result = getPermissions(domain.getCodeSource()); + if (result == null) + result = new Permissions(); + + PermissionCollection pc = domain.getPermissions(); + if (pc != null) + for (Enumeration e = pc.elements(); e.hasMoreElements(); ) + result.add((Permission) e.nextElement()); + + return result; + } + + /** + * Evaluates the global policy for the permissions granted to the {@link + * ProtectionDomain} and tests whether the <code>permission</code> is granted. + * + * @param domain the {@link ProtectionDomain} to test. + * @param permission the {@link Permission} object to be tested for + * implication. + * @return <code>true</code> if <code>permission</code> is a proper subset of + * a permission granted to this {@link ProtectionDomain}. + * @since 1.4 + * @see ProtectionDomain + */ + public boolean implies(ProtectionDomain domain, Permission permission) + { + if (pd2pc == null) + setup(this); + + PermissionCollection pc = (PermissionCollection) pd2pc.get(domain); + if (pc != null) + return pc.implies(permission); + + boolean result = false; + pc = getPermissions(domain); + if (pc != null) + { + result = pc.implies(permission); + pd2pc.put(domain, pc); + } + + return result; + } + + /** + * Refreshes/reloads the policy configuration. The behavior of this method + * depends on the implementation. For example, calling refresh on a file-based + * policy will cause the file to be re-read. + */ + public abstract void refresh(); +} diff --git a/libjava/classpath/java/security/Principal.java b/libjava/classpath/java/security/Principal.java new file mode 100644 index 00000000000..6d9de6ccd8a --- /dev/null +++ b/libjava/classpath/java/security/Principal.java @@ -0,0 +1,85 @@ +/* Principal.java -- A security entity + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +/** + * This interface models an entity (such as a user or a certificate authority) + * for the purposes of applying the Java security model. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see X509Certificate + * @since 1.1 + * @status updated to 1.4 + */ +public interface Principal +{ + /** + * This method tests another <code>Principal</code> object for equality + * with this one. + * + * @param obj the Object to test for equality + * @return true if the specified <code>Principal</code> is equal + */ + boolean equals(Object obj); + + /** + * This method returns a <code>String</code> representation of this + * <code>Principal</code>. + * + * @return this <code>Principal</code> represented as a <code>String</code> + */ + String toString(); + + /** + * This method returns a hash code value for this <code>Principal</code>. + * Remember the contract of hashCode - two objects which compare as + * equals() must have the same hashCode(). + * + * @return a hash value + */ + int hashCode(); + + /** + * This method returns a <code>String</code> that names this + * <code>Principal</code>. + * + * @return the name of this <code>Principal</code> + */ + String getName(); +} // interface Principal diff --git a/libjava/classpath/java/security/PrivateKey.java b/libjava/classpath/java/security/PrivateKey.java new file mode 100644 index 00000000000..70607c13442 --- /dev/null +++ b/libjava/classpath/java/security/PrivateKey.java @@ -0,0 +1,62 @@ +/* PrivateKey.java -- tagging interface for all private keys + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +/** + * This interface specified no methods. In simply provides a common + * super-interface for all algorithm specific private key values. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @see PublicKey + * @see Certificate + * @see Signature#initVerify(PublicKey) + * @see DSAPrivateKey + * @see RSAPrivateKey + * @see RSAPrivateCrtKey + * @since 1.1 + * @status updated to 1.4 + */ +public interface PrivateKey extends Key +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 6034044314589513430L; +} // interface PrivateKey diff --git a/libjava/classpath/java/security/PrivilegedAction.java b/libjava/classpath/java/security/PrivilegedAction.java new file mode 100644 index 00000000000..c3a41346f9c --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedAction.java @@ -0,0 +1,64 @@ +/* PrivilegedAction.java -- Perform a privileged action + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specifes a single <code>run</code> method that + * executes a privileged operation. This method is called by + * <code>AccessController.doPrivileged()</code> after that method + * activiates the required privileges. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AccessController + * @see PrivilegedExceptionAction + * @since 1.1 + * @status updated to 1.4 + */ +public interface PrivilegedAction +{ + /** + * This method performs an operation that requires higher privileges to + * perform. It is called when a section of code invokes + * <code>AccessController.doPrivileged()</code>. + * + * @return obj An implementation dependent return value + * @see AccessController#doPrivileged(PrivilegedAction) + * @see AccessController#doPrivileged(PrivilegedAction, AccessControlContext) + */ + Object run(); +} // interface PrivilegedAction diff --git a/libjava/classpath/java/security/PrivilegedActionException.java b/libjava/classpath/java/security/PrivilegedActionException.java new file mode 100644 index 00000000000..3f08c813065 --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedActionException.java @@ -0,0 +1,109 @@ +/* PrivilegedActionException.java -- wrap an exception in a privileged action + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when an exception is thrown during a + * privileged action being performed with the + * <code>AccessController.doPrivileged()</code> method. It wraps the + * actual exception thrown in the privileged code. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see PrivilegedExceptionAction + * @see AccessController#doPrivileged(PrivilegedExceptionAction) + * @see AccessController#doPrivileged(PrivilegedExceptionAction, AccessControlContext) + * @status updated to 1.4 + */ +public class PrivilegedActionException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4724086851538908602L; + + /** + * This is the actual exception that occurred. + * + * @serial the wrapped exception + */ + private Exception exception; + + /** + * Create a new instance that wraps the specified <code>Exception</code>. + * + * @param e the <code>Exception</code> to wrap + */ + public PrivilegedActionException(Exception e) + { + super(e); + exception = e; + } + + /** + * Get the underlying <code>Exception</code> that caused this one. This + * is a legacy method, the preferred way is {@link #getCause()}. + * + * @return the cause + */ + public Exception getException() + { + return exception; + } + + /** + * Gets the cause of this exception. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return exception; + } + + /** + * Convert this to a String. + * + * @return the string representation + */ + public String toString() + { + return super.toString(); + } +} diff --git a/libjava/classpath/java/security/PrivilegedExceptionAction.java b/libjava/classpath/java/security/PrivilegedExceptionAction.java new file mode 100644 index 00000000000..d3d0478fd48 --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedExceptionAction.java @@ -0,0 +1,65 @@ +/* PrivilegedExceptionAction.java -- Perform a privileged operation + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface defines a method that is called by + * <code>AccessController.doPrivileged()</code> in order to perform a + * privileged operation with higher privileges enabled. This interface + * differs from <code>PrivilegedAction</code> in that the <code>run</code> + * method in this interface may throw a checked exception. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface PrivilegedExceptionAction +{ + /** + * This method performs an operation that requires higher privileges to + * successfully complete. It is called when a section of code invokes + * <code>AccessController.doPrivileged()</code>. + * + * @return obj An implementation defined return value + * @throws Exception An implementation specific exception + * @see AccessController#doPrivileged(PrivilegedExceptionAction) + * @see AccessController#doPrivileged(PrivilegedExceptionAction, + * AccessControlContext) + */ + Object run() throws Exception; +} // interface PrivilegedExceptionAction diff --git a/libjava/classpath/java/security/ProtectionDomain.java b/libjava/classpath/java/security/ProtectionDomain.java new file mode 100644 index 00000000000..a5851b5adf4 --- /dev/null +++ b/libjava/classpath/java/security/ProtectionDomain.java @@ -0,0 +1,269 @@ +/* ProtectionDomain.java -- A security domain + Copyright (C) 1998, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * <p>This <code>ProtectionDomain</code> class encapsulates the characteristics + * of a domain, which encloses a set of classes whose instances are granted a + * set of permissions when being executed on behalf of a given set of + * <i>Principals</i>. + * + * <p>A static set of permissions can be bound to a <code>ProtectionDomain</code> + * when it is constructed; such permissions are granted to the domain regardless + * of the {@link Policy} in force. However, to support dynamic security + * policies, a <code>ProtectionDomain</code> can also be constructed such that + * it is dynamically mapped to a set of permissions by the current {@link + * Policy} whenever a permission is checked.</p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @version 0.0 + */ +public class ProtectionDomain +{ + /** This is the <code>CodeSource</code> for this protection domain. */ + private CodeSource code_source; + + /** This is the set of permissions granted to this domain. */ + private PermissionCollection perms; + + /** The {@link ClassLoader} associated with this domain. */ + private ClassLoader classloader; + + /** The array of Principals associated with this domain.. */ + private Principal[] principals; + + /** Post 1.4 the policy may be refreshed! use false for pre 1.4. */ + private boolean staticBinding; + + /** + * Creates a new <code>ProtectionDomain</code> with the given {@link + * CodeSource} and {@link Permissions}. If the permissions object is not + * <code>null</code>, then <code>setReadOnly()</code> will be called on the + * passed in {@link Permissions} object. The only permissions granted to this + * domain are the ones specified; the current {@link Policy} will not be + * consulted. + * + * @param codesource the codesource associated with this domain. + * @param permissions the permissions granted to this domain + */ + public ProtectionDomain(CodeSource codesource, PermissionCollection permissions) + { + this(codesource, permissions, null, null, true); + } + + /** + * <p>Creates a new ProtectionDomain qualified by the given CodeSource, + * Permissions, ClassLoader and array of Principals. If the permissions + * object is not null, then <code>setReadOnly()</code> will be called on the + * passed in Permissions object. The permissions granted to this domain are + * dynamic; they include both the static permissions passed to this + * constructor, and any permissions granted to this domain by the current + * Policy at the time a permission is checked.</p> + * + * <p>This constructor is typically used by {@link ClassLoader}s and {@link + * DomainCombiner}s which delegate to <code>Policy</code> to actively + * associate the permissions granted to this domain. This constructor affords + * the Policy provider the opportunity to augment the supplied + * PermissionCollection to reflect policy changes.</p> + * + * @param codesource the CodeSource associated with this domain. + * @param permissions the permissions granted to this domain. + * @param classloader the ClassLoader associated with this domain. + * @param principals the array of Principals associated with this domain. + * @since 1.4 + * @see Policy#refresh() + * @see Policy#getPermissions(ProtectionDomain) + */ + public ProtectionDomain(CodeSource codesource, + PermissionCollection permissions, + ClassLoader classloader, Principal[] principals) + { + this(codesource, permissions, classloader, principals, false); + } + + private ProtectionDomain(CodeSource codesource, + PermissionCollection permissions, + ClassLoader classloader, Principal[] principals, + boolean staticBinding) + { + super(); + + code_source = codesource; + if (permissions != null) + { + perms = permissions; + perms.setReadOnly(); + } + + this.classloader = classloader; + this.principals = + (principals != null ? (Principal[]) principals.clone() : new Principal[0]); + this.staticBinding = staticBinding; + } + + /** + * Returns the {@link CodeSource} of this domain. + * + * @return the {@link CodeSource} of this domain which may be <code>null</code>. + * @since 1.2 + */ + public final CodeSource getCodeSource() + { + return code_source; + } + + /** + * Returns the {@link ClassLoader} of this domain. + * + * @return the {@link ClassLoader} of this domain which may be + * <code>null</code>. + * @since 1.4 + */ + public final ClassLoader getClassLoader() + { + return this.classloader; + } + + /** + * Returns an array of principals for this domain. + * + * @return returns a non-null array of principals for this domain. Changes to + * this array will have no impact on the <code>ProtectionDomain</code>. + * @since 1.4 + */ + public final Principal[] getPrincipals() + { + return (Principal[]) principals.clone(); + } + + /** + * Returns the static permissions granted to this domain. + * + * @return the static set of permissions for this domain which may be + * <code>null</code>. + * @see Policy#refresh() + * @see Policy#getPermissions(ProtectionDomain) + */ + public final PermissionCollection getPermissions() + { + return perms; + } + + /** + * <p>Check and see if this <code>ProtectionDomain</code> implies the + * permissions expressed in the <code>Permission</code> object.</p> + * + * <p>The set of permissions evaluated is a function of whether the + * <code>ProtectionDomain</code> was constructed with a static set of + * permissions or it was bound to a dynamically mapped set of permissions.</p> + * + * <p>If the <code>ProtectionDomain</code> was constructed to a statically + * bound {@link PermissionCollection} then the permission will only be checked + * against the {@link PermissionCollection} supplied at construction.</p> + * + * <p>However, if the <code>ProtectionDomain</code> was constructed with the + * constructor variant which supports dynamically binding permissions, then + * the permission will be checked against the combination of the + * {@link PermissionCollection} supplied at construction and the current + * {@link Policy} binding. + * + * @param permission the {@link Permission} object to check. + * @return <code>true</code> if <code>permission</code> is implicit to this + * <code>ProtectionDomain</code>. + */ + public boolean implies(Permission permission) + { + if (staticBinding) + return (perms == null ? false : perms.implies(permission)); + // Else dynamically bound. Do we have it? + // NOTE: this will force loading of Policy.currentPolicy + return Policy.getCurrentPolicy().implies(this, permission); + } + + /** + * Convert a <code>ProtectionDomain</code> to a String. + * + * @return a string representation of the object. + */ + public String toString() + { + String linesep = System.getProperty("line.separator"); + StringBuffer sb = new StringBuffer("ProtectionDomain (").append(linesep); + + if (code_source == null) + sb.append("CodeSource:null"); + else + sb.append(code_source); + + sb.append(linesep); + if (classloader == null) + sb.append("ClassLoader:null"); + else + sb.append(classloader); + + sb.append(linesep); + sb.append("Principals:"); + if (principals != null && principals.length > 0) + { + sb.append("["); + Principal pal; + for (int i = 0; i < principals.length; i++) + { + pal = principals[i]; + sb.append("'").append(pal.getName()) + .append("' of type ").append(pal.getClass().getName()); + if (i < principals.length-1) + sb.append(", "); + } + sb.append("]"); + } + else + sb.append("none"); + + sb.append(linesep); + if (!staticBinding) // include all but dont force loading Policy.currentPolicy + if (Policy.isLoaded()) + sb.append(Policy.getCurrentPolicy().getPermissions(this)); + else // fallback on this one's permissions + sb.append(perms); + else + sb.append(perms); + + return sb.append(linesep).append(")").append(linesep).toString(); + } +} diff --git a/libjava/classpath/java/security/Provider.java b/libjava/classpath/java/security/Provider.java new file mode 100644 index 00000000000..4ffaa55bcb1 --- /dev/null +++ b/libjava/classpath/java/security/Provider.java @@ -0,0 +1,202 @@ +/* Provider.java -- Security provider information + Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; +import java.util.Properties; + +/** + * This class represents a Java security architecture service provider. + * The services provided by a such a provider can range from security + * algorithms to key generation. + * <p> + * Providers are installed by name and version number. There is one + * standard provider supplied with the class library. This is the + * "GNU" provider, which can also be accessed by the alias "SUN" for + * compatibility with the JDK. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Provider extends Properties implements Serializable +{ + private static final long serialVersionUID = -4298000515446427739L; + + /** + * This is a textual description of the provider + */ + private String info; + + /** + * This is the name of the provider + */ + private String name; + + /** + * This is the version number of the provider + */ + private double version; + + /** + * This method initializes a new instance of <code>Provider</code> to have + * the specified name, version, and description information. + * + * @param name The name to assign to this <code>Provider</code>. + * @param version The version number for this <code>Provider</code>. + * @param info A textual description of this provider. + */ + protected Provider(String name, double version, String info) + { + this.name = name; + this.version = version; + this.info = info; + } + + /** + * This method returns the name assigned to this <code>Provider</code>. + * + * @return The <code>Provider</code>'s name. + */ + public String getName() + { + return (name); + } + + /** + * This method retunrs the version number of this <code>Provider</code>. + * + * @return The <code>Provider</code>'s version number. + */ + public double getVersion() + { + return (version); + } + + /** + * This method returns a textual description of the <code>Provider</code>. + * + * @return A description of the <code>Provider</code>. + */ + public String getInfo() + { + return (info); + } + + /** + * Sets the key property to have the specified value. + * <p> + * <bold>NOT IMPLEMENTED YET</bold>[ + * First, if there is a security manager, its <code>checkSecurityAccess</code> + * method is called with the string "putProviderProperty."+name, where name is + * the provider name, to see if it's ok to set this provider's property + * values. + * If the default implementation of <code>checkSecurityAccess</code> is used + * (that is, that method is not overriden), then this results in a call to the + * security manager's <code>checkPermission</code> method with a + * <code>SecurityPermission("putProviderProperty."+name)</code> + * permission.<br>] + * + * @param key The property key. + * @param value The property value. + * + * @return The previous value of the specified property (<code>key</code>), + * or <code>null</code> if it did not have one. + * @throws SecurityException If a security manager exists and its + * {@link java.lang.SecurityManager.checkSecurityAccess(java.lang.String)} + * method denies access to set property values. + * @since Classpath 0.4+cvs, JDK 1.2 + * @see java.lang.Object.equals(Object) + * @see java.util.Hashtable.get(Object) + */ + public Object put(Object key, Object value) + { + return super.put(toCanonicalKey(key), value); + } + + // overrides same in java.util.Hashtable + public Object get(Object key) + { + return super.get(toCanonicalKey(key)); + } + + /** + * This method removes the specified key entry (and its associated value) + * from the property mapping list. + * + * @param key The key to remove + * + * @return The previous value for this key, or <code>null</code> if no + * previous value. + */ + public Object remove(Object key) + { + return super.remove(toCanonicalKey(key)); + } + + /** + * This method clears the entire property list such that it no longer + * contains the properties used to look up the services provided by + * the <code>Provider</code>. + */ + public void clear() + { + super.clear(); + } + + /** + * This method returns a <code>String</code> representation of this + * object. This will include the <code>Provider</code> name and + * version number. + * + * @return A <code>String</code> representation of this object. + */ + public String toString() + { + return (getClass().getName() + ": name=" + getName() + " version=" + + version); + } + + private Object toCanonicalKey(Object key) + { + if (key.getClass().isAssignableFrom(String.class)) // is it ours? + return ((String) key).toUpperCase(); // use default locale + else + return key; + } +} diff --git a/libjava/classpath/java/security/ProviderException.java b/libjava/classpath/java/security/ProviderException.java new file mode 100644 index 00000000000..2dafcec3495 --- /dev/null +++ b/libjava/classpath/java/security/ProviderException.java @@ -0,0 +1,70 @@ +/* ProviderException.java -- Generic security provider runtime exception + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception indicates that a runtime problem was encounterd with + * a security provider. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class ProviderException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5256023526693665674L; + + /** + * Create an instance with no descriptive error message. + */ + public ProviderException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public ProviderException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/PublicKey.java b/libjava/classpath/java/security/PublicKey.java new file mode 100644 index 00000000000..9bf14584089 --- /dev/null +++ b/libjava/classpath/java/security/PublicKey.java @@ -0,0 +1,60 @@ +/* PublicKey.java -- tagging interface for all public keys + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specified no methods. In simply provides a common + * super-interface for all algorithm specific public key values. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @see PrivateKey + * @see Certificate + * @see Signature#initVerify(PublicKey) + * @see DSAPublicKey + * @see RSAPublicKey + * @since 1.1 + * @status updated to 1.4 + */ +public interface PublicKey extends Key +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 7187392471159151072L; +} // interface PublicKey diff --git a/libjava/classpath/java/security/SecureClassLoader.java b/libjava/classpath/java/security/SecureClassLoader.java new file mode 100644 index 00000000000..9d1fac79749 --- /dev/null +++ b/libjava/classpath/java/security/SecureClassLoader.java @@ -0,0 +1,128 @@ +/* SecureClassLoader.java --- A Secure Class Loader + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * A Secure Class Loader for loading classes with additional + * support for specifying code source and permissions when + * they are retrieved by the system policy handler. + * + * @since 1.2 + * + * @author Mark Benvenuto + */ +public class SecureClassLoader extends ClassLoader +{ + java.util.WeakHashMap protectionDomainCache = new java.util.WeakHashMap(); + + protected SecureClassLoader(ClassLoader parent) + { + super(parent); + SecurityManager sm = System.getSecurityManager(); + if(sm != null) + sm.checkCreateClassLoader(); + } + + protected SecureClassLoader() + { + SecurityManager sm = System.getSecurityManager(); + if(sm != null) + sm.checkCreateClassLoader(); + } + + /** + * Creates a class using an array of bytes and a + * CodeSource. + * + * @param name the name to give the class. null if unknown. + * @param b the data representing the classfile, in classfile format. + * @param off the offset into the data where the classfile starts. + * @param len the length of the classfile data in the array. + * @param cs the CodeSource for the class or null when unknown. + * + * @return the class that was defined and optional CodeSource. + * + * @exception ClassFormatError if the byte array is not in proper classfile format. + */ + protected final Class defineClass(String name, byte[] b, int off, int len, + CodeSource cs) + { + if (cs != null) + { + ProtectionDomain protectionDomain; + + synchronized (protectionDomainCache) + { + protectionDomain = (ProtectionDomain)protectionDomainCache.get(cs); + } + + if (protectionDomain == null) + { + protectionDomain + = new ProtectionDomain(cs, getPermissions(cs), this, null); + synchronized (protectionDomainCache) + { + ProtectionDomain domain + = (ProtectionDomain)protectionDomainCache.get(cs); + if (domain == null) + protectionDomainCache.put(cs, protectionDomain); + else + protectionDomain = domain; + } + } + return super.defineClass(name, b, off, len, protectionDomain); + } + else + return super.defineClass(name, b, off, len); + } + + /** + * Returns a PermissionCollection for the specified CodeSource. + * The default implementation invokes + * java.security.Policy.getPermissions. + * + * This method is called by defineClass that takes a CodeSource + * arguement to build a proper ProtectionDomain for the class + * being defined. + */ + protected PermissionCollection getPermissions(CodeSource cs) + { + Policy policy = Policy.getCurrentPolicy(); + return policy.getPermissions(cs); + } +} diff --git a/libjava/classpath/java/security/SecureRandom.java b/libjava/classpath/java/security/SecureRandom.java new file mode 100644 index 00000000000..3ee3a841d26 --- /dev/null +++ b/libjava/classpath/java/security/SecureRandom.java @@ -0,0 +1,380 @@ +/* SecureRandom.java --- Secure Random class implementation + Copyright (C) 1999, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.java.security.Engine; + +import java.util.Enumeration; +import java.util.Random; + +/** + * An interface to a cryptographically secure pseudo-random number + * generator (PRNG). Random (or at least unguessable) numbers are used + * in all areas of security and cryptography, from the generation of + * keys and initialization vectors to the generation of random padding + * bytes. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @author Casey Marshall + */ +public class SecureRandom extends Random +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for PRNGs. */ + private static final String SECURE_RANDOM = "SecureRandom"; + + private static final long serialVersionUID = 4940670005562187L; + + //Serialized Field + long counter = 0; //Serialized + Provider provider = null; + byte[] randomBytes = null; //Always null + int randomBytesUsed = 0; + SecureRandomSpi secureRandomSpi = null; + byte[] state = null; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + Default constructor for SecureRandom. It constructs a + new SecureRandom by instantating the first SecureRandom + algorithm in the default security provier. + + It is not seeded and should be seeded using setSeed or else + on the first call to getnextBytes it will force a seed. + + It is maintained for backwards compatibility and programs + should use {@link #getInstance(java.lang.String)}. + */ + public SecureRandom() + { + Provider[] p = Security.getProviders(); + + //Format of Key: SecureRandom.algname + String key; + + String classname = null; + int i; + Enumeration e; + for (i = 0; i < p.length; i++) + { + e = p[i].propertyNames(); + while (e.hasMoreElements()) + { + key = (String) e.nextElement(); + if (key.startsWith("SECURERANDOM.")) + { + if ((classname = p[i].getProperty(key)) != null) + { + try + { + secureRandomSpi = (SecureRandomSpi) Class. + forName(classname).newInstance(); + provider = p[i]; + return; + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Ignore. + } + } + } + } + } + + // Nothing found. Fall back to SHA1PRNG + secureRandomSpi = new gnu.java.security.provider.SHA1PRNG(); + } + + /** + A constructor for SecureRandom. It constructs a new + SecureRandom by instantating the first SecureRandom algorithm + in the default security provier. + + It is seeded with the passed function and is useful if the user + has access to hardware random device (like a radiation detector). + + It is maintained for backwards compatibility and programs + should use getInstance. + + @param seed Seed bytes for class + */ + public SecureRandom(byte[] seed) + { + this(); + setSeed(seed); + } + + /** + A constructor for SecureRandom. It constructs a new + SecureRandom using the specified SecureRandomSpi from + the specified security provier. + + @param secureRandomSpi A SecureRandomSpi class + @param provider A Provider class + */ + protected SecureRandom(SecureRandomSpi secureRandomSpi, Provider provider) + { + this.secureRandomSpi = secureRandomSpi; + this.provider = provider; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Returns an instance of a SecureRandom. It creates the class from + * the first provider that implements it. + * + * @param algorithm The algorithm name. + * @return A new SecureRandom implementing the given algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements + * the given algorithm. + */ + public static SecureRandom getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignore. + } + } + + // None found. + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns an instance of a SecureRandom. It creates the class + * for the specified algorithm from the named provider. + * + * @param algorithm The algorithm name. + * @param provider The provider name. + * @return A new SecureRandom implementing the chosen algorithm. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm, or if the implementation cannot be + * instantiated. + * @throws NoSuchProviderException If no provider named + * <code>provider</code> is currently installed. + * @throws IllegalArgumentException If <code>provider</code> is null + * or is empty. + */ + public static SecureRandom getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Returns an instance of a SecureRandom. It creates the class for + * the specified algorithm from the given provider. + * + * @param algorithm The SecureRandom algorithm to create. + * @param provider The provider to get the instance from. + * @throws NoSuchAlgorithmException If the algorithm cannot be found, or + * if the class cannot be instantiated. + * @throws IllegalArgumentException If <code>provider</code> is null. + */ + public static SecureRandom getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + try + { + return new SecureRandom((SecureRandomSpi) + Engine.getInstance(SECURE_RANDOM, algorithm, provider), + provider); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + Returns the provider being used by the current SecureRandom class. + + @return The provider from which this SecureRandom was attained + */ + public final Provider getProvider() + { + return provider; + } + + /** + Seeds the SecureRandom. The class is re-seeded for each call and + each seed builds on the previous seed so as not to weaken security. + + @param seed seed bytes to seed with + */ + public void setSeed(byte[] seed) + { + secureRandomSpi.engineSetSeed(seed); + } + + /** + Seeds the SecureRandom. The class is re-seeded for each call and + each seed builds on the previous seed so as not to weaken security. + + @param seed 8 seed bytes to seed with + */ + public void setSeed(long seed) + { + // This particular setSeed will be called by Random.Random(), via + // our own constructor, before secureRandomSpi is initialized. In + // this case we can't call a method on secureRandomSpi, and we + // definitely don't want to throw a NullPointerException. + // Therefore we test. + if (secureRandomSpi != null) + { + byte[] tmp = { (byte) (0xff & (seed >> 56)), + (byte) (0xff & (seed >> 48)), + (byte) (0xff & (seed >> 40)), + (byte) (0xff & (seed >> 32)), + (byte) (0xff & (seed >> 24)), + (byte) (0xff & (seed >> 16)), + (byte) (0xff & (seed >> 8)), + (byte) (0xff & seed) + }; + secureRandomSpi.engineSetSeed(tmp); + } + } + + /** + Generates a user specified number of bytes. This function + is the basis for all the random functions. + + @param bytes array to store generated bytes in + */ + public void nextBytes(byte[] bytes) + { + randomBytesUsed += bytes.length; + counter++; + secureRandomSpi.engineNextBytes(bytes); + } + + /** + Generates an integer containing the user specified + number of random bits. It is right justified and padded + with zeros. + + @param numBits number of random bits to get, 0 <= numBits <= 32; + + @return the random bits + */ + protected final int next(int numBits) + { + if (numBits == 0) + return 0; + + byte[] tmp = new byte[numBits / 8 + (1 * (numBits % 8))]; + + secureRandomSpi.engineNextBytes(tmp); + randomBytesUsed += tmp.length; + counter++; + + int ret = 0; + + for (int i = 0; i < tmp.length; i++) + ret |= (tmp[i] & 0xFF) << (8 * i); + + long mask = (1L << numBits) - 1; + return (int) (ret & mask); + } + + /** + Returns the given number of seed bytes. This method is + maintained only for backwards capability. + + @param numBytes number of seed bytes to get + + @return an array containing the seed bytes + */ + public static byte[] getSeed(int numBytes) + { + byte[] tmp = new byte[numBytes]; + + new Random().nextBytes(tmp); + return tmp; + //return secureRandomSpi.engineGenerateSeed( numBytes ); + } + + /** + Returns the specified number of seed bytes. + + @param numBytes number of seed bytes to get + + @return an array containing the seed bytes + */ + public byte[] generateSeed(int numBytes) + { + return secureRandomSpi.engineGenerateSeed(numBytes); + } + +} diff --git a/libjava/classpath/java/security/SecureRandomSpi.java b/libjava/classpath/java/security/SecureRandomSpi.java new file mode 100644 index 00000000000..7759097a456 --- /dev/null +++ b/libjava/classpath/java/security/SecureRandomSpi.java @@ -0,0 +1,85 @@ +/* SecureRandomSpi.java --- Secure Random Service Provider Interface + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.io.Serializable; + +/** + SecureRandomSpi is the Service Provider Interface for SecureRandom + providers. It provides an interface for providers to the + SecureRandom engine to write their own pseudo-random number + generator. + + @since JDK 1.2 + + @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class SecureRandomSpi implements Serializable +{ + private static final long serialVersionUID = -2991854161009191830L; + + /** + Default Constructor for SecureRandomSpi + */ + public SecureRandomSpi() + { + } + + /** + Updates the seed for SecureRandomSpi but does not reset seed. + It does to this so repeated called never decrease randomness. + */ + protected abstract void engineSetSeed(byte[] seed); + + /** + Gets a user specified number of bytes depending on the length + of the array? + + @param bytes array to fill with random bytes + */ + protected abstract void engineNextBytes(byte[] bytes); + + /** + Gets a user specified number of bytes specified by the + parameter. + + @param numBytes number of random bytes to generate + + @return an array full of random bytes + */ + protected abstract byte[] engineGenerateSeed(int numBytes); +} diff --git a/libjava/classpath/java/security/Security.java b/libjava/classpath/java/security/Security.java new file mode 100644 index 00000000000..54b97923efd --- /dev/null +++ b/libjava/classpath/java/security/Security.java @@ -0,0 +1,740 @@ +/* Security.java --- Java base security class implementation + Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.classpath.SystemProperties; + +import gnu.classpath.Configuration; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; + +/** + * This class centralizes all security properties and common security methods. + * One of its primary uses is to manage providers. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public final class Security +{ + private static final String ALG_ALIAS = "Alg.Alias."; + + private static Vector providers = new Vector(); + private static Properties secprops = new Properties(); + + static + { + String base = SystemProperties.getProperty("gnu.classpath.home.url"); + String vendor = SystemProperties.getProperty("gnu.classpath.vm.shortname"); + + // Try VM specific security file + boolean loaded = loadProviders (base, vendor); + + // Append classpath standard provider if possible + if (!loadProviders (base, "classpath") + && !loaded + && providers.size() == 0) + { + if (Configuration.DEBUG) + { + /* No providers found and both security files failed to + * load properly. Give a warning in case of DEBUG is + * enabled. Could be done with java.util.logging later. + */ + System.err.println + ("WARNING: could not properly read security provider files:"); + System.err.println + (" " + base + "/security/" + vendor + + ".security"); + System.err.println + (" " + base + "/security/" + "classpath" + + ".security"); + System.err.println + (" Falling back to standard GNU security provider"); + } + providers.addElement (new gnu.java.security.provider.Gnu()); + } + } + // This class can't be instantiated. + private Security() + { + } + + /** + * Tries to load the vender specific security providers from the given + * base URL. Returns true if the resource could be read and completely + * parsed successfully, false otherwise. + */ + private static boolean loadProviders(String baseUrl, String vendor) + { + if (baseUrl == null || vendor == null) + return false; + + boolean result = true; + String secfilestr = baseUrl + "/security/" + vendor + ".security"; + try + { + InputStream fin = new URL(secfilestr).openStream(); + secprops.load(fin); + + int i = 1; + String name; + while ((name = secprops.getProperty("security.provider." + i)) != null) + { + Exception exception = null; + try + { + providers.addElement(Class.forName(name).newInstance()); + } + catch (ClassNotFoundException x) + { + exception = x; + } + catch (InstantiationException x) + { + exception = x; + } + catch (IllegalAccessException x) + { + exception = x; + } + + if (exception != null) + { + System.err.println ("WARNING: Error loading security provider " + + name + ": " + exception); + result = false; + } + i++; + } + } + catch (IOException ignored) + { + result = false; + } + + return result; + } + + /** + * Gets a specified property for an algorithm. The algorithm name should be a + * standard name. See Appendix A in the Java Cryptography Architecture API + * Specification & Reference for information about standard algorithm + * names. One possible use is by specialized algorithm parsers, which may map + * classes to algorithms which they understand (much like {@link Key} parsers + * do). + * + * @param algName the algorithm name. + * @param propName the name of the property to get. + * @return the value of the specified property. + * @deprecated This method used to return the value of a proprietary property + * in the master file of the "SUN" Cryptographic Service Provider in order to + * determine how to parse algorithm-specific parameters. Use the new + * provider-based and algorithm-independent {@link AlgorithmParameters} and + * {@link KeyFactory} engine classes (introduced in the Java 2 platform) + * instead. + */ + public static String getAlgorithmProperty(String algName, String propName) + { + if (algName == null || propName == null) + return null; + + String property = String.valueOf(propName) + "." + String.valueOf(algName); + Provider p; + for (Iterator i = providers.iterator(); i.hasNext(); ) + { + p = (Provider) i.next(); + for (Iterator j = p.keySet().iterator(); j.hasNext(); ) + { + String key = (String) j.next(); + if (key.equalsIgnoreCase(property)) + return p.getProperty(key); + } + } + return null; + } + + /** + * <p>Adds a new provider, at a specified position. The position is the + * preference order in which providers are searched for requested algorithms. + * Note that it is not guaranteed that this preference will be respected. The + * position is 1-based, that is, <code>1</code> is most preferred, followed by + * <code>2</code>, and so on.</p> + * + * <p>If the given provider is installed at the requested position, the + * provider that used to be at that position, and all providers with a + * position greater than position, are shifted up one position (towards the + * end of the list of installed providers).</p> + * + * <p>A provider cannot be added if it is already installed.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with the string <code>"insertProvider."+provider. + * getName()</code> to see if it's ok to add a new provider. If the default + * implementation of <code>checkSecurityAccess()</code> is used (i.e., that + * method is not overriden), then this will result in a call to the security + * manager's <code>checkPermission()</code> method with a + * <code>SecurityPermission("insertProvider."+provider.getName())</code> + * permission.</p> + * + * @param provider the provider to be added. + * @param position the preference position that the caller would like for + * this provider. + * @return the actual preference position in which the provider was added, or + * <code>-1</code> if the provider was not added because it is already + * installed. + * @throws SecurityException if a security manager exists and its + * {@link SecurityManager#checkSecurityAccess(String)} method denies access + * to add a new provider. + * @see #getProvider(String) + * @see #removeProvider(String) + * @see SecurityPermission + */ + public static int insertProviderAt(Provider provider, int position) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("insertProvider." + provider.getName()); + + position--; + int max = providers.size (); + for (int i = 0; i < max; i++) + { + if (((Provider) providers.elementAt(i)).getName().equals(provider.getName())) + return -1; + } + + if (position < 0) + position = 0; + if (position > max) + position = max; + + providers.insertElementAt(provider, position); + + return position + 1; + } + + /** + * <p>Adds a provider to the next position available.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with the string <code>"insertProvider."+provider. + * getName()</code> to see if it's ok to add a new provider. If the default + * implementation of <code>checkSecurityAccess()</code> is used (i.e., that + * method is not overriden), then this will result in a call to the security + * manager's <code>checkPermission()</code> method with a + * <code>SecurityPermission("insertProvider."+provider.getName())</code> + * permission.</p> + * + * @param provider the provider to be added. + * @return the preference position in which the provider was added, or + * <code>-1</code> if the provider was not added because it is already + * installed. + * @throws SecurityException if a security manager exists and its + * {@link SecurityManager#checkSecurityAccess(String)} method denies access + * to add a new provider. + * @see #getProvider(String) + * @see #removeProvider(String) + * @see SecurityPermission + */ + public static int addProvider(Provider provider) + { + return insertProviderAt (provider, providers.size () + 1); + } + + /** + * <p>Removes the provider with the specified name.</p> + * + * <p>When the specified provider is removed, all providers located at a + * position greater than where the specified provider was are shifted down + * one position (towards the head of the list of installed providers).</p> + * + * <p>This method returns silently if the provider is not installed.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with the string <code>"removeProvider."+name</code> + * to see if it's ok to remove the provider. If the default implementation of + * <code>checkSecurityAccess()</code> is used (i.e., that method is not + * overriden), then this will result in a call to the security manager's + * <code>checkPermission()</code> method with a <code>SecurityPermission( + * "removeProvider."+name)</code> permission.</p> + * + * @param name the name of the provider to remove. + * @throws SecurityException if a security manager exists and its + * {@link SecurityManager#checkSecurityAccess(String)} method denies access + * to remove the provider. + * @see #getProvider(String) + * @see #addProvider(Provider) + */ + public static void removeProvider(String name) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeProvider." + name); + + int max = providers.size (); + for (int i = 0; i < max; i++) + { + if (((Provider) providers.elementAt(i)).getName().equals(name)) + { + providers.remove(i); + break; + } + } + } + + /** + * Returns an array containing all the installed providers. The order of the + * providers in the array is their preference order. + * + * @return an array of all the installed providers. + */ + public static Provider[] getProviders() + { + Provider[] array = new Provider[providers.size ()]; + providers.copyInto (array); + return array; + } + + /** + * Returns the provider installed with the specified name, if any. Returns + * <code>null</code> if no provider with the specified name is installed. + * + * @param name the name of the provider to get. + * @return the provider of the specified name. + * @see #removeProvider(String) + * @see #addProvider(Provider) + */ + public static Provider getProvider(String name) + { + Provider p; + int max = providers.size (); + for (int i = 0; i < max; i++) + { + p = (Provider) providers.elementAt(i); + if (p.getName().equals(name)) + return p; + } + return null; + } + + /** + * <p>Gets a security property value.</p> + * + * <p>First, if there is a security manager, its <code>checkPermission()</code> + * method is called with a <code>SecurityPermission("getProperty."+key)</code> + * permission to see if it's ok to retrieve the specified security property + * value.</p> + * + * @param key the key of the property being retrieved. + * @return the value of the security property corresponding to key. + * @throws SecurityException if a security manager exists and its + * {@link SecurityManager#checkPermission(Permission)} method denies access + * to retrieve the specified security property value. + * @see #setProperty(String, String) + * @see SecurityPermission + */ + public static String getProperty(String key) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("getProperty." + key); + + return secprops.getProperty(key); + } + + /** + * <p>Sets a security property value.</p> + * + * <p>First, if there is a security manager, its <code>checkPermission()</code> + * method is called with a <code>SecurityPermission("setProperty."+key)</code> + * permission to see if it's ok to set the specified security property value. + * </p> + * + * @param key the name of the property to be set. + * @param datnum the value of the property to be set. + * @throws SecurityException if a security manager exists and its + * {@link SecurityManager#checkPermission(Permission)} method denies access + * to set the specified security property value. + * @see #getProperty(String) + * @see SecurityPermission + */ + public static void setProperty(String key, String datnum) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setProperty." + key); + + secprops.put(key, datnum); + } + + /** + * Returns a Set of Strings containing the names of all available algorithms + * or types for the specified Java cryptographic service (e.g., Signature, + * MessageDigest, Cipher, Mac, KeyStore). Returns an empty Set if there is no + * provider that supports the specified service. For a complete list of Java + * cryptographic services, please see the Java Cryptography Architecture API + * Specification & Reference. Note: the returned set is immutable. + * + * @param serviceName the name of the Java cryptographic service (e.g., + * Signature, MessageDigest, Cipher, Mac, KeyStore). Note: this parameter is + * case-insensitive. + * @return a Set of Strings containing the names of all available algorithms + * or types for the specified Java cryptographic service or an empty set if + * no provider supports the specified service. + * @since 1.4 + */ + public static Set getAlgorithms(String serviceName) + { + HashSet result = new HashSet(); + if (serviceName == null || serviceName.length() == 0) + return result; + + serviceName = serviceName.trim(); + if (serviceName.length() == 0) + return result; + + serviceName = serviceName.toUpperCase()+"."; + Provider[] providers = getProviders(); + int ndx; + for (int i = 0; i < providers.length; i++) + for (Enumeration e = providers[i].propertyNames(); e.hasMoreElements(); ) + { + String service = ((String) e.nextElement()).trim(); + if (service.toUpperCase().startsWith(serviceName)) + { + service = service.substring(serviceName.length()).trim(); + ndx = service.indexOf(' '); // get rid of attributes + if (ndx != -1) + service = service.substring(0, ndx); + result.add(service); + } + } + return Collections.unmodifiableSet(result); + } + + /** + * <p>Returns an array containing all installed providers that satisfy the + * specified selection criterion, or <code>null</code> if no such providers + * have been installed. The returned providers are ordered according to their + * preference order.</p> + * + * <p>A cryptographic service is always associated with a particular + * algorithm or type. For example, a digital signature service is always + * associated with a particular algorithm (e.g., <i>DSA</i>), and a + * CertificateFactory service is always associated with a particular + * certificate type (e.g., <i>X.509</i>).</p> + * + * <p>The selection criterion must be specified in one of the following two + * formats:</p> + * + * <ul> + * <li><p><crypto_service>.<algorithm_or_type></p> + * <p>The cryptographic service name must not contain any dots.</p> + * <p>A provider satisfies the specified selection criterion iff the + * provider implements the specified algorithm or type for the specified + * cryptographic service.</p> + * <p>For example, "CertificateFactory.X.509" would be satisfied by any + * provider that supplied a CertificateFactory implementation for X.509 + * certificates.</p></li> + * + * <li><p><crypto_service>.<algorithm_or_type> <attribute_name>:<attribute_value></p> + * <p>The cryptographic service name must not contain any dots. There must + * be one or more space charaters between the the <algorithm_or_type> + * and the <attribute_name>.</p> + * <p>A provider satisfies this selection criterion iff the provider + * implements the specified algorithm or type for the specified + * cryptographic service and its implementation meets the constraint + * expressed by the specified attribute name/value pair.</p> + * <p>For example, "Signature.SHA1withDSA KeySize:1024" would be satisfied + * by any provider that implemented the SHA1withDSA signature algorithm + * with a keysize of 1024 (or larger).</p></li> + * </ul> + * + * <p>See Appendix A in the Java Cryptogaphy Architecture API Specification + * & Reference for information about standard cryptographic service names, + * standard algorithm names and standard attribute names.</p> + * + * @param filter the criterion for selecting providers. The filter is case- + * insensitive. + * @return all the installed providers that satisfy the selection criterion, + * or null if no such providers have been installed. + * @throws InvalidParameterException if the filter is not in the required + * format. + * @see #getProviders(Map) + */ + public static Provider[] getProviders(String filter) + { + if (providers == null || providers.isEmpty()) + return null; + + if (filter == null || filter.length() == 0) + return getProviders(); + + HashMap map = new HashMap(1); + int i = filter.indexOf(':'); + if (i == -1) // <service>.<algorithm> + map.put(filter, ""); + else // <service>.<algorithm> <attribute>:<value> + map.put(filter.substring(0, i), filter.substring(i+1)); + + return getProviders(map); + } + + /** + * <p>Returns an array containing all installed providers that satisfy the + * specified selection criteria, or <code>null</code> if no such providers + * have been installed. The returned providers are ordered according to their + * preference order.</p> + * + * <p>The selection criteria are represented by a map. Each map entry + * represents a selection criterion. A provider is selected iff it satisfies + * all selection criteria. The key for any entry in such a map must be in one + * of the following two formats:</p> + * + * <ul> + * <li><p><crypto_service>.<algorithm_or_type></p> + * <p>The cryptographic service name must not contain any dots.</p> + * <p>The value associated with the key must be an empty string.</p> + * <p>A provider satisfies this selection criterion iff the provider + * implements the specified algorithm or type for the specified + * cryptographic service.</p></li> + * + * <li><p><crypto_service>.<algorithm_or_type> <attribute_name></p> + * <p>The cryptographic service name must not contain any dots. There must + * be one or more space charaters between the <algorithm_or_type> and + * the <attribute_name>.</p> + * <p>The value associated with the key must be a non-empty string. A + * provider satisfies this selection criterion iff the provider implements + * the specified algorithm or type for the specified cryptographic service + * and its implementation meets the constraint expressed by the specified + * attribute name/value pair.</p></li> + * </ul> + * + * <p>See Appendix A in the Java Cryptogaphy Architecture API Specification + * & Reference for information about standard cryptographic service names, + * standard algorithm names and standard attribute names.</p> + * + * @param filter the criteria for selecting providers. The filter is case- + * insensitive. + * @return all the installed providers that satisfy the selection criteria, + * or <code>null</code> if no such providers have been installed. + * @throws InvalidParameterException if the filter is not in the required + * format. + * @see #getProviders(String) + */ + public static Provider[] getProviders(Map filter) + { + if (providers == null || providers.isEmpty()) + return null; + + if (filter == null) + return getProviders(); + + Set querries = filter.keySet(); + if (querries == null || querries.isEmpty()) + return getProviders(); + + LinkedHashSet result = new LinkedHashSet(providers); // assume all + int dot, ws; + String querry, service, algorithm, attribute, value; + LinkedHashSet serviceProviders = new LinkedHashSet(); // preserve insertion order + for (Iterator i = querries.iterator(); i.hasNext(); ) + { + querry = (String) i.next(); + if (querry == null) // all providers + continue; + + querry = querry.trim(); + if (querry.length() == 0) // all providers + continue; + + dot = querry.indexOf('.'); + if (dot == -1) // syntax error + throw new InvalidParameterException( + "missing dot in '" + String.valueOf(querry)+"'"); + + value = (String) filter.get(querry); + // deconstruct querry into [service, algorithm, attribute] + if (value == null || value.trim().length() == 0) // <service>.<algorithm> + { + value = null; + attribute = null; + service = querry.substring(0, dot).trim(); + algorithm = querry.substring(dot+1).trim(); + } + else // <service>.<algorithm> <attribute> + { + ws = querry.indexOf(' '); + if (ws == -1) + throw new InvalidParameterException( + "value (" + String.valueOf(value) + + ") is not empty, but querry (" + String.valueOf(querry) + + ") is missing at least one space character"); + value = value.trim(); + attribute = querry.substring(ws+1).trim(); + // was the dot in the attribute? + if (attribute.indexOf('.') != -1) + throw new InvalidParameterException( + "attribute_name (" + String.valueOf(attribute) + + ") in querry (" + String.valueOf(querry) + ") contains a dot"); + + querry = querry.substring(0, ws).trim(); + service = querry.substring(0, dot).trim(); + algorithm = querry.substring(dot+1).trim(); + } + + // service and algorithm must not be empty + if (service.length() == 0) + throw new InvalidParameterException( + "<crypto_service> in querry (" + String.valueOf(querry) + + ") is empty"); + + if (algorithm.length() == 0) + throw new InvalidParameterException( + "<algorithm_or_type> in querry (" + String.valueOf(querry) + + ") is empty"); + + selectProviders(service, algorithm, attribute, value, result, serviceProviders); + result.retainAll(serviceProviders); // eval next retaining found providers + if (result.isEmpty()) // no point continuing + break; + } + + if (result.isEmpty()) + return null; + + return (Provider[]) result.toArray(new Provider[0]); + } + + private static void selectProviders(String svc, String algo, String attr, + String val, LinkedHashSet providerSet, + LinkedHashSet result) + { + result.clear(); // ensure we start with an empty result set + for (Iterator i = providerSet.iterator(); i.hasNext(); ) + { + Provider p = (Provider) i.next(); + if (provides(p, svc, algo, attr, val)) + result.add(p); + } + } + + private static boolean provides(Provider p, String svc, String algo, + String attr, String val) + { + Iterator it; + String serviceDotAlgorithm = null; + String key = null; + String realVal; + boolean found = false; + // if <svc>.<algo> <attr> is in the set then so is <svc>.<algo> + // but it may be stored under an alias <algo>. resolve + outer: for (int r = 0; r < 3; r++) // guard against circularity + { + serviceDotAlgorithm = (svc+"."+String.valueOf(algo)).trim(); + for (it = p.keySet().iterator(); it.hasNext(); ) + { + key = (String) it.next(); + if (key.equalsIgnoreCase(serviceDotAlgorithm)) // eureka + { + found = true; + break outer; + } + // it may be there but as an alias + if (key.equalsIgnoreCase(ALG_ALIAS + serviceDotAlgorithm)) + { + algo = p.getProperty(key); + continue outer; + } + // else continue inner + } + } + + if (!found) + return false; + + // found a candidate for the querry. do we have an attr to match? + if (val == null) // <service>.<algorithm> querry + return true; + + // <service>.<algorithm> <attribute>; find the key entry that match + String realAttr; + int limit = serviceDotAlgorithm.length() + 1; + for (it = p.keySet().iterator(); it.hasNext(); ) + { + key = (String) it.next(); + if (key.length() <= limit) + continue; + + if (key.substring(0, limit).equalsIgnoreCase(serviceDotAlgorithm+" ")) + { + realAttr = key.substring(limit).trim(); + if (! realAttr.equalsIgnoreCase(attr)) + continue; + + // eveything matches so far. do the value + realVal = p.getProperty(key); + if (realVal == null) + return false; + + realVal = realVal.trim(); + // is it a string value? + if (val.equalsIgnoreCase(realVal)) + return true; + + // assume value is a number. cehck for greater-than-or-equal + return (new Integer(val).intValue() >= new Integer(realVal).intValue()); + } + } + + return false; + } +} diff --git a/libjava/classpath/java/security/SecurityPermission.java b/libjava/classpath/java/security/SecurityPermission.java new file mode 100644 index 00000000000..6aba18f346e --- /dev/null +++ b/libjava/classpath/java/security/SecurityPermission.java @@ -0,0 +1,178 @@ +/* SecurityPermission.java -- Class for named security permissions + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This class provides a mechanism for specified named permissions + * related to the Java security framework. These permissions have no + * associated actions list. They are either granted or not granted. + * + * <p>The list of valid permission names is:<br> + * <table border=1> + * <tr><th>Permission Name</th><th>Permission Allows</th><th>Risks</th</tr> + * <tr> + * <td><code>createAccessControlContext</code></td> + * <td>Allows creation of an AccessControlContext</td> + * <td>The new control context can have a rogue DomainCombiner, leading + * to a privacy leak</td></tr> + * <tr> + * <td><code>getDomainCombiner</code></td> + * <td>Get a DomainCombiner from an AccessControlContext</td> + * <td>Access to a DomainCombiner can lead to a privacy leak</td></tr> + * <tr> + * <td><code>getPolicy</code></td> + * <td>Allows retrieval of the system security policy</td> + * <td>Malicious code can use information from the policy to better plan + * an attack</td></tr> + * <tr> + * <td><code>setPolicy</code></td> + * <td>Allows the security policy to be changed</td> + * <td>Malicious code can give itself any permission it wants</td></tr> + * <tr> + * <td><code>getProperty.</code><em>key</em></td> + * <td>Retrieve the property specified by the key</td> + * <td>Malicious code can use information from the property to better plan + * an attack</td></tr> + * <tr> + * <td><code>setProperty.</code><em>key</em></td> + * <td>Allows changing of the value of all properties implied by key</td> + * <td>Malicious code can insert rogue classes to steal keys or recreate + * the security policy with whatever permissions it desires</td></tr> + * <tr> + * <td><code>insertProvider.</code><em>key</em></td> + * <td>Allows the named provider to be added</td> + * <td>Malicious code can insert rogue providers that steal data</td></tr> + * <tr> + * <td><code>removeProvider.</code><em>key</em></td> + * <td>Allows the named provider to be removed</td> + * <td>A missing provider can cripple code that relies on it</td></tr> + * <tr> + * <td><code>setSystemScope</code></td> + * <td>Allows the system identity scope to be set</td> + * <td>Malicious code can add certificates not available in the original + * identity scope, to gain more permissions</td></tr> + * <tr> + * <td><code>setIdentityPublicKey</code></td> + * <td>Allows the public key of an Identity to be set</td> + * <td>Malicious code can install its own key to gain permissions not + * allowed by the original identity scope</td></tr> + * <tr> + * <td><code>SetIdentityInfo</code></td> + * <td>Allows the description of an Identity to be set</td> + * <td>Malicious code can spoof users into trusting a fake identity</td></tr> + * <tr> + * <td><code>addIdentityCertificate</code></td> + * <td>Allows a certificate to be set for the public key of an identity</td> + * <td>The public key can become trusted to a wider audience than originally + * intended</td></tr> + * <tr> + * <td><code>removeIdentityCertificate</code></td> + * <td>Allows removal of a certificate from an identity's public key</td> + * <td>The public key can become less trusted than it should be</td></tr> + * <tr> + * <td><code>printIdentity</code></td> + * <td>View the name of the identity and scope, and whether they are + * trusted</td> + * <td>The scope may include a filename, which provides an entry point for + * further security breaches</td></tr> + * <tr> + * <td><code>clearProviderProperties.</code><em>key</em></td> + * <td>Allows the properties of the named provider to be cleared</td> + * <td>This can disable parts of the program which depend on finding the + * provider</td></tr> + * <tr> + * <td><code>putProviderProperty.</code><em>key</em></td> + * <td>Allows the properties of the named provider to be changed</td> + * <td>Malicious code can replace the implementation of a provider</td></tr> + * <tr> + * <td><code>removeProviderProperty.</code><em>key</em></td> + * <td>Allows the properties of the named provider to be deleted</td> + * <td>This can disable parts of the program which depend on finding the + * provider</td></tr> + * <tr> + * <td><code>getSignerPrivateKey</code></td> + * <td>Allows the retrieval of the private key for a signer</td> + * <td>Anyone that can access the private key can claim to be the + * Signer</td></tr> + * <tr> + * <td><code>setSignerKeyPair</code></td> + * <td>Allows the public and private key of a Signer to be changed</td> + * <td>The replacement might be a weaker encryption, or the attacker + * can use knowledge of the replaced key to decrypt an entire + * communication session</td></tr> + * </table> + * + * <p>There is some degree of security risk in granting any of these + * permissions. Some of them can completely compromise system security. + * Please exercise extreme caution in granting these permissions. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permission + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public final class SecurityPermission extends BasicPermission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5236109936224050470L; + + /** + * Create a new instance with the specified name. + * + * @param name the name to assign to this permission + */ + public SecurityPermission(String name) + { + super(name); + } + + /** + * Create a new instance with the specified name. As SecurityPermission + * carries no actions, the second parameter is ignored. + * + * @param name the name to assign to this permission + * @param actions ignored + */ + public SecurityPermission(String name, String actions) + { + super(name); + } +} // class SecurityPermission diff --git a/libjava/classpath/java/security/Signature.java b/libjava/classpath/java/security/Signature.java new file mode 100644 index 00000000000..852c959220f --- /dev/null +++ b/libjava/classpath/java/security/Signature.java @@ -0,0 +1,636 @@ +/* Signature.java --- Signature Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.security.Engine; + +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; + +/** + * <p>This <code>Signature</code> class is used to provide applications the + * functionality of a digital signature algorithm. Digital signatures are used + * for authentication and integrity assurance of digital data.</p> + * + * <p>The signature algorithm can be, among others, the NIST standard <i>DSS</i>, + * using <i>DSA</i> and <i>SHA-1</i>. The <i>DSA</i> algorithm using the + * <i>SHA-1</i> message digest algorithm can be specified as <code>SHA1withDSA + * </code>. In the case of <i>RSA</i>, there are multiple choices for the + * message digest algorithm, so the signing algorithm could be specified as, for + * example, <code>MD2withRSA</code>, <code>MD5withRSA</code>, or + * <code>SHA1withRSA</code>. The algorithm name must be specified, as there is + * no default.</p> + * + * <p>Like other algorithm-based classes in Java Security, <code>Signature</code> + * provides implementation-independent algorithms, whereby a caller (application + * code) requests a particular signature algorithm and is handed back a properly + * initialized <code>Signature</code> object. It is also possible, if desired, + * to request a particular algorithm from a particular provider. See the + * <code>getInstance()</code> methods.</p> + * + * <p>Thus, there are two ways to request a <code>Signature</code> algorithm + * object: by specifying either just an algorithm name, or both an algorithm + * name and a package provider.</p> + * + * <p>If just an algorithm name is specified, the system will determine if there + * is an implementation of the algorithm requested available in the environment, + * and if there is more than one, if there is a preferred one.</p> + * + * <p>If both an algorithm name and a package provider are specified, the system + * will determine if there is an implementation of the algorithm in the package + * requested, and throw an exception if there is not.</p> + * + * <p>A <code>Signature</code> object can be used to generate and verify digital + * signatures.</p> + * + * <p>There are three phases to the use of a <code>Signature</code> object for + * either signing data or verifying a signature:</p> + * + * <ol> + * <li>Initialization, with either + * <ul> + * <li>a public key, which initializes the signature for verification + * (see <code>initVerify()</code>), or</li> + * <li>a private key (and optionally a Secure Random Number Generator), + * which initializes the signature for signing (see + * {@link #initSign(PrivateKey)} and {@link #initSign(PrivateKey, SecureRandom)} + * ).</li> + * </ul></li> + * <li>Updating<br/> + * Depending on the type of initialization, this will update the bytes to + * be signed or verified. See the update methods.<br/></li> + * <li>Signing or Verifying a signature on all updated bytes. See the + * <code>sign()</code> methods and the <code>verify()</code> method.</li> + * </ol> + * + * <p>Note that this class is abstract and extends from {@link SignatureSpi} for + * historical reasons. Application developers should only take notice of the + * methods defined in this <code>Signature</code> class; all the methods in the + * superclass are intended for cryptographic service providers who wish to + * supply their own implementations of digital signature algorithms. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class Signature extends SignatureSpi +{ + /** Service name for signatures. */ + private static final String SIGNATURE = "Signature"; + + /** + * Possible <code>state</code> value, signifying that this signature object + * has not yet been initialized. + */ + protected static final int UNINITIALIZED = 0; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Possible <code>state</code> value, signifying that this signature object + * has been initialized for signing. + */ + protected static final int SIGN = 2; + + /** + * Possible <code>state</code> value, signifying that this signature object + * has been initialized for verification. + */ + protected static final int VERIFY = 3; + + /** Current state of this signature object. */ + protected int state = UNINITIALIZED; + + private String algorithm; + Provider provider; + + /** + * Creates a <code>Signature</code> object for the specified algorithm. + * + * @param algorithm the standard string name of the algorithm. See Appendix A + * in the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + */ + protected Signature(String algorithm) + { + this.algorithm = algorithm; + state = UNINITIALIZED; + } + + /** + * Generates a <code>Signature</code> object that implements the specified + * digest algorithm. If the default provider package provides an + * implementation of the requested digest algorithm, an instance of + * <code>Signature</code> containing that implementation is returned. If the + * algorithm is not available in the default package, other packages are + * searched. + * + * @param algorithm the standard name of the algorithm requested. See Appendix + * A in the Java Cryptography Architecture API Specification & Reference + * for information about standard algorithm names. + * @return the new Signature object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * environment. + */ + public static Signature getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignored. + } + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Generates a <code>Signature</code> object implementing the specified + * algorithm, as supplied from the specified provider, if such an algorithm + * is available from the provider. + * + * @param algorithm the name of the algorithm requested. See Appendix A in + * the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the name of the provider. + * @return the new <code>Signature</code> object. + * @throws NoSuchAlgorithmException if the algorithm is not available in the + * package supplied by the requested provider. + * @throws NoSuchProviderException if the provider is not available in the + * environment. + * @throws IllegalArgumentException if the provider name is <code>null</code> + * or empty. + * @see Provider + */ + public static Signature getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (provider == null || provider.length() == 0) + throw new IllegalArgumentException("Illegal provider"); + + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Generates a <code>Signature</code> object implementing the specified + * algorithm, as supplied from the specified provider, if such an algorithm + * is available from the provider. Note: the provider doesn't have to be + * registered. + * + * @param algorithm the name of the algorithm requested. See Appendix A in + * the Java Cryptography Architecture API Specification & Reference for + * information about standard algorithm names. + * @param provider the provider. + * @return the new <code>Signature</code> object. + * @throws NoSuchAlgorithmException if the <code>algorithm</code> is not + * available in the package supplied by the requested <code>provider</code>. + * @throws IllegalArgumentException if the <code>provider</code> is + * <code>null</code>. + * @since 1.4 + * @see Provider + */ + public static Signature getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("Illegal provider"); + + Signature result = null; + Object o = null; + try + { + o = Engine.getInstance(SIGNATURE, algorithm, provider); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + + if (o instanceof SignatureSpi) + { + result = new DummySignature((SignatureSpi) o, algorithm); + } + else if (o instanceof Signature) + { + result = (Signature) o; + result.algorithm = algorithm; + } + else + { + throw new NoSuchAlgorithmException(algorithm); + } + result.provider = provider; + return result; + } + + /** + * Returns the provider of this signature object. + * + * @return the provider of this signature object. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this object for verification. If this method is called again + * with a different argument, it negates the effect of this call. + * + * @param publicKey the public key of the identity whose signature is going + * to be verified. + * @throws InvalidKeyException if the key is invalid. + */ + public final void initVerify(PublicKey publicKey) throws InvalidKeyException + { + state = VERIFY; + engineInitVerify(publicKey); + } + + /** + * <p>Initializes this object for verification, using the public key from the + * given certificate.</p> + * + * <p>If the certificate is of type <i>X.509</i> and has a <i>key usage</i> + * extension field marked as <i>critical</i>, and the value of the <i>key + * usage</i> extension field implies that the public key in the certificate + * and its corresponding private key are not supposed to be used for digital + * signatures, an {@link InvalidKeyException} is thrown.</p> + * + * @param certificate the certificate of the identity whose signature is + * going to be verified. + * @throws InvalidKeyException if the public key in the certificate is not + * encoded properly or does not include required parameter information or + * cannot be used for digital signature purposes. + */ + public final void initVerify(Certificate certificate) + throws InvalidKeyException + { + state = VERIFY; + if (certificate.getType().equals("X509")) + { + X509Certificate cert = (X509Certificate) certificate; + boolean[]array = cert.getKeyUsage(); + if (array != null && array[0] == false) + throw new InvalidKeyException( + "KeyUsage of this Certificate indicates it cannot be used for digital signing"); + } + this.initVerify(certificate.getPublicKey()); + } + + /** + * Initialize this object for signing. If this method is called again with a + * different argument, it negates the effect of this call. + * + * @param privateKey the private key of the identity whose signature is going + * to be generated. + * @throws InvalidKeyException if the key is invalid. + */ + public final void initSign(PrivateKey privateKey) throws InvalidKeyException + { + state = SIGN; + engineInitSign(privateKey); + } + + /** + * Initialize this object for signing. If this method is called again with a + * different argument, it negates the effect of this call. + * + * @param privateKey the private key of the identity whose signature is going + * to be generated. + * @param random the source of randomness for this signature. + * @throws InvalidKeyException if the key is invalid. + */ + public final void initSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + state = SIGN; + engineInitSign(privateKey, random); + } + + /** + * <p>Returns the signature bytes of all the data updated. The format of the + * signature depends on the underlying signature scheme.</p> + * + * <p>A call to this method resets this signature object to the state it was + * in when previously initialized for signing via a call to + * <code>initSign(PrivateKey)</code>. That is, the object is reset and + * available to generate another signature from the same signer, if desired, + * via new calls to <code>update()</code> and <code>sign()</code>.</p> + * + * @return the signature bytes of the signing operation's result. + * @throws SignatureException if this signature object is not initialized + * properly. + */ + public final byte[] sign() throws SignatureException + { + if (state == SIGN) + return engineSign(); + else + throw new SignatureException(); + } + + /** + * <p>Finishes the signature operation and stores the resulting signature + * bytes in the provided buffer <code>outbuf</code>, starting at <code>offset + * </code>. The format of the signature depends on the underlying signature + * scheme.</p> + * + * <p>This signature object is reset to its initial state (the state it was + * in after a call to one of the <code>initSign()</code> methods) and can be + * reused to generate further signatures with the same private key.</p> + * + * @param outbuf buffer for the signature result. + * @param offset offset into outbuf where the signature is stored. + * @param len number of bytes within outbuf allotted for the signature. + * @return the number of bytes placed into outbuf. + * @throws SignatureException if an error occurs or len is less than the + * actual signature length. + * @since 1.2 + */ + public final int sign(byte[] outbuf, int offset, int len) + throws SignatureException + { + if (state == SIGN) + return engineSign(outbuf, offset, len); + else + throw new SignatureException(); + } + + /** + * <p>Verifies the passed-in signature.</p> + * + * <p>A call to this method resets this signature object to the state it was + * in when previously initialized for verification via a call to + * <code>initVerify(PublicKey)</code>. That is, the object is reset and + * available to verify another signature from the identity whose public key + * was specified in the call to <code>initVerify()</code>.</p> + * + * @param signature the signature bytes to be verified. + * @return <code>true</code> if the signature was verified, <code>false</code> + * if not. + * @throws SignatureException if this signature object is not initialized + * properly, or the passed-in signature is improperly encoded or of the wrong + * type, etc. + */ + public final boolean verify(byte[]signature) throws SignatureException + { + if (state == VERIFY) + return engineVerify(signature); + else + throw new SignatureException(); + } + + /** + * <p>Verifies the passed-in <code>signature</code> in the specified array of + * bytes, starting at the specified <code>offset</code>.</p> + * + * <p>A call to this method resets this signature object to the state it was + * in when previously initialized for verification via a call to + * <code>initVerify(PublicKey)</code>. That is, the object is reset and + * available to verify another signature from the identity whose public key + * was specified in the call to <code>initVerify()</code>.</p> + * + * @param signature the signature bytes to be verified. + * @param offset the offset to start from in the array of bytes. + * @param length the number of bytes to use, starting at offset. + * @return <code>true</code> if the signature was verified, <code>false</code> + * if not. + * @throws SignatureException if this signature object is not initialized + * properly, or the passed-in <code>signature</code> is improperly encoded or + * of the wrong type, etc. + * @throws IllegalArgumentException if the <code>signature</code> byte array + * is <code>null</code>, or the <code>offset</code> or <code>length</code> is + * less than <code>0</code>, or the sum of the <code>offset</code> and + * <code>length</code> is greater than the length of the <code>signature</code> + * byte array. + */ + public final boolean verify(byte[] signature, int offset, int length) + throws SignatureException + { + if (state != VERIFY) + throw new SignatureException("illegal state"); + + if (signature == null) + throw new IllegalArgumentException("signature is null"); + if (offset < 0) + throw new IllegalArgumentException("offset is less than 0"); + if (length < 0) + throw new IllegalArgumentException("length is less than 0"); + if (offset + length < signature.length) + throw new IllegalArgumentException("range is out of bounds"); + + return engineVerify(signature, offset, length); + } + + /** + * Updates the data to be signed or verified by a byte. + * + * @param b the byte to use for the update. + * @throws SignatureException if this signature object is not initialized + * properly. + */ + public final void update(byte b) throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(b); + else + throw new SignatureException(); + } + + /** + * Updates the data to be signed or verified, using the specified array of + * bytes. + * + * @param data the byte array to use for the update. + * @throws SignatureException if this signature object is not initialized + * properly. + */ + public final void update(byte[]data) throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(data, 0, data.length); + else + throw new SignatureException(); + } + + /** + * Updates the data to be signed or verified, using the specified array of + * bytes, starting at the specified offset. + * + * @param data the array of bytes. + * @param off the offset to start from in the array of bytes. + * @param len the number of bytes to use, starting at offset. + * @throws SignatureException if this signature object is not initialized + * properly. + */ + public final void update(byte[]data, int off, int len) + throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(data, off, len); + else + throw new SignatureException(); + } + + /** + * Returns the name of the algorithm for this signature object. + * + * @return the name of the algorithm for this signature object. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns a string representation of this signature object, providing + * information that includes the state of the object and the name of the + * algorithm used. + * + * @return a string representation of this signature object. + */ + public String toString() + { + return (algorithm + " Signature"); + } + + /** + * Sets the specified algorithm parameter to the specified value. This method + * supplies a general-purpose mechanism through which it is possible to set + * the various parameters of this object. A parameter may be any settable + * parameter for the algorithm, such as a parameter size, or a source of + * random bits for signature generation (if appropriate), or an indication of + * whether or not to perform a specific but optional computation. A uniform + * algorithm-specific naming scheme for each parameter is desirable but left + * unspecified at this time. + * + * @param param the string identifier of the parameter. + * @param value the parameter value. + * @throws InvalidParameterException if param is an invalid parameter for this + * signature algorithm engine, the parameter is already set and cannot be set + * again, a security exception occurs, and so on. + * @see #getParameter(String) + * @deprecated Use setParameter(AlgorithmParameterSpec). + */ + public final void setParameter(String param, Object value) + throws InvalidParameterException + { + engineSetParameter(param, value); + } + + /** + * Initializes this signature engine with the specified parameter set. + * + * @param params the parameters. + * @throws InvalidAlgorithmParameterException if the given parameters are + * inappropriate for this signature engine. + * @see #getParameters() + */ + public final void setParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + engineSetParameter(params); + } + + /** + * <p>Returns the parameters used with this signature object.</p> + * + * <p>The returned parameters may be the same that were used to initialize + * this signature, or may contain a combination of default and randomly + * generated parameter values used by the underlying signature implementation + * if this signature requires algorithm parameters but was not initialized + * with any. + * + * @return the parameters used with this signature, or <code>null</code> if + * this signature does not use any parameters. + * @see #setParameter(AlgorithmParameterSpec) + */ + public final AlgorithmParameters getParameters() + { + return engineGetParameters(); + } + + /** + * Gets the value of the specified algorithm parameter. This method supplies + * a general-purpose mechanism through which it is possible to get the various + * parameters of this object. A parameter may be any settable parameter for + * the algorithm, such as a parameter size, or a source of random bits for + * signature generation (if appropriate), or an indication of whether or not + * to perform a specific but optional computation. A uniform + * algorithm-specific naming scheme for each parameter is desirable but left + * unspecified at this time. + * + * @param param the string name of the parameter. + * @return the object that represents the parameter value, or null if there + * is none. + * @throws InvalidParameterException if param is an invalid parameter for this + * engine, or another exception occurs while trying to get this parameter. + * @see #setParameter(String, Object) + * @deprecated + */ + public final Object getParameter(String param) + throws InvalidParameterException + { + return engineGetParameter(param); + } + + /** + * Returns a clone if the implementation is cloneable. + * + * @return a clone if the implementation is cloneable. + * @throws CloneNotSupportedException if this is called on an implementation + * that does not support {@link Cloneable}. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/SignatureException.java b/libjava/classpath/java/security/SignatureException.java new file mode 100644 index 00000000000..e294c16c3f6 --- /dev/null +++ b/libjava/classpath/java/security/SignatureException.java @@ -0,0 +1,70 @@ +/* SignatureException.java -- Generic error in signature + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when a problem is encountered with a + * digital signature. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class SignatureException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7509989324975124438L; + + /** + * Create an instance with no descriptive error message. + */ + public SignatureException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the message + */ + public SignatureException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/SignatureSpi.java b/libjava/classpath/java/security/SignatureSpi.java new file mode 100644 index 00000000000..471a73d17cd --- /dev/null +++ b/libjava/classpath/java/security/SignatureSpi.java @@ -0,0 +1,302 @@ +/* SignatureSpi.java --- Signature Service Provider Interface + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * <p>This class defines the <i>Service Provider Interface (SPI)</i> for the + * {@link Signature} class, which is used to provide the functionality of a + * digital signature algorithm. Digital signatures are used for authentication + * and integrity assurance of digital data.</p> + * + * <p>All the abstract methods in this class must be implemented by each + * cryptographic service provider who wishes to supply the implementation of a + * particular signature algorithm. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @since 1.2 + * @see Signature + */ +public abstract class SignatureSpi +{ + /** Application-specified source of randomness. */ + protected SecureRandom appRandom; + + public SignatureSpi() + { + appRandom = null; + } + + /** + * Initializes this signature object with the specified public key for + * verification operations. + * + * @param publicKey the public key of the identity whose signature is going + * to be verified. + * @throws InvalidKeyException if the key is improperly encoded, parameters + * are missing, and so on. + */ + protected abstract void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException; + + /** + * Initializes this signature object with the specified private key for + * signing operations. + * + * @param privateKey the private key of the identity whose signature will be + * generated. + * @throws InvalidKeyException if the key is improperly encoded, parameters + * are missing, and so on. + */ + protected abstract void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException; + + /** + * <p>Initializes this signature object with the specified private key and + * source of randomness for signing operations.</p> + * + * <p>This concrete method has been added to this previously-defined abstract + * class. (For backwards compatibility, it cannot be abstract.)</p> + * + * @param privateKey the private key of the identity whose signature will be + * generated. + * @param random the source of randomness. + * @throws InvalidKeyException if the key is improperly encoded, parameters + * are missing, and so on. + * @since 1.2 + */ + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + appRandom = random; + engineInitSign(privateKey); + } + + /** + * Updates the data to be signed or verified using the specified byte. + * + * @param b the byte to use for the update. + * @throws SignatureException if the engine is not initialized properly. + */ + protected abstract void engineUpdate(byte b) throws SignatureException; + + /** + * Updates the data to be signed or verified, using the specified array of + * bytes, starting at the specified offset. + * + * @param b the array of bytes. + * @param off the offset to start from in the array of bytes. + * @param len the number of bytes to use, starting at offset. + * @throws SignatureException if the engine is not initialized properly. + */ + protected abstract void engineUpdate(byte[] b, int off, int len) + throws SignatureException; + + /** + * Returns the signature bytes of all the data updated so far. The format of + * the signature depends on the underlying signature scheme. + * + * @return the signature bytes of the signing operation's result. + * @throws SignatureException if the engine is not initialized properly. + */ + protected abstract byte[] engineSign() throws SignatureException; + + /** + * <p>Finishes this signature operation and stores the resulting signature + * bytes in the provided buffer <code>outbuf</code>, starting at <code>offset + * </code>. The format of the signature depends on the underlying signature + * scheme.</p> + * + * <p>The signature implementation is reset to its initial state (the state it + * was in after a call to one of the <code>engineInitSign()</code> methods) + * and can be reused to generate further signatures with the same private key. + * This method should be abstract, but we leave it concrete for binary + * compatibility. Knowledgeable providers should override this method.</p> + * + * @param outbuf buffer for the signature result. + * @param offset offset into outbuf where the signature is stored. + * @param len number of bytes within outbuf allotted for the signature. Both + * this default implementation and the <b>GNU</b> provider do not return + * partial digests. If the value of this parameter is less than the actual + * signature length, this method will throw a {@link SignatureException}. This + * parameter is ignored if its value is greater than or equal to the actual + * signature length. + * @return the number of bytes placed into <code>outbuf</code>. + * @throws SignatureException if an error occurs or len is less than the + * actual signature length. + * @since 1.2 + */ + protected int engineSign(byte[] outbuf, int offset, int len) + throws SignatureException + { + byte[] tmp = engineSign(); + if (tmp.length > len) + throw new SignatureException("Invalid Length"); + + System.arraycopy(outbuf, offset, tmp, 0, tmp.length); + return tmp.length; + } + + /** + * Verifies the passed-in signature. + * + * @param sigBytes the signature bytes to be verified. + * @return <code>true</code> if the signature was verified, <code>false</code> + * if not. + * @throws SignatureException if the engine is not initialized properly, or + * the passed-in signature is improperly encoded or of the wrong type, etc. + */ + protected abstract boolean engineVerify(byte[] sigBytes) + throws SignatureException; + + /** + * <p>Verifies the passed-in <code>signature</code> in the specified array of + * bytes, starting at the specified <code>offset</code>.</p> + * + * <p>Note: Subclasses should overwrite the default implementation.</p> + * + * @param sigBytes the signature bytes to be verified. + * @param offset the offset to start from in the array of bytes. + * @param length the number of bytes to use, starting at offset. + * @return <code>true</code> if the signature was verified, <code>false</code> + * if not. + * @throws SignatureException if the engine is not initialized properly, or + * the passed-in <code>signature</code> is improperly encoded or of the wrong + * type, etc. + */ + protected boolean engineVerify(byte[] sigBytes, int offset, int length) + throws SignatureException + { + byte[] tmp = new byte[length]; + System.arraycopy(sigBytes, offset, tmp, 0, length); + return engineVerify(tmp); + } + + /** + * Sets the specified algorithm parameter to the specified value. This method + * supplies a general-purpose mechanism through which it is possible to set + * the various parameters of this object. A parameter may be any settable + * parameter for the algorithm, such as a parameter size, or a source of + * random bits for signature generation (if appropriate), or an indication of + * whether or not to perform a specific but optional computation. A uniform + * algorithm-specific naming scheme for each parameter is desirable but left + * unspecified at this time. + * + * @param param the string identifier of the parameter. + * @param value the parameter value. + * @throws InvalidParameterException if <code>param</code> is an invalid + * parameter for this signature algorithm engine, the parameter is already set + * and cannot be set again, a security exception occurs, and so on. + * @deprecated Replaced by engineSetParameter(AlgorithmParameterSpec). + */ + protected abstract void engineSetParameter(String param, Object value) + throws InvalidParameterException; + + /** + * This method is overridden by providers to initialize this signature engine + * with the specified parameter set. + * + * @param params the parameters. + * @throws UnsupportedOperationException if this method is not overridden by + * a provider. + * @throws InvalidAlgorithmParameterException if this method is overridden by + * a provider and the the given parameters are inappropriate for this + * signature engine. + */ + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + throw new UnsupportedOperationException(); + } + + /** + * <p>This method is overridden by providers to return the parameters used + * with this signature engine, or <code>null</code> if this signature engine + * does not use any parameters.</p> + * + * <p>The returned parameters may be the same that were used to initialize + * this signature engine, or may contain a combination of default and randomly + * generated parameter values used by the underlying signature implementation + * if this signature engine requires algorithm parameters but was not + * initialized with any.</p> + * + * @return the parameters used with this signature engine, or <code>null</code> + * if this signature engine does not use any parameters. + * @throws UnsupportedOperationException if this method is not overridden by + * a provider. + */ + protected AlgorithmParameters engineGetParameters() + { + throw new UnsupportedOperationException(); + } + + /** + * Gets the value of the specified algorithm parameter. This method supplies + * a general-purpose mechanism through which it is possible to get the various + * parameters of this object. A parameter may be any settable parameter for + * the algorithm, such as a parameter size, or a source of random bits for + * signature generation (if appropriate), or an indication of whether or not + * to perform a specific but optional computation. A uniform algorithm-specific + * naming scheme for each parameter is desirable but left unspecified at this + * time. + * + * @param param the string name of the parameter. + * @return the object that represents the parameter value, or <code>null</code> + * if there is none. + * @throws InvalidParameterException if <code>param</code> is an invalid + * parameter for this engine, or another exception occurs while trying to get + * this parameter. + * @deprecated + */ + protected abstract Object engineGetParameter(String param) + throws InvalidParameterException; + + /** + * Returns a clone if the implementation is cloneable. + * + * @return a clone if the implementation is cloneable. + * @throws CloneNotSupportedException if this is called on an implementation + * that does not support {@link Cloneable}. + * @see Cloneable + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/SignedObject.java b/libjava/classpath/java/security/SignedObject.java new file mode 100644 index 00000000000..d565b2ea3b4 --- /dev/null +++ b/libjava/classpath/java/security/SignedObject.java @@ -0,0 +1,240 @@ +/* SignedObject.java --- Signed Object Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * <p><code>SignedObject</code> is a class for the purpose of creating authentic + * runtime objects whose integrity cannot be compromised without being detected. + * </p> + * + * <p>More specifically, a <code>SignedObject</code> contains another + * {@link Serializable} object, the (to-be-)signed object and its signature.</p> + * + * <p>The signed object is a <i>"deep copy"</i> (in serialized form) of an + * original object. Once the copy is made, further manipulation of the original + * object has no side effect on the copy.</p> + * + * <p>The underlying signing algorithm is designated by the {@link Signature} + * object passed to the constructor and the <code>verify()</code> method. A + * typical usage for signing is the following:</p> + * + * <pre> + * Signature signingEngine = Signature.getInstance(algorithm, provider); + * SignedObject so = new SignedObject(myobject, signingKey, signingEngine); + * </pre> + * + * <p>A typical usage for verification is the following (having received + * <code>SignedObject</code> so):</p> + * + * <pre> + * Signature verificationEngine = Signature.getInstance(algorithm, provider); + * if (so.verify(publickey, verificationEngine)) + * try + * { + * Object myobj = so.getObject(); + * } + * catch (ClassNotFoundException ignored) {}; + * </pre> + * + * <p>Several points are worth noting. First, there is no need to initialize the + * signing or verification engine, as it will be re-initialized inside the + * constructor and the <code>verify()</code> method. Secondly, for verification + * to succeed, the specified public key must be the public key corresponding to + * the private key used to generate the <code>SignedObject</code>.</p> + * + * <p>More importantly, for flexibility reasons, the <code>constructor</code> + * and <code>verify()</code> method allow for customized signature engines, + * which can implement signature algorithms that are not installed formally as + * part of a crypto provider. However, it is crucial that the programmer writing + * the verifier code be aware what {@link Signature} engine is being used, as + * its own implementation of the <code>verify()</code> method is invoked to + * verify a signature. In other words, a malicious {@link Signature} may choose + * to always return <code>true</code> on verification in an attempt to bypass a + * security check.</p> + * + * <p>The signature algorithm can be, among others, the NIST standard <i>DSS</i>, + * using <i>DSA</i> and <i>SHA-1</i>. The algorithm is specified using the same + * convention as that for signatures. The <i>DSA</i> algorithm using the + * <i>SHA-1</i> message digest algorithm can be specified, for example, as + * <code>"SHA/DSA"</code> or <code>"SHA-1/DSA"</code> (they are equivalent). In + * the case of <i>RSA</i>, there are multiple choices for the message digest + * algorithm, so the signing algorithm could be specified as, for example, + * <code>"MD2/RSA"</code>, <code>"MD5/RSA"</code> or <code>"SHA-1/RSA"</code>. + * The algorithm name must be specified, as there is no default.</p> + * + * <p>The name of the Cryptography Package Provider is designated also by the + * {@link Signature} parameter to the <code>constructor</code> and the <code> + * verify()</code> method. If the provider is not specified, the default + * provider is used. Each installation can be configured to use a particular + * provider as default.</p> + * + * <p>Potential applications of <code>SignedObject</code> include:</p> + * + * <ul> + * <li>It can be used internally to any Java runtime as an unforgeable + * authorization token -- one that can be passed around without the fear that + * the token can be maliciously modified without being detected.</li> + * <li>It can be used to sign and serialize data/object for storage outside the + * Java runtime (e.g., storing critical access control data on disk).</li> + * <li>Nested <i>SignedObjects</i> can be used to construct a logical sequence + * of signatures, resembling a chain of authorization and delegation.</li> + * </ul> + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @since 1.2 + * @see Signature + */ +public final class SignedObject implements Serializable +{ + private static final long serialVersionUID = 720502720485447167L; + + /** @serial */ + private byte[] content; + /** @serial */ + private byte[] signature; + /** @serial */ + private String thealgorithm; + + /** + * Constructs a <code>SignedObject</code> from any {@link Serializable} + * object. The given object is signed with the given signing key, using the + * designated signature engine. + * + * @param object the object to be signed. + * @param signingKey the private key for signing. + * @param signingEngine the signature signing engine. + * @throws IOException if an error occurs during serialization. + * @throws InvalidKeyException if the key is invalid. + * @throws SignatureException if signing fails. + */ + public SignedObject(Serializable object, PrivateKey signingKey, + Signature signingEngine) + throws IOException, InvalidKeyException, SignatureException + { + thealgorithm = signingEngine.getAlgorithm(); + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + ObjectOutputStream p = new ObjectOutputStream(ostream); + p.writeObject(object); + p.flush(); + p.close(); + + content = ostream.toByteArray(); + + signingEngine.initSign(signingKey); + signingEngine.update(content); + signature = signingEngine.sign(); + } + + /** + * Retrieves the encapsulated object. The encapsulated object is de-serialized + * before it is returned. + * + * @return the encapsulated object. + * @throws IOException if an error occurs during de-serialization. + * @throws ClassNotFoundException if an error occurs during de-serialization. + */ + public Object getObject() throws IOException, ClassNotFoundException + { + ByteArrayInputStream bais = new ByteArrayInputStream(content); + ObjectInput oi = new ObjectInputStream(bais); + Object obj = oi.readObject(); + oi.close(); + bais.close(); + + return obj; + } + + /** + * Retrieves the signature on the signed object, in the form of a byte array. + * + * @return a copy of the signature. + */ + public byte[] getSignature() + { + return (byte[]) signature.clone(); + + } + + /** + * Retrieves the name of the signature algorithm. + * + * @return the signature algorithm name. + */ + public String getAlgorithm() + { + return thealgorithm; + } + + /** + * Verifies that the signature in this <code>SignedObject</code> is the valid + * signature for the object stored inside, with the given verification key, + * using the designated verification engine. + * + * @param verificationKey the public key for verification. + * @param verificationEngine the signature verification engine. + * @return <code>true</code> if the signature is valid, <code>false</code> + * otherwise. + * @throws SignatureException if signature verification failed. + * @throws InvalidKeyException if the verification key is invalid. + */ + public boolean verify(PublicKey verificationKey, Signature verificationEngine) + throws InvalidKeyException, SignatureException + { + verificationEngine.initVerify(verificationKey); + verificationEngine.update(content); + return verificationEngine.verify(signature); + } + + /** Called to restore the state of the SignedObject from a stream. */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + content = (byte[]) content.clone(); + signature = (byte[]) signature.clone(); + } +} diff --git a/libjava/classpath/java/security/Signer.java b/libjava/classpath/java/security/Signer.java new file mode 100644 index 00000000000..ae1463db84c --- /dev/null +++ b/libjava/classpath/java/security/Signer.java @@ -0,0 +1,164 @@ +/* Signer.java --- Signer Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * <p>This class is used to represent an {@link Identity} that can also + * digitally sign data.</p> + * + * <p>The management of a signer's private keys is an important and sensitive + * issue that should be handled by subclasses as appropriate to their intended + * use.</p> + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @deprecated This class is no longer used. Its functionality has been replaced + * by <code>java.security.KeyStore</code>, the <code>java.security.cert</code> + * package, and <code>java.security.Principal</code>. + */ +public abstract class Signer extends Identity +{ + private static final long serialVersionUID = -1763464102261361480L; + private PrivateKey privateKey = null; + + /** + * Creates a <code>Signer</code>. This constructor should only be used for + * serialization. + */ + protected Signer() + { + } + + /** + * Creates a <code>Signer</code> with the specified identity name. + * + * @param name the identity name. + */ + public Signer(String name) + { + super(name); + } + + /** + * Creates a <code>Signer</code> with the specified identity name and scope. + * + * @param name the identity name. + * @param scope the scope of the identity. + * @throws KeyManagementException if there is already an identity with the + * same name in the scope. + */ + public Signer(String name, IdentityScope scope) throws KeyManagementException + { + super(name, scope); + } + + /** + * <p>Returns this signer's private key.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"getSignerPrivateKey"</code> as its + * argument to see if it's ok to return the private key.</p> + * + * @return this signer's private key, or <code>null</code> if the private key + * has not yet been set. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow returning the + * private key. + * @see SecurityManager#checkSecurityAccess(String) + */ + public PrivateKey getPrivateKey() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("getSignerPrivateKey"); + + return privateKey; + } + + /** + * <p>Sets the key pair (public key and private key) for this signer.</p> + * + * <p>First, if there is a security manager, its <code>checkSecurityAccess() + * </code> method is called with <code>"setSignerKeyPair"</code> as its + * argument to see if it's ok to set the key pair.</p> + * + * @param pair an initialized key pair. + * @throws InvalidParameterException if the key pair is not properly + * initialized. + * @throws KeyException if the key pair cannot be set for any other reason. + * @throws SecurityException if a security manager exists and its + * <code>checkSecurityAccess()</code> method doesn't allow setting the key + * pair. + * @see SecurityManager#checkSecurityAccess(String) + */ + public final void setKeyPair(KeyPair pair) + throws InvalidParameterException, KeyException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setSignerKeyPair"); + + try + { + if (pair.getPublic() != null) + setPublicKey(pair.getPublic()); + else + throw new InvalidParameterException(); + + } + catch (KeyManagementException kme) + { + throw new KeyException(); + } + + if (pair.getPrivate() != null) + privateKey = pair.getPrivate(); + else + throw new InvalidParameterException(); + } + + /** + * Returns a string of information about the signer. + * + * @return a string of information about the signer. + * @see SecurityManager#checkSecurityAccess(String) + */ + public String toString() + { + return (getName() + ": " + privateKey); + } +} diff --git a/libjava/classpath/java/security/UnrecoverableKeyException.java b/libjava/classpath/java/security/UnrecoverableKeyException.java new file mode 100644 index 00000000000..6759c3c7b91 --- /dev/null +++ b/libjava/classpath/java/security/UnrecoverableKeyException.java @@ -0,0 +1,71 @@ +/* UnrecoverableKeyException.java -- Cannot recover a key from the key store + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when a key cannot be recovered from the key + * store. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class UnrecoverableKeyException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7275063078190151277L; + + /** + * Create an instance with no descriptive error message. + */ + public UnrecoverableKeyException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public UnrecoverableKeyException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/UnresolvedPermission.java b/libjava/classpath/java/security/UnresolvedPermission.java new file mode 100644 index 00000000000..d3f671a9c06 --- /dev/null +++ b/libjava/classpath/java/security/UnresolvedPermission.java @@ -0,0 +1,304 @@ +/* UnresolvedPermission.java -- Placeholder for unresolved permissions + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +// All uses of Certificate in this file refer to the one in the listed +// package, not this one. +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; +import java.util.Vector; + +/** + * This class is used to hold instances of all permissions that cannot + * be resolved to available permission classes when the security + * <code>Policy</code> object is instantiated. This may happen when the + * necessary security class has not yet been downloaded from the network. + * + * <p>Instances of this class are re-resolved when + * <code>AccessController</code> check is done. At that time, a scan is + * made of all existing <code>UnresolvedPermission</code> objects and they + * are converted to objects of the appropriate permission type if the class + * for that type is then available. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permission + * @see Permissions + * @see PermissionCollection + * @see Policy + * @since 1.1 + * @status updated to 1.4 + */ +public final class UnresolvedPermission extends Permission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4821973115467008846L; + + /** + * The list of actions associated with this permission object. + * + * @serial the permission actions + */ + private final String actions; + + /** + * The list of <code>Certificates</code> associated with this object. + */ + private final transient Certificate[] certs; + + /** + * The name of the class this object should be resolved to. + * + * @serial the fully-qualified classname of the resolved type + */ + // Package visible for use by UnresolvedPermissionCollection. + final String type; + + /** + * The name of the permission. + * + * @serial the permission name + */ + private final String name; + + /** + * Create a new instance with all the information necessary to resolve it + * to an instance of the proper class at a future time. + * + * @param type the fully-qualified name of the class of this permission + * @param name the name of this permission + * @param actions the action list for this permission + * @param certs the list of certificates that sign this permission + */ + public UnresolvedPermission(String type, String name, String actions, + Certificate[] certs) + { + super(name); + this.name = name; + this.type = type; + this.actions = actions; + this.certs = certs; + } + + /** + * This method returns <code>false</code> always to indicate that this + * permission does not imply the specified permission. An + * <code>UnresolvedPermission</code> never grants any permissions. + * + * @param perm the <code>Permission</code> object to test + * @return false; until a permission is resolved, it implies nothing + */ + public boolean implies(Permission perm) + { + return false; + } + + /** + * This method tests this permission for equality against the specified + * <code>Object</code>. This will be true if and only if the following + * conditions are met:<ul> + * <li>The specified <code>Object</code> is an UnresolvedPermission</li> + * <li>The specified permission has the same type (i.e., desired class name) + * as this permission.</li> + * <li>The specified permission has the same name as this one.</li> + * <li>The specified permissoin has the same action list as this one.</li> + * <li>The specified permission has the same certificate list as this + * one.</li> + * </ul> + * + * @param obj the <code>Object</code> to test for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof UnresolvedPermission)) + return (false); + UnresolvedPermission up = (UnresolvedPermission) obj; + return up.name.equals(name) && up.actions.equals(actions) + && up.type.equals(type) && Arrays.equals(up.certs, certs); + } + + /** + * Returns a hash code value for this object. Following the lead of + * Permission, this returns the hashcode of the permission name. + * + * @return A hash value + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * This method returns the list of actions associated with this + * permission. + * + * @return the action list + */ + public String getActions() + { + return actions; + } + + /** + * This method returns a <code>String</code> representation of this + * class. The format is: '(unresolved "ClassName "name" "actions")' + * + * @return <code>String</code> representation of this object + */ + public String toString() + { + return "(unresolved " + type + ' ' + name + ' ' + actions + ')'; + } + + /** + * This class returns a <code>PermissionCollection</code> object that can + * be used to store instances of <code>UnresolvedPermission</code>. + * + * @return a new <code>PermissionCollection</code> + */ + public PermissionCollection newPermissionCollection() + { + return new UnresolvedPermissionCollection(); + } +} // class UnresolvedPermission + +/** + * Implements the permission collection for unresolved permissions, and + * obeys serialization of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class UnresolvedPermissionCollection extends PermissionCollection +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7176153071733132400L; + + // Package-private to avoid a trampoline. + /** + * Hashtable where we store permissions. + * + * @serial map of typename to a Vector of permissions (you'd think Sun + * would document this better!) + */ + final Hashtable permissions = new Hashtable(); + + /** + * Add a permission. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not an UnresolvedPermission + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException(); + if (! (perm instanceof UnresolvedPermission)) + throw new IllegalArgumentException(); + UnresolvedPermission up = (UnresolvedPermission) perm; + Vector v = (Vector) permissions.get(up.type); + if (v == null) + { + v = new Vector(); + permissions.put(up.type, v); + } + v.add(up); + } + + /** + * Returns true if perm is implied by the collection. + * + * @param perm the permission to check + * @return false; unresolved permissions imply nothing + */ + public boolean implies(Permission perm) + { + return false; + } + + /** + * Return the elements. + * + * @return the elements + */ + public Enumeration elements() + { + return new Enumeration() + { + Enumeration main_enum = permissions.elements(); + Enumeration sub_enum; + + public boolean hasMoreElements() + { + if (sub_enum == null) + { + if (main_enum == null) + return false; + if (! main_enum.hasMoreElements()) + { + main_enum = null; + return false; + } + Vector v = (Vector) main_enum.nextElement(); + sub_enum = v.elements(); + } + if (! sub_enum.hasMoreElements()) + { + sub_enum = null; + return hasMoreElements(); + } + return true; + } + + public Object nextElement() + { + if (! hasMoreElements()) + throw new NoSuchElementException(); + return sub_enum.nextElement(); + } + }; + } +} // class UnresolvedPermissionCollection diff --git a/libjava/classpath/java/security/acl/Acl.java b/libjava/classpath/java/security/acl/Acl.java new file mode 100644 index 00000000000..ff139afd6c0 --- /dev/null +++ b/libjava/classpath/java/security/acl/Acl.java @@ -0,0 +1,153 @@ +/* Acl.java -- An access control list + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * A Java access control list (ACL) is a group of individual ACL entries. + * These entries consist of a <code>Principal</code> and a list of + * permissions this <code>Principal</code> is either granted or denied. + * A given <code>Principal</code> can have at most one positive ACL entry + * (i.e., one that grants permissions) and one negative ACL entry (i.e., one + * that denies permissions). If a given permission is both granted and + * denied, the ACL treats it as if it were never granted or denied. If + * both a <code>Principal</code> and a <code>Group</code> to which the + * <code>Principal</code> belongs have an ACL entry, the permissions for + * the individual <code>Principal</code> take precedence over the + * permissions of the <code>Group</code> if there is a conflict. + * <p> + * Additionally, the ACL interface extends the <code>Owner</code> interface + * and so an ACL has owners. Actions which modify the ACL are restricted + * to owners. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Acl extends Owner +{ + + /** + * This method returns the name of this ACL. + * + * @return The name of this ACL + */ + String getName(); + + /** + * This method sets the name of the ACL + * + * @param caller The <code>Principal</code> requesting the action. + * @param name The new name for this ACL. + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + void setName(Principal caller, String name) + throws NotOwnerException; + + /** + * This method adds the specified entry to the ACL + * + * @param caller The <code>Principal</code> requesting the addition + * @param entry The ACL entry to add + * + * @return <code>true</code> if the entry was added, <code>false</code> + * if there is already an entry of the same type for the + * <code>Principal</code>. + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + boolean addEntry(Principal caller, AclEntry entry) + throws NotOwnerException; + + /** + * This method delets the specified entry from the ACL + * + * @param caller The <code>Principal</code> requesting the deletion. + * @param entry The ACL entry to delete + * + * @return <code>true</code> if the entry was deleted, or <code>false</code> + * if this entry was not part of the ACL to begin with + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + boolean removeEntry(Principal caller, AclEntry entry) + throws NotOwnerException; + + /** + * This method returns a list of all the entries in the ACL as an + * <code>Enumeration</code>. + * + * @return An enumeration of the ACL entries + */ + Enumeration entries(); + + /** + * This method tests whether or not the specified <code>Principal</code> + * has the specified <code>Permission</code> + * + * @param user The <code>Principal</code> to test + * @param perm The <code>Permission</code> to test for + * + * @return <code>true</code> if the user has been granted the permission, + * <code>false</code> otherwise + */ + boolean checkPermission(Principal user, Permission perm); + + /** + * This method returns a list of <code>Permission</code>'s that are granted + * to a particular <code>Principal</code>. This includes any permissions + * that are granted to <code>Group</code>'s to which the <code>Principal</code> + * belongs unless they are overridden by a negative ACL. This permission + * list is returned as an <code>Enumeration</code>. + * + * @param user The <code>Principal</code> to retrieve permissions for. + * + * @return A list of permissions for the <code>Principal</code>. + */ + Enumeration getPermissions(Principal user); + + /** + * This method returns the ACL as a <code>String</code> + * + * @return A <code>String</code> representation of this ACL + */ + String toString(); +} diff --git a/libjava/classpath/java/security/acl/AclEntry.java b/libjava/classpath/java/security/acl/AclEntry.java new file mode 100644 index 00000000000..7b1bcf54db9 --- /dev/null +++ b/libjava/classpath/java/security/acl/AclEntry.java @@ -0,0 +1,143 @@ +/* AclEntry.java -- An entry in an ACL list. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * This interface models an entry in an access control list (ACL). Java + * ACL's consist of a list of entries, where each consists of a + * <code>Principal</code> and a list of <code>Permission</code>'s which + * have been granted to that <code>Principal</code>. An ACL can also + * be <em>negative</em>, which indicates that the list of + * <code>Permission</code>'s is a list of permissions that are <em>not</em> + * granted to the <code>Principal</code>. A <code>Principal</code> can + * have at most one regular (or positive) ACL entry and one negative + * ACL entry. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface AclEntry extends Cloneable +{ + /** + * This method returns the <code>Principal</code> associated with this + * ACL entry. + * + * @return The <code>Principal</code> for this ACL entry + */ + Principal getPrincipal(); + + /** + * This method sets ths <code>Principal</code> associated with this + * ACL entry. This operation will only succeed if there is not already + * a <code>Principal</code> assigned. + * + * @param user The <code>Principal</code> for this ACL entry + * + * @return <code>true</code> if the <code>Principal</code> was successfully set or <code>false</code> if this entry already has a <code>Principal</code>. + */ + boolean setPrincipal(Principal user); + + /** + * This method sets this ACL entry to be a <em>negative</em> entry, indicating + * that it contains a list of permissions that are <em>not</em> granted + * to the entry's <code>Principal</code>. Note that there is no way to + * undo this operation. + */ + void setNegativePermissions(); + + /** + * This method tests whether or not this ACL entry is a negative entry or not. + * + * @return <code>true</code> if this ACL entry is negative, <code>false</code> otherwise + */ + boolean isNegative(); + + /** + * This method adds the specified permission to this ACL entry. + * + * @param perm The <code>Permission</code> to add + * + * @return <code>true</code> if the permission was added or <code>false</code> if it was already set for this entry + */ + boolean addPermission(Permission permission); + + /** + * This method deletes the specified permission to this ACL entry. + * + * @param perm The <code>Permission</code> to delete from this ACL entry. + * + * @return <code>true</code> if the permission was successfully deleted or <code>false</code> if the permission was not part of this ACL to begin with + */ + boolean removePermission(Permission perm); + + /** + * This method tests whether or not the specified permission is associated + * with this ACL entry. + * + * @param perm The <code>Permission</code> to test + * + * @return <code>true</code> if this permission is associated with this entry or <code>false</code> otherwise + */ + boolean checkPermission(Permission permission); + + /** + * This method returns a list of all <code>Permission</code> objects + * associated with this ACL entry as an <code>Enumeration</code>. + * + * @return A list of permissions for this ACL entry + */ + Enumeration permissions(); + + /** + * This method returns this object as a <code>String</code>. + * + * @return A <code>String</code> representation of this object + */ + String toString(); + + /** + * This method returns a clone of this ACL entry + * + * @return A clone of this ACL entry + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/acl/AclNotFoundException.java b/libjava/classpath/java/security/acl/AclNotFoundException.java new file mode 100644 index 00000000000..9a16d9c50b0 --- /dev/null +++ b/libjava/classpath/java/security/acl/AclNotFoundException.java @@ -0,0 +1,60 @@ +/* AclNotFoundException.java -- thrown when an ACL is not found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown when a requested access control list (ACL) is + * not found. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AclNotFoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5684295034092681791L; + + /** + * Initializes a new instance of this class with no descriptive message + */ + public AclNotFoundException() + { + } +} diff --git a/libjava/classpath/java/security/acl/Group.java b/libjava/classpath/java/security/acl/Group.java new file mode 100644 index 00000000000..3ffdf15a4ce --- /dev/null +++ b/libjava/classpath/java/security/acl/Group.java @@ -0,0 +1,90 @@ +/* Group.java -- Represents a group of Principals + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * This interface represents a group of <code>Principals</code>. Note that + * since this interface extends <code>Principal</code>, a <code>Group</code> + * can be used where ever a <code>Principal</code> is requested. This + * includes arguments to the methods in this interface. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Group extends Principal +{ + /** + * This method adds a new <code>Principal</code> to this group. + * + * @param user The new <code>Principal</code> to add + * + * @return <code>true</code> if the user was successfully added or <code>false</code> if the user is already a member + */ + boolean addMember(Principal user); + + /** + * This method deletes a member from the group. + * + * @param user The <code>Principal</code> to delete + * + * @return <code>true</code> if the user was successfully deleted or <code>false</code> if the user is not a member of the group + */ + boolean removeMember(Principal user); + + /** + * This method tests whether or not a given <code>Principal</code> is a + * member of this group. + * + * @param user The <code>Principal</code> to test for membership + * + * @return <code>true</code> if the user is member, <code>false</code> otherwise + */ + boolean isMember(Principal member); + + /** + * This method returns a list of all members of the group as an + * <code>Enumeration</code>. + * + * @return The list of all members of the group + */ + Enumeration members(); +} diff --git a/libjava/classpath/java/security/acl/LastOwnerException.java b/libjava/classpath/java/security/acl/LastOwnerException.java new file mode 100644 index 00000000000..95272445997 --- /dev/null +++ b/libjava/classpath/java/security/acl/LastOwnerException.java @@ -0,0 +1,62 @@ +/* LastOwnerException.java -- User attempted to delete last ACL owner + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown when an attempt is made to delete the last owner + * of an access control list (ACL) + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Owner#deleteOwner(java.security.Principal, java.security.Principal) + * @status updated to 1.4 + */ +public class LastOwnerException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5141997548211140359L; + + /** + * Initialize a new instance of <code>LastOwnerException</code> that does + * not have a log message. + */ + public LastOwnerException() + { + } +} diff --git a/libjava/classpath/java/security/acl/NotOwnerException.java b/libjava/classpath/java/security/acl/NotOwnerException.java new file mode 100644 index 00000000000..bea94763e4b --- /dev/null +++ b/libjava/classpath/java/security/acl/NotOwnerException.java @@ -0,0 +1,62 @@ +/* NotOwnerException.java -- Attempt to modify an unowned ACL + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown whenever an operation is attempted that requires + * the caller to be the owner of the access control list (ACL) when the caller + * is in fact not the owner of the ACL. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NotOwnerException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5555597911163362399L; + + /** + * Initializes a new instance of <code>NotOwnerException</code> that does + * not have a descriptive message. + */ + public NotOwnerException() + { + } +} diff --git a/libjava/classpath/java/security/acl/Owner.java b/libjava/classpath/java/security/acl/Owner.java new file mode 100644 index 00000000000..df1605b2e4f --- /dev/null +++ b/libjava/classpath/java/security/acl/Owner.java @@ -0,0 +1,95 @@ +/* Owner.java -- ACL owner + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; + +/** + * This interface provides a mechanism for maintaining a list of owners + * of an access control list (ACL). Since a <code>Principal</code> must + * be an owner in order to modify the owner list, a mechanism must be + * provided to specify the initial owner of the ACL. The proper way to do + * this is for the implementing class to specify the initial owner in + * the contructor for that class. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Owner +{ + /** + * This method adds an owner to the access control list (ACL). Only a + * <code>Principal</code> who is already an owner can perform this operation. + * + * @param caller The <code>Principal</code> who is requesting that an owner be added + * @param owner The <code>Principal</code> to add as a new owner + * + * @param <code>true</code> if the new owner was successfully added or <code>false</code> if the specified new owner is already an owner + * + * @exception NotOwnerException If the caller is not already an owner of this ACL + */ + boolean addOwner(Principal caller, Principal owner) + throws NotOwnerException; + + /** + * This method delets an owner from the access control list (ACL). Only a + * <code>Principal</code> who is an owner can perform this operation. An + * owner can delete itself from the list. If there is only one + * owner remaining on this list, any attempt to delete it will throw an + * exception. + * + * @param caller The <code>Principal</code> who is requesting that an owner be deleted + * @param owner The <code>Principal</code> to delete as an owner + * + * @param <code>true</code> if the new owner was successfully deleted or <code>false</code> if the specified owner is not currently an owner + * + * @exception NotOwnerException If the caller is not already an owner of this ACL + * @exception LastOwnerException If completing the operation would delete the last ACL owner + */ + boolean deleteOwner(Principal caller, Principal owner) + throws NotOwnerException, LastOwnerException; + + /** + * This method tests whether or not a given <code>Principal</code> is an + * owner of this access control list (ACL). + * + * @return <code>true</code> if the <code>Principal</code> is an owner, <code>false</code> otherwise + */ + boolean isOwner(Principal owner); +} diff --git a/libjava/classpath/java/security/acl/Permission.java b/libjava/classpath/java/security/acl/Permission.java new file mode 100644 index 00000000000..e5ba2913890 --- /dev/null +++ b/libjava/classpath/java/security/acl/Permission.java @@ -0,0 +1,67 @@ +/* Permission.java -- Information about an ACL permission + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This interface provides information about a permission that can be + * granted. Note that this is <em>not</em> the same as the class + * <code>java.security.Permission</code>. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Permission +{ + /** + * This method tests whether or not a specified <code>Permission</code> + * (passed as an <code>Object</code>) is the same as this permission. + * + * @param perm The permission to check for equality + * + * @return <code>true</code> if the specified permission is the same as this one, <code>false</code> otherwise + */ + boolean equals (Object perm); + + /** + * This method returns this <code>Permission</code> as a <code>String</code>. + * + * @return A <code>String</code> representing this permission. + */ + String toString(); +} diff --git a/libjava/classpath/java/security/acl/package.html b/libjava/classpath/java/security/acl/package.html new file mode 100644 index 00000000000..19facf190b2 --- /dev/null +++ b/libjava/classpath/java/security/acl/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.security.acl package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.security.acl</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/security/cert/CRL.java b/libjava/classpath/java/security/cert/CRL.java new file mode 100644 index 00000000000..e763663f52c --- /dev/null +++ b/libjava/classpath/java/security/cert/CRL.java @@ -0,0 +1,98 @@ +/* CRL.java --- Certificate Revocation List + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + Certificate Revocation List class for managing CRLs that + have different formats but the same general use. They + all serve as lists of revoked certificates and can + be queried for a given certificate. + + Specialized CRLs extend this class. + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public abstract class CRL +{ + + private String type; + + /** + Creates a new CRL for the specified type. An example + is "X.509". + + @param type the standard name for the CRL type. + */ + protected CRL(String type) + { + this.type = type; + } + + /** + Returns the CRL type. + + @return a string representing the CRL type + */ + public final String getType() + { + return type; + } + + /** + Returns a string representing the CRL. + + @return a string representing the CRL. + */ + public abstract String toString(); + + /** + Determines whether or not the specified Certificate + is revoked. + + @param cert A certificate to check if it is revoked + + @return true if the certificate is revoked, + false otherwise. + */ + public abstract boolean isRevoked(Certificate cert); + + +} diff --git a/libjava/classpath/java/security/cert/CRLException.java b/libjava/classpath/java/security/cert/CRLException.java new file mode 100644 index 00000000000..f3addfe9594 --- /dev/null +++ b/libjava/classpath/java/security/cert/CRLException.java @@ -0,0 +1,73 @@ +/* CRLException.java -- Certificate Revocation List Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.GeneralSecurityException; + +/** + * Exception for a Certificate Revocation List. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 +*/ +public class CRLException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -6694728944094197147L; + + /** + * Constructs an CRLExceptionwithout a message string. + */ + public CRLException() + { + } + + /** + * Constructs an CRLException with a message string. + * + * @param msg a message to display with exception + */ + public CRLException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CRLSelector.java b/libjava/classpath/java/security/cert/CRLSelector.java new file mode 100644 index 00000000000..1fa5a207dfc --- /dev/null +++ b/libjava/classpath/java/security/cert/CRLSelector.java @@ -0,0 +1,69 @@ +/* CRLSelector.java -- matches CRLs against criteria. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * A generic interface to classes that match certificate revocation + * lists (CRLs) to some given criteria. Implementations of this + * interface are useful for finding {@link CRL} objects in a {@link + * CertStore}. + * + * @see CertStore + * @see CertSelector + * @see X509CRLSelector + */ +public interface CRLSelector extends Cloneable +{ + + /** + * Returns a clone of this instance. + * + * @return The clone. + */ + Object clone(); + + /** + * Match a given certificate revocation list to this selector's + * criteria, returning true if it matches, false otherwise. + * + * @param crl The certificate revocation list to test. + * @return The boolean result of this test. + */ + boolean match(CRL crl); +} diff --git a/libjava/classpath/java/security/cert/CertPath.java b/libjava/classpath/java/security/cert/CertPath.java new file mode 100644 index 00000000000..e818763aab4 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPath.java @@ -0,0 +1,252 @@ +/* CertPath.java -- a sequence of certificates + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +import java.io.ByteArrayInputStream; +import java.io.NotSerializableException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; + +/** + * This class represents an immutable sequence, or path, of security + * certificates. The path type must match the type of each certificate in the + * path, or in other words, for all instances of cert in a certpath object, + * <code>cert.getType().equals(certpath.getType())</code> will return true. + * + * <p>Since this class is immutable, it is thread-safe. During serialization, + * the path is consolidated into a {@link CertPathRep}, which preserves the + * data regardless of the underlying implementation of the path. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class CertPath implements Serializable +{ + /** + * The serialized representation of a path. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + protected static class CertPathRep implements Serializable + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 3015633072427920915L; + + /** + * The certificate type. + * + * @serial the type of the certificate path + */ + private final String type; + + /** + * The encoded form of the path. + * + * @serial the encoded form + */ + private final byte[] data; + + /** + * Create the new serial representation. + * + * @param type the path type + * @param data the encoded path data + */ + protected CertPathRep(String type, byte[] data) + { + this.type = type; + this.data = data; + } + + /** + * Decode the data into an actual {@link CertPath} upon deserialization. + * + * @return the replacement object + * @throws ObjectStreamException if replacement fails + */ + protected Object readResolve() throws ObjectStreamException + { + try + { + return CertificateFactory.getInstance(type) + .generateCertPath(new ByteArrayInputStream(data)); + } + catch (CertificateException e) + { + throw (ObjectStreamException) + new NotSerializableException("java.security.cert.CertPath: " + + type).initCause(e); + } + } + } // class CertPathRep + + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6068470306649138683L; + + /** + * The path type. + * + * @serial the type of all certificates in this path + */ + private final String type; + + /** + * Create a certificate path with the given type. Most code should use + * {@link CertificateFactory} to create CertPaths. + * + * @param type the type of the path + */ + protected CertPath(String type) + { + this.type = type; + } + + /** + * Get the (non-null) type of all certificates in the path. + * + * @return the path certificate type + */ + public String getType() + { + return type; + } + + /** + * Get an immutable iterator over the path encodings (all String names), + * starting with the default encoding. The iterator will throw an + * <code>UnsupportedOperationException</code> if an attempt is made to + * remove items from the list. + * + * @return the iterator of supported encodings in the path + */ + public abstract Iterator getEncodings(); + + /** + * Compares this path to another for semantic equality. To be equal, both + * must be instances of CertPath, with the same type, and identical + * certificate lists. Overriding classes must not change this behavior. + * + * @param o the object to compare to + * @return true if the two are equal + */ + public boolean equals(Object o) + { + if (! (o instanceof CertPath)) + return false; + CertPath cp = (CertPath) o; + return type.equals(cp.type) + && getCertificates().equals(cp.getCertificates()); + } + + /** + * Returns the hashcode of this certificate path. This is defined as:<br> + * <code>31 * getType().hashCode() + getCertificates().hashCode()</code>. + * + * @return the hashcode + */ + public int hashCode() + { + return 31 * type.hashCode() + getCertificates().hashCode(); + } + + public String toString() + { + List l = getCertificates(); + int size = l.size(); + int i = 0; + StringBuffer result = new StringBuffer(type); + result.append(" Cert Path: length = ").append(size).append(".\n[\n"); + while (--size >= 0) + result.append(l.get(i++)).append('\n'); + return result.append("\n]").toString(); + } + + /** + * Returns the encoded form of this path, via the default encoding. + * + * @return the encoded form + * @throws CertificateEncodingException if encoding fails + */ + public abstract byte[] getEncoded() throws CertificateEncodingException; + + /** + * Returns the encoded form of this path, via the specified encoding. + * + * @param encoding the encoding to use + * @return the encoded form + * @throws CertificateEncodingException if encoding fails or does not exist + */ + public abstract byte[] getEncoded(String encoding) + throws CertificateEncodingException; + + /** + * Returns the immutable, thread-safe list of certificates in this path. + * + * @return the list of certificates, non-null but possibly empty + */ + public abstract List getCertificates(); + + /** + * Serializes the path in its encoded form, to ensure reserialization with + * the appropriate factory object without worrying about list implementation. + * The result will always be an instance of {@link CertPathRep}. + * + * @return the replacement object + * @throws ObjectStreamException if the replacement creation fails + */ + protected Object writeReplace() throws ObjectStreamException + { + try + { + return new CertPathRep(type, getEncoded()); + } + catch (CertificateEncodingException e) + { + throw (ObjectStreamException) + new NotSerializableException("java.security.cert.CertPath: " + + type).initCause(e); + } + } +} // class CertPath diff --git a/libjava/classpath/java/security/cert/CertPathBuilder.java b/libjava/classpath/java/security/cert/CertPathBuilder.java new file mode 100644 index 00000000000..f6965205f53 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilder.java @@ -0,0 +1,238 @@ +/* CertPathBuilder.java -- bulids CertPath objects from Certificates. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.Engine; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; + +/** + * This class builds certificate paths (also called certificate chains), + * which can be used to establish trust for a particular certificate by + * building a path from a trusted certificate (a trust anchor) to the + * untrusted certificate. + * + * @see CertPath + */ +public class CertPathBuilder +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertPathBuilder. */ + private static final String CERT_PATH_BUILDER = "CertPathBuilder"; + + /** The underlying implementation. */ + private CertPathBuilderSpi cpbSpi; + + /** The provider of this implementation. */ + private Provider provider; + + /** The name of this implementation. */ + private String algorithm; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathBuilder. + * + * @param cpbSpi The underlying implementation. + * @param provider The provider of the implementation. + * @param algorithm This implementation's name. + */ + protected CertPathBuilder(CertPathBuilderSpi cpbSpi, Provider provider, + String algorithm) + { + this.cpbSpi = cpbSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Get the default cert path builder type. + * + * <p>This value can be set at run-time by the security property + * <code>"certpathbuilder.type"</code>. If this property is not set, + * then the value returned is <code>"PKIX"</code>. + * + * @return The default CertPathBuilder algorithm. + */ + public static final String getDefaultType() + { + String type = Security.getProperty("certpathbuilder.type"); + if (type == null) + type = "PKIX"; + return type; + } + + /** + * Get an instance of a named CertPathBuilder, from the first provider + * that implements it. + * + * @param algorithm The name of the CertPathBuilder to create. + * @return The new instance. + * @throws NoSuchAlgorithmException If no installed provider + * implements the named algorithm. + */ + public static CertPathBuilder getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignored. + } + } + + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Get an instance of a named CertPathBuilder from the named + * provider. + * + * @param algorithm The name of the CertPathBuilder to create. + * @param provider The name of the provider from which to get the + * implementation. + * @return The new instance. + * @throws NoSuchAlgorithmException If no installed provider + * implements the named algorithm. + * @throws NoSuchProviderException If the named provider does not + * exist. + */ + public static CertPathBuilder getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + return getInstance(algorithm, p); + } + + /** + * Get an instance of a named CertPathBuilder from the specified + * provider. + * + * @param algorithm The name of the CertPathBuilder to create. + * @param provider The provider from which to get the implementation. + * @return The new instance. + * @throws NoSuchAlgorithmException If no installed provider + * implements the named algorithm. + * @throws IllegalArgumentException If <i>provider</i> in + * <tt>null</tt>. + */ + public static CertPathBuilder getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("null provider"); + try + { + return new CertPathBuilder((CertPathBuilderSpi) + Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider), + provider, algorithm); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the name of this CertPathBuilder algorithm. + * + * @return The algorithm name. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Return the provider of this instance's implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Builds a certificate path. The {@link CertPathParameters} parameter + * passed to this method is implementation-specific, but in general + * should contain some number of certificates and some number of + * trusted certificates (or "trust anchors"). + * + * @param params The parameters. + * @retrun The certificate path result. + * @throws CertPathBuilderException If the certificate path cannot be + * built. + * @throws InvalidAlgorithmParameterException If the implementation + * rejects the specified parameters. + */ + public final CertPathBuilderResult build(CertPathParameters params) + throws CertPathBuilderException, InvalidAlgorithmParameterException + { + return cpbSpi.engineBuild(params); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderException.java b/libjava/classpath/java/security/cert/CertPathBuilderException.java new file mode 100644 index 00000000000..98515101025 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderException.java @@ -0,0 +1,159 @@ +/* CertPathBuilderException.java -- wraps an exception during certificate + path building + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while using a <code>CertPathBuilder</code>, wrapping + * the lower exception. This class is not thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertPathBuilder + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertPathBuilderException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 5316471420178794402L; + + /** + * Create an exception without a message. The cause may be initialized. + */ + public CertPathBuilderException() + { + } + + /** + * Create an exception with a message. The cause may be initialized. + * + * @param msg a message to display with exception + */ + public CertPathBuilderException(String msg) + { + super(msg); + } + + /** + * Create an exception with a cause. The message will be + * <code>cause == null ? null : cause.toString()</code>. + * + * @param cause the cause + */ + public CertPathBuilderException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Create an exception with a cause and a message. + * + * @param msg the message + * @param cause the cause + */ + public CertPathBuilderException(String msg, Throwable cause) + { + super(msg); + initCause(cause); + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to <code>System.err</code>. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderResult.java b/libjava/classpath/java/security/cert/CertPathBuilderResult.java new file mode 100644 index 00000000000..737ba9431b3 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderResult.java @@ -0,0 +1,63 @@ +/* CertPathBuilderResult -- results from building cert paths. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * A standard interface for the result of building a certificate path. + * All implementations of this class must provide a way to get the + * certificate path, but may also define additional methods for + * returning other result data generated by the certificate path + * builder. + */ +public interface CertPathBuilderResult extends Cloneable { + + /** + * Creates a copy of this builder result. + * + * @return The copy. + */ + Object clone(); + + /** + * Get the certificate path that was built. + * + * @retrn The certificate path. + */ + CertPath getCertPath(); +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderSpi.java b/libjava/classpath/java/security/cert/CertPathBuilderSpi.java new file mode 100644 index 00000000000..afc7fc07366 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderSpi.java @@ -0,0 +1,74 @@ +/* CertPathBuilderSpi -- CertPathBuilder service provider interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +/** + * The {@link CertPathBuilder} <i>Service Provider Interface</i> + * (<b>SPI</b>). + * + * @see CertPathBuilder + */ +public abstract class CertPathBuilderSpi { + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathBuilderSpi. + */ + public CertPathBuilderSpi() { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Creates a certificate path from the specified parameters. + * + * @param params The parameters to use. + * @return The certificate path result. + * @throws CertPathBuilderException If the certificate path cannot be + * built. + * @throws java.security.InvalidAlgorithmParameterException If the + * implementation rejects the specified parameters. + */ + public abstract CertPathBuilderResult engineBuild(CertPathParameters params) + throws CertPathBuilderException, + java.security.InvalidAlgorithmParameterException; +} diff --git a/libjava/classpath/java/security/cert/CertPathParameters.java b/libjava/classpath/java/security/cert/CertPathParameters.java new file mode 100644 index 00000000000..62a5cb6a69e --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathParameters.java @@ -0,0 +1,58 @@ +/* CertPathParameters.java -- parameters for CertPathBuilder. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +/** + * Parameters for generating and validating certificate paths. This + * class does not define any methods (except a required cloneable + * interface) and is provided only to provide type safety for + * implementations. Concrete implementations implement this interface + * in accord with thier own needs. + * + * @see CertPathBuilder + * @see CertPathValidator + */ +public interface CertPathParameters extends Cloneable { + + /** + * Makes a copy of this CertPathParameters instance. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertPathValidator.java b/libjava/classpath/java/security/cert/CertPathValidator.java new file mode 100644 index 00000000000..5fed19e9a78 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidator.java @@ -0,0 +1,249 @@ +/* CertPathValidator -- validates certificate paths. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.Engine; + +import java.security.AccessController; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; + +/** + * Generic interface to classes that validate certificate paths. + * + * <p>Using this class is similar to all the provider-based security + * classes; the method of interest, {@link + * #validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)}, + * which takes provider-specific implementations of {@link + * CertPathParameters}, and return provider-specific implementations of + * {@link CertPathValidatorResult}. + * + * @since JDK 1.4 + * @see CertPath + */ +public class CertPathValidator { + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertPathValidator. */ + private static final String CERT_PATH_VALIDATOR = "CertPathValidator"; + + /** The underlying implementation. */ + private final CertPathValidatorSpi validatorSpi; + + /** The provider of this implementation. */ + private final Provider provider; + + /** The algorithm's name. */ + private final String algorithm; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathValidator. + * + * @param validatorSpi The underlying implementation. + * @param provider The provider of the implementation. + * @param algorithm The algorithm name. + */ + protected CertPathValidator(CertPathValidatorSpi validatorSpi, + Provider provider, String algorithm) + { + this.validatorSpi = validatorSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Returns the default validator type. + * + * <p>This value may be set at run-time via the security property + * "certpathvalidator.type", or the value "PKIX" if this property is + * not set. + * + * @return The default validator type. + */ + public static synchronized String getDefaultType() { + String type = (String) AccessController.doPrivileged( + new PrivilegedAction() + { + public Object run() + { + return Security.getProperty("certpathvalidator.type"); + } + } + ); + if (type == null) + type = "PKIX"; + return type; + } + + /** + * Get an instance of the given validator from the first provider that + * implements it. + * + * @param algorithm The name of the algorithm to get. + * @return The new instance. + * @throws NoSuchAlgorithmException If no installed provider + * implements the requested algorithm. + */ + public static CertPathValidator getInstance(String algorithm) + throws NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignored. + } + } + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Get an instance of the given validator from the named provider. + * + * @param algorithm The name of the algorithm to get. + * @param provider The name of the provider from which to get the + * implementation. + * @return The new instance. + * @throws NoSuchAlgorithmException If the named provider does not + * implement the algorithm. + * @throws NoSuchProviderException If no provider named + * <i>provider</i> is installed. + */ + public static CertPathValidator getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException + { + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + + return getInstance(algorithm, p); + } + + /** + * Get an instance of the given validator from the given provider. + * + * @param algorithm The name of the algorithm to get. + * @param provider The provider from which to get the implementation. + * @return The new instance. + * @throws NoSuchAlgorithmException If the provider does not implement + * the algorithm. + * @throws IllegalArgumentException If <i>provider</i> is null. + */ + public static CertPathValidator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("null provider"); + + try + { + return new CertPathValidator((CertPathValidatorSpi) + Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider), + provider, algorithm); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new NoSuchAlgorithmException(algorithm); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(algorithm); + } + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the name of this validator. + * + * @return This validator's name. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Return the provider of this implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Attempt to validate a certificate path. + * + * @param certPath The path to validate. + * @param params The algorithm-specific parameters. + * @return The result of this validation attempt. + * @throws CertPathValidatorException If the certificate path cannot + * be validated. + * @throws InvalidAlgorithmParameterException If this implementation + * rejects the specified parameters. + */ + public final CertPathValidatorResult validate(CertPath certPath, + CertPathParameters params) + throws CertPathValidatorException, InvalidAlgorithmParameterException + { + return validatorSpi.engineValidate(certPath, params); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorException.java b/libjava/classpath/java/security/cert/CertPathValidatorException.java new file mode 100644 index 00000000000..f3195be296f --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorException.java @@ -0,0 +1,226 @@ +/* CertPathValidatorException.java -- wraps an exception during validation + of a CertPath + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while validating a certification path. In addition, + * it can store the path an index in that path that caused the problem. This + * class is not thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertPathValidator + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertPathValidatorException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -3083180014971893139L; + + /** + * The index of the certificate path that failed, or -1. + * + * @serial the failed index + */ + private final int index; + + /** + * The <code>CertPath</code> that failed. + * + * @serial the object being validated at time of failure + */ + private final CertPath certPath; + + /** + * Create an exception without a message. The cause may be initialized. The + * index is set to -1 and the failed CertPath object to null. + */ + public CertPathValidatorException() + { + this((String) null); + } + + /** + * Create an exception with a message. The cause may be initialized. The + * index is set to -1 and the failed CertPath object to null. + * + * @param msg a message to display with exception + */ + public CertPathValidatorException(String msg) + { + super(msg); + index = -1; + certPath = null; + } + + /** + * Create an exception with a cause. The message will be + * <code>cause == null ? null : cause.toString()</code>. The index is set + * to -1 and the failed CertPath object to null. + * + * @param cause the cause + */ + public CertPathValidatorException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause, null, -1); + } + + /** + * Create an exception with a cause and a message. The index is set to -1 + * and the failed CertPath object to null. + * + * @param msg the message + * @param cause the cause + */ + public CertPathValidatorException(String msg, Throwable cause) + { + this(msg, cause, null, -1); + } + + /** + * Create an exception with a cause, message, failed object, and index of + * failure in that CertPath. + * + * @param msg the message + * @param cause the cause + * @param certPath the path that was being validated, or null + * @param index the index of the path, or -1 + * @throws IndexOutOfBoundsException if index is < -1 or + * > certPath.getCertificates().size() + * @throws IllegalArgumentException if certPath is null but index != -1 + */ + public CertPathValidatorException(String msg, Throwable cause, + CertPath certPath, int index) + { + super(msg); + initCause(cause); + if (index < -1 || (certPath != null + && index >= certPath.getCertificates().size())) + throw new IndexOutOfBoundsException(); + if ((certPath == null) != (index == -1)) + throw new IllegalArgumentException(); + this.certPath = certPath; + this.index = index; + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the certificate path that had the failure, or null. + * + * @return the culprit path + */ + public CertPath getCertPath() + { + return certPath; + } + + /** + * Get the index that failed, or -1. + * + * @return the colprit index + */ + public int getIndex() + { + return index; + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to <code>System.err</code>. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorResult.java b/libjava/classpath/java/security/cert/CertPathValidatorResult.java new file mode 100644 index 00000000000..71aaf89bc18 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorResult.java @@ -0,0 +1,63 @@ +/* CertPathValidatorResult -- result of validating certificate paths + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Interface to the result of calling {@link + * CertPathValidator#validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)}. + * + * <p>This interface defines no methods other than the required + * {@link java.lang.Cloneable} interface, and is intended to group and + * provide type safety for validator results. Providers that implement + * a certificate path validator must also provide an implementation of + * this interface, possibly defining additional methods. + * + * @since JDK 1.4 + * @see CertPathValidator + */ +public interface CertPathValidatorResult extends Cloneable +{ + + /** + * Returns a copy of this validator result. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorSpi.java b/libjava/classpath/java/security/cert/CertPathValidatorSpi.java new file mode 100644 index 00000000000..8d18b49dac4 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorSpi.java @@ -0,0 +1,79 @@ +/* CertPathValidatorSpi -- cert path validator service provider interface + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * The <i>service provider interface</i> (<b>SPI</b>) for the {@link + * CertPathValidator} class. Providers implementing certificate path + * validators must subclass this class and implement its abstract + * methods. + */ +public abstract class CertPathValidatorSpi +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Default constructor. + */ + public CertPathValidatorSpi() + { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Attempt to validate a certificate path. + * + * @param certPath The path to validate. + * @param params The algorithm-specific parameters. + * @return The result of this validation attempt. + * @throws CertPathValidatorException If the certificate path cannot + * be validated. + * @throws InvalidAlgorithmParameterException If this implementation + * rejects the specified parameters. + */ + public abstract CertPathValidatorResult + engineValidate(CertPath certPath, CertPathParameters params) + throws CertPathValidatorException, + java.security.InvalidAlgorithmParameterException; +} diff --git a/libjava/classpath/java/security/cert/CertSelector.java b/libjava/classpath/java/security/cert/CertSelector.java new file mode 100644 index 00000000000..aea614ad95e --- /dev/null +++ b/libjava/classpath/java/security/cert/CertSelector.java @@ -0,0 +1,58 @@ +/* CertSelector.java -- certificate selector interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +public interface CertSelector extends Cloneable +{ + + /** + * Returns a copy of this CertSelector. + * + * @return The copy. + */ + Object clone(); + + /** + * Match a certificate according to this selector's criteria. + * + * @param cert The certificate to match. + * @return true if the certificate matches thin criteria. + */ + boolean match(Certificate cert); +} diff --git a/libjava/classpath/java/security/cert/CertStore.java b/libjava/classpath/java/security/cert/CertStore.java new file mode 100644 index 00000000000..864da868f19 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStore.java @@ -0,0 +1,294 @@ +/* CertStore -- stores and retrieves certificates. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.Engine; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; +import java.util.Collection; + +/** + * A CertStore is a read-only repository for certificates and + * certificate revocation lists. + * + * @since JDK 1.4 + */ +public class CertStore +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertStore. */ + private static final String CERT_STORE = "CertStore"; + + /** The underlying implementation. */ + private CertStoreSpi storeSpi; + + /** This implementation's provider. */ + private Provider provider; + + /** The name of this key store type. */ + private String type; + + /** The parameters used to initialize this instance, if any. */ + private CertStoreParameters params; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new CertStore. + * + * @param storeSpi The underlying implementation. + * @param provider The provider of this implementation. + * @param type The type of CertStore this class represents. + * @param params The parameters used to initialize this instance, if any. + */ + protected CertStore(CertStoreSpi storeSpi, Provider provider, String type, + CertStoreParameters params) + { + this.storeSpi = storeSpi; + this.provider = provider; + this.type = type; + this.params = params; + } + +// Class methods. + // ------------------------------------------------------------------------ + + /** + * Returns the default certificate store type. + * + * <p>This value can be set at run-time via the security property + * "certstore.type"; if not specified than the default type will be + * "LDAP". + * + * @return The default CertStore type. + */ + public static final synchronized String getDefaultType() + { + String type = null; + type = (String) java.security.AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return Security.getProperty("certstore.type"); + } + } + ); + if (type == null) + type = "LDAP"; + return type; + } + + /** + * Get an instance of the given certificate store from the first + * installed provider. + * + * @param type The type of CertStore to create. + * @param params The parameters to initialize this cert store with. + * @return The new instance. + * @throws InvalidAlgorithmParameterException If the instance rejects + * the specified parameters. + * @throws NoSuchAlgorithmException If no installed provider + * implements the specified CertStore. + * @throws IllegalArgumentException If <i>provider</i> is null. + */ + public static CertStore getInstance(String type, CertStoreParameters params) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + { + Provider[] p = Security.getProviders(); + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(type, params, p[i]); + } + catch (NoSuchAlgorithmException e) + { + // Ignored. + } + } + + throw new NoSuchAlgorithmException(type); + } + + /** + * Get an instance of the given certificate store from the named + * provider. + * + * @param type The type of CertStore to create. + * @param params The parameters to initialize this cert store with. + * @param provider The name of the provider from which to get the + * implementation. + * @return The new instance. + * @throws InvalidAlgorithmParameterException If the instance rejects + * the specified parameters. + * @throws NoSuchAlgorithmException If the specified provider does not + * implement the specified CertStore. + * @throws NoSuchProviderException If no provider named + * <i>provider</i> is installed. + * @throws IllegalArgumentException If <i>provider</i> is null. + */ + public static CertStore getInstance(String type, CertStoreParameters params, + String provider) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, + NoSuchProviderException + { + Provider p = Security.getProvider(provider); + if (p == null) + throw new NoSuchProviderException(provider); + return getInstance(type, params, p); + } + + /** + * Get an instance of the given certificate store from the given + * provider. + * + * @param type The type of CertStore to create. + * @param params The parameters to initialize this cert store with. + * @param provider The provider from which to get the implementation. + * @return The new instance. + * @throws InvalidAlgorithmParameterException If the instance rejects + * the specified parameters. + * @throws NoSuchAlgorithmException If the specified provider does not + * implement the specified CertStore. + * @throws IllegalArgumentException If <i>provider</i> is null. + */ + public static CertStore getInstance(String type, CertStoreParameters params, + Provider provider) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + { + if (provider == null) + throw new IllegalArgumentException("null provider"); + + try + { + return new CertStore((CertStoreSpi) Engine.getInstance(CERT_STORE, + type, provider, new Object[] { params }), provider, type, params); + } + catch (ClassCastException cce) + { + throw new NoSuchAlgorithmException(type); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + Throwable cause = ite.getCause(); + if (cause instanceof InvalidAlgorithmParameterException) + throw (InvalidAlgorithmParameterException) cause; + else + throw new NoSuchAlgorithmException(type); + } + } + +// Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the type of certificate store this instance represents. + * + * @return The CertStore type. + */ + public final String getType() + { + return type; + } + + /** + * Return the provider of this implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Get the parameters this instance was created with, if any. The + * parameters will be cloned before they are returned. + * + * @return The parameters, or null. + */ + public final CertStoreParameters getCertStoreParameters() + { + return params != null ? (CertStoreParameters) params.clone() : null; + } + + /** + * Get a collection of certificates from this CertStore, optionally + * filtered by the specified CertSelector. The Collection returned may + * be empty, but will never be null. + * + * <p>Implementations may not allow a null argument, even if no + * filtering is desired. + * + * @param selector The certificate selector. + * @return The collection of certificates. + * @throws CertStoreException If the certificates cannot be retrieved. + */ + public final Collection getCertificates(CertSelector selector) + throws CertStoreException + { + return storeSpi.engineGetCertificates(selector); + } + + /** + * Get a collection of certificate revocation lists from this CertStore, + * optionally filtered by the specified CRLSelector. The Collection + * returned may be empty, but will never be null. + * + * <p>Implementations may not allow a null argument, even if no + * filtering is desired. + * + * @param selector The certificate selector. + * @return The collection of certificate revocation lists. + * @throws CertStoreException If the CRLs cannot be retrieved. + */ + public final Collection getCRLs(CRLSelector selector) + throws CertStoreException + { + return storeSpi.engineGetCRLs(selector); + } +} diff --git a/libjava/classpath/java/security/cert/CertStoreException.java b/libjava/classpath/java/security/cert/CertStoreException.java new file mode 100644 index 00000000000..a4d8b7a46e1 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreException.java @@ -0,0 +1,159 @@ +/* CertStoreException.java -- wraps an exception during certificate storage + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while retrieving certificates and CRLs from + * <code>CertStore</code>, wrapping the lower exception. This class is not + * thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertStore + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertStoreException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 2395296107471573245L; + + /** + * Create an exception without a message. The cause may be initialized. + */ + public CertStoreException() + { + } + + /** + * Create an exception with a message. The cause may be initialized. + * + * @param msg a message to display with exception + */ + public CertStoreException(String msg) + { + super(msg); + } + + /** + * Create an exception with a cause. The message will be + * <code>cause == null ? null : cause.toString()</code>. + * + * @param cause the cause + */ + public CertStoreException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Create an exception with a cause and a message. + * + * @param msg the message + * @param cause the cause + */ + public CertStoreException(String msg, Throwable cause) + { + super(msg); + initCause(cause); + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to <code>System.err</code>. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertStoreParameters.java b/libjava/classpath/java/security/cert/CertStoreParameters.java new file mode 100644 index 00000000000..aab22f01fda --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreParameters.java @@ -0,0 +1,60 @@ +/* CertStoreParameters -- interface to CertStore parameters. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Parameters used when creating instances of {@link CertStore}. This + * class does not define any methods (except a required cloneable + * interface) and is provided only to provide type safety for + * implementations. Concrete implementations implement this interface + * in accord with thier own needs. + * + * @see LDAPCertStoreParameters + * @see CollectionCertStoreParameters + */ +public interface CertStoreParameters extends Cloneable +{ + + /** + * Create a copy of these parameters. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertStoreSpi.java b/libjava/classpath/java/security/cert/CertStoreSpi.java new file mode 100644 index 00000000000..eca0e866ca1 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreSpi.java @@ -0,0 +1,102 @@ +/* CertStoreSpi -- certificate store service provider interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.Collection; + +/** + * The <i>service provider interface</i> (<b>SPI</b>) for the {@link + * CertStore} class. + * + * <p>Providers wishing to implement a CertStore must subclass this + * class, implementing all the abstract methods. Providers may also + * implement the {@link CertStoreParameters} interface, if they require + * parameters. + * + * @since JDK 1.4 + * @see CertStore + * @see CollectionCertStoreParameters + * @see LDAPCertStoreParameters + */ +public abstract class CertStoreSpi +{ + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertStoreSpi. + * + * @param params The parameters to initialize this instance with, or + * null if no parameters are required. + * @throws InvalidAlgorithmParameterException If the specified + * parameters are inappropriate for this class. + */ + public CertStoreSpi(CertStoreParameters params) + throws java.security.InvalidAlgorithmParameterException + { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Get the certificates from this store, filtering them through the + * specified CertSelector. + * + * @param selector The CertSelector to filter certificates. + * @return A (non-null) collection of certificates. + * @throws CertStoreException If the certificates cannot be retrieved. + */ + public abstract Collection engineGetCertificates(CertSelector selector) + throws CertStoreException; + + /** + * Get the certificate revocation list from this store, filtering them + * through the specified CRLSelector. + * + * @param selector The CRLSelector to filter certificate revocation + * lists. + * @return A (non-null) collection of certificate revocation list. + * @throws CertStoreException If the CRLs cannot be retrieved. + */ + public abstract Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException; +} diff --git a/libjava/classpath/java/security/cert/Certificate.java b/libjava/classpath/java/security/cert/Certificate.java new file mode 100644 index 00000000000..f8456f97ae5 --- /dev/null +++ b/libjava/classpath/java/security/cert/Certificate.java @@ -0,0 +1,306 @@ +/* Certificate.java --- Certificate class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.ByteArrayInputStream; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; + +/** + * The Certificate class is an abstract class used to manage + * identity certificates. An identity certificate is a + * combination of a principal and a public key which is + * certified by another principal. This is the puprose of + * Certificate Authorities (CA). + * + * <p>This class is used to manage different types of certificates + * but have important common puposes. Different types of + * certificates like X.509 and OpenPGP share general certificate + * functions (like encoding and verifying) and information like + * public keys. + * + * <p>X.509, OpenPGP, and SDSI can be implemented by subclassing this + * class even though they differ in storage methods and information + * stored. + * + * @see CertificateFactory + * @see X509Certificate + * @since JDK 1.2 + * @author Mark Benvenuto + * @author Casey Marshall + */ +public abstract class Certificate implements Serializable +{ + private static final long serialVersionUID = -6751606818319535583L; + + private String type; + + /** + Constructs a new certificate of the specified type. An example + is "X.509". + + @param type a valid standard name for a certificate. + */ + protected Certificate(String type) + { + this.type = type; + } + + /** + Returns the Certificate type. + + @return a string representing the Certificate type + */ + public final String getType() + { + return type; + } + + /** + Compares this Certificate to other. It checks if the + object if instanceOf Certificate and then checks if + the encoded form matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof Certificate ) { + try { + Certificate x = (Certificate) other; + if( getEncoded().length != x.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = x.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CertificateEncodingException cee ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this Certificate in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this Certificate. + It assumes each certificate has only one encoding format. + Ex: X.509 is encoded as ASN.1 DER + + @return byte array containg encoded form + + @throws CertificateEncodingException if an error occurs + */ + public abstract byte[] getEncoded() throws CertificateEncodingException; + + /** + Verifies that this Certificate was properly signed with the + PublicKey that corresponds to its private key. + + @param key PublicKey to verify with + + @throws CertificateException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException no provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key) + throws CertificateException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Verifies that this Certificate was properly signed with the + PublicKey that corresponds to its private key and uses + the signature engine provided by the provider. + + @param key PublicKey to verify with + @param sigProvider Provider to use for signature algorithm + + @throws CertificateException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException incorrect provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key, + String sigProvider) + throws CertificateException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Returns a string representing the Certificate. + + @return a string representing the Certificate. + */ + public abstract String toString(); + + + /** + Returns the public key stored in the Certificate. + + @return The public key + */ + public abstract PublicKey getPublicKey(); + + // Protected methods. + // ------------------------------------------------------------------------ + + /** + * Returns a replacement for this certificate to be serialized. This + * method returns the equivalent to the following for this class: + * + * <blockquote> + * <pre>new CertificateRep(getType(), getEncoded());</pre> + * </blockquote> + * + * <p>This thusly replaces the certificate with its name and its + * encoded form, which can be deserialized later with the {@link + * CertificateFactory} implementation for this certificate's type. + * + * @return The replacement object to be serialized. + * @throws ObjectStreamException If the replacement could not be + * created. + */ + protected Object writeReplace() throws ObjectStreamException + { + try + { + return new CertificateRep(getType(), getEncoded()); + } + catch (CertificateEncodingException cee) + { + throw new InvalidObjectException(cee.toString()); + } + } + + // Inner class. + // ------------------------------------------------------------------------ + + /** + Certificate.CertificateRep is an inner class used to provide an alternate + storage mechanism for serialized Certificates. + */ + protected static class CertificateRep implements java.io.Serializable + { + + /** From JDK1.4. */ + private static final long serialVersionUID = -8563758940495660020L; + + /** The certificate type, e.g. "X.509". */ + private String type; + + /** The encoded certificate data. */ + private byte[] data; + + /** + * Create an alternative representation of this certificate. The + * <code>(type, data)</code> pair is typically the certificate's + * type as returned by {@link Certificate#getType()} (i.e. the + * canonical name of the certificate type) and the encoded form as + * returned by {@link Certificate#getEncoded()}. + * + * <p>For example, X.509 certificates would create an instance of + * this class with the parameters "X.509" and the ASN.1 + * representation of the certificate, encoded as DER bytes. + * + * @param type The certificate type. + * @param data The encoded certificate data. + */ + protected CertificateRep(String type, byte[] data) + { + this.type = type; + this.data = data; + } + + /** + * Deserialize this certificate replacement into the appropriate + * certificate object. That is, this method attempts to create a + * {@link CertificateFactory} for this certificate's type, then + * attempts to parse the encoded data with that factory, returning + * the resulting certificate. + * + * @return The deserialized certificate. + * @throws ObjectStreamException If there is no appropriate + * certificate factory for the given type, or if the encoded form + * cannot be parsed. + */ + protected Object readResolve() throws ObjectStreamException + { + try + { + CertificateFactory fact = CertificateFactory.getInstance(type); + return fact.generateCertificate(new ByteArrayInputStream(data)); + } + catch (Exception e) + { + throw new InvalidObjectException(e.toString()); + } + } + } +} diff --git a/libjava/classpath/java/security/cert/CertificateEncodingException.java b/libjava/classpath/java/security/cert/CertificateEncodingException.java new file mode 100644 index 00000000000..0bb0c26d358 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateEncodingException.java @@ -0,0 +1,71 @@ +/* CertificateEncodingException.java -- Certificate Encoding Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate Encoding. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 + */ +public class CertificateEncodingException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6219492851589449162L; + + /** + * Constructs an exception without a message string. + */ + public CertificateEncodingException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg A message to display with exception + */ + public CertificateEncodingException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateException.java b/libjava/classpath/java/security/cert/CertificateException.java new file mode 100644 index 00000000000..3e075ddaf35 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateException.java @@ -0,0 +1,74 @@ +/* CertificateException.java -- Certificate Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.GeneralSecurityException; + +/** + * Exception for a Certificate. + * + * @author Mark Benvenuto + * @see Certificate + * @since 1.2 + * @status updated to 1.4 + */ +public class CertificateException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3192535253797119798L; + + /** + * Constructs an exception without a message string. + */ + public CertificateException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateExpiredException.java b/libjava/classpath/java/security/cert/CertificateExpiredException.java new file mode 100644 index 00000000000..5b37142b5ec --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateExpiredException.java @@ -0,0 +1,71 @@ +/* CertificateExpiredException.java --- Certificate Expired Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate Expiring. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 + */ +public class CertificateExpiredException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 9071001339691533771L; + + /** + * Constructs an exception without a message string. + */ + public CertificateExpiredException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateExpiredException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateFactory.java b/libjava/classpath/java/security/cert/CertificateFactory.java new file mode 100644 index 00000000000..aedeff53573 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateFactory.java @@ -0,0 +1,358 @@ +/* CertificateFactory.java -- Certificate Factory Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.Engine; + +import java.io.InputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * This class implements the CertificateFactory class interface used to + * generate certificates, certificate revocation lists (CRLs), and certificate + * paths objects from their encoded forms. + * + * @author Mark Benvenuto + * @author Casey Marshall + * @since JDK 1.2 + * @status Fully compatible with JDK 1.4. + */ +public class CertificateFactory +{ + + /** The service name for certificate factories. */ + private static final String CERTIFICATE_FACTORY = "CertificateFactory"; + + private CertificateFactorySpi certFacSpi; + private Provider provider; + private String type; + + /** + * Creates an instance of CertificateFactory. + * + * @param certFacSpi The underlying CertificateFactory engine. + * @param provider The provider of this implementation. + * @param type The type of Certificate this factory creates. + */ + protected CertificateFactory(CertificateFactorySpi certFacSpi, + Provider provider, String type) + { + this.certFacSpi = certFacSpi; + this.provider = provider; + this.type = type; + } + +// Class methods. + // ------------------------------------------------------------------------ + + /** + * Gets an instance of the CertificateFactory class representing + * the specified certificate factory. If the type is not + * found then, it throws CertificateException. + * + * @param type The type of certificate factory to create. + * @return a CertificateFactory repesenting the desired type + * @throws CertificateException If the type of certificate is not + * implemented by any installed provider. + */ + public static final CertificateFactory getInstance(String type) + throws CertificateException + { + Provider[] p = Security.getProviders(); + + for (int i = 0; i < p.length; i++) + { + try + { + return getInstance(type, p[i]); + } + catch (CertificateException e) + { + // Ignored. + } + } + + throw new CertificateException(type); + } + + /** + * Gets an instance of the CertificateFactory class representing + * the specified certificate factory from the specified provider. + * If the type is not found then, it throws {@link CertificateException}. + * If the provider is not found, then it throws + * {@link java.security.NoSuchProviderException}. + * + * @param type The type of certificate factory to create. + * @param provider The name of the provider from which to get the + * implementation. + * @return A CertificateFactory for the desired type. + * @throws CertificateException If the type of certificate is not + * implemented by the named provider. + * @throws NoSuchProviderException If the named provider is not installed. + */ + public static final CertificateFactory getInstance(String type, + String provider) + throws CertificateException, NoSuchProviderException + { + Provider p = Security.getProvider(provider); + if( p == null) + throw new NoSuchProviderException(provider); + + return getInstance(type, p); + } + + /** + * Get a certificate factory for the given certificate type from the + * given provider. + * + * @param type The type of certificate factory to create. + * @param provider The provider from which to get the implementation. + * @return A CertificateFactory for the desired type. + * @throws CertificateException If the type of certificate is not + * implemented by the provider. + * @throws IllegalArgumentException If the provider is null. + */ + public static final CertificateFactory getInstance(String type, + Provider provider) + throws CertificateException + { + if (provider == null) + throw new IllegalArgumentException("null provider"); + + try + { + return new CertificateFactory((CertificateFactorySpi) + Engine.getInstance(CERTIFICATE_FACTORY, type, provider), + provider, type); + } + catch (ClassCastException cce) + { + throw new CertificateException(type); + } + catch (java.lang.reflect.InvocationTargetException ite) + { + throw new CertificateException(type); + } + catch (NoSuchAlgorithmException nsae) + { + throw new CertificateException(nsae.getMessage()); + } + } + +// Instance methods. + // ------------------------------------------------------------------------ + + /** + * Gets the provider of this implementation. + * + * @return The provider of this implementation. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Returns the type of the certificate this factory creates. + * + * @return A string with the type of certificate + */ + public final String getType() + { + return type; + } + + /** + * Generates a Certificate from the encoded data read + * from an InputStream. + * + * <p>The input stream must contain only one certificate. + * + * <p>If there exists a specialized certificate class for the + * certificate format handled by the certificate factory + * then the return Ceritificate should be a typecast of it. + * Ex: A X.509 CertificateFactory should return X509Certificate. + * + * <p>For X.509 certificates, the certificate in inStream must be + * DER encoded and supplied in binary or printable (Base64) + * encoding. If the certificate is in Base64 encoding, it must be + * bounded by -----BEGINCERTIFICATE-----, and + * -----END CERTIFICATE-----. + * + * @param inStream An input stream containing the certificate data. + * @return A certificate initialized from the decoded InputStream data. + * @throws CertificateException If an error occurs decoding the + * certificate. + */ + public final Certificate generateCertificate(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertificate(inStream); + } + + /** + * Returns a collection of certificates that were read from the + * input stream. It may be empty, have only one, or have + * multiple certificates. + * + * For a X.509 certificate factory, the stream may contain a + * single DER encoded certificate or a PKCS#7 certificate + * chain. This is a PKCS#7 <I>SignedData</I> object with the + * most significant field being <I>certificates</I>. If no + * CRLs are present, then an empty collection is returned. + * + * @param inStream An input stream containing the certificate data. + * @return A collection of certificates initialized from the decoded + * InputStream data. + * @throws CertificateException If an error occurs decoding the + * certificates. + */ + public final Collection generateCertificates(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertificates(inStream); + } + + /** + * Generates a CRL based on the encoded data read + * from the InputStream. + * + * <p>The input stream must contain only one CRL. + * + * <p>If there exists a specialized CRL class for the + * CRL format handled by the certificate factory + * then the return CRL should be a typecast of it. + * Ex: A X.509 CertificateFactory should return X509CRL. + * + * @param inStream An input stream containing the CRL data. + * @return A CRL initialized from the decoded InputStream data. + * @throws CRLException If an error occurs decoding the CRL. + */ + public final CRL generateCRL(InputStream inStream) + throws CRLException + { + return certFacSpi.engineGenerateCRL(inStream); + } + + /** + * <p>Generates CRLs based on the encoded data read + * from the InputStream. + * + * <p>For a X.509 certificate factory, the stream may contain a + * single DER encoded CRL or a PKCS#7 CRL set. This is a + * PKCS#7 <I>SignedData</I> object with the most significant + * field being <I>crls</I>. If no CRLs are present, then an + * empty collection is returned. + * + * @param inStream an input stream containing the CRLs. + * @return a collection of CRLs initialized from the decoded + * InputStream data. + * @throws CRLException If an error occurs decoding the CRLs. + */ + public final Collection generateCRLs(InputStream inStream) + throws CRLException + { + return certFacSpi.engineGenerateCRLs( inStream ); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream. The default encoding of this factory is used. + * + * @param inStream The InputStream containing the CertPath data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public final CertPath generateCertPath(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(inStream); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream, using the specified encoding. + * + * @param inStream The InputStream containing the CertPath data. + * @param encoding The encoding of the InputStream data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public final CertPath generateCertPath(InputStream inStream, String encoding) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(inStream, encoding); + } + + /** + * Generate a {@link CertPath} and initialize it with the certificates + * in the {@link java.util.List} argument. + * + * @param certificates The list of certificates with which to create + * the CertPath. + * @return A CertPath initialized from the certificates. + * @throws CertificateException If an error occurs generating the + * CertPath. + */ + public final CertPath generateCertPath(List certificates) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(certificates); + } + + /** + * Returns an Iterator of CertPath encodings supported by this + * factory, with the default encoding first. The returned Iterator + * cannot be modified. + * + * @return The Iterator of supported encodings. + */ + public final Iterator getCertPathEncodings() + { + return certFacSpi.engineGetCertPathEncodings(); + } +} // class CertificateFactory diff --git a/libjava/classpath/java/security/cert/CertificateFactorySpi.java b/libjava/classpath/java/security/cert/CertificateFactorySpi.java new file mode 100644 index 00000000000..beea9646a67 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateFactorySpi.java @@ -0,0 +1,225 @@ +/* CertificateFactorySpi.java --- Certificate Factory Class + Copyright (C) 1999,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.InputStream; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + CertificateFactorySpi is the abstract class Service Provider + Interface (SPI) for the CertificateFactory class. A provider + must implement all the abstract methods if they wish to + supply a certificate factory for a particular certificate + type. Ex: X.509 + + Certificate factories are used to generate certificates and + certificate revocation lists (CRL) from their encoding. + + @since JDK 1.2 + + @author Mark Benvenuto + */ +public abstract class CertificateFactorySpi +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Constructs a new CertificateFactorySpi + */ + public CertificateFactorySpi() + {} + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + Generates a Certificate based on the encoded data read + from the InputStream. + + The input stream must contain only one certificate. + + If there exists a specialized certificate class for the + certificate format handled by the certificate factory + then the return Ceritificate should be a typecast of it. + Ex: A X.509 CertificateFactory should return X509Certificate. + + For X.509 certificates, the certificate in inStream must be + DER encoded and supplied in binary or printable (Base64) + encoding. If the certificate is in Base64 encoding, it must be + bounded by -----BEGIN CERTIFICATE-----, and + -----END CERTIFICATE-----. + + @param inStream an input stream containing the certificate data + + @return a certificate initialized with InputStream data. + + @throws CertificateException Certificate parsing error + */ + public abstract Certificate engineGenerateCertificate(InputStream inStream) + throws CertificateException; + + /** + Returns a collection of certificates that were read from the + input stream. It may be empty, have only one, or have + multiple certificates. + + For a X.509 certificate factory, the stream may contain a + single DER encoded certificate or a PKCS#7 certificate + chain. This is a PKCS#7 <I>SignedData</I> object with the + most significant field being <I>certificates</I>. If no + CRLs are present, then an empty collection is returned. + + @param inStream an input stream containing the certificates + + @return a collection of certificates initialized with + the InputStream data. + + @throws CertificateException Certificate parsing error + */ + public abstract Collection engineGenerateCertificates(InputStream inStream) + throws CertificateException; + + /** + Generates a CRL based on the encoded data read + from the InputStream. + + The input stream must contain only one CRL. + + If there exists a specialized CRL class for the + CRL format handled by the certificate factory + then the return CRL should be a typecast of it. + Ex: A X.509 CertificateFactory should return X509CRL. + + @param inStream an input stream containing the CRL data + + @return a CRL initialized with InputStream data. + + @throws CRLException CRL parsing error + */ + public abstract CRL engineGenerateCRL(InputStream inStream) + throws CRLException; + + /** + Generates CRLs based on the encoded data read + from the InputStream. + + For a X.509 certificate factory, the stream may contain a + single DER encoded CRL or a PKCS#7 CRL set. This is a + PKCS#7 <I>SignedData</I> object with the most significant + field being <I>crls</I>. If no CRLs are present, then an + empty collection is returned. + + @param inStream an input stream containing the CRLs + + @return a collection of CRLs initialized with + the InputStream data. + + @throws CRLException CRL parsing error + */ + public abstract Collection engineGenerateCRLs(InputStream inStream) + throws CRLException; + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream. The default encoding of this factory is used. + * + * @param inStream The InputStream containing the CertPath data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public CertPath engineGenerateCertPath(InputStream inStream) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream, using the specified encoding. + * + * @param inStream The InputStream containing the CertPath data. + * @param encoding The encoding of the InputStream data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public CertPath engineGenerateCertPath(InputStream inStream, String encoding) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Generate a {@link CertPath} and initialize it with the certificates + * in the {@link java.util.List} argument. + * + * @param certificates The list of certificates with which to create + * the CertPath. + * @return A CertPath initialized from the certificates. + * @throws CertificateException If an error occurs generating the + * CertPath. + */ + public CertPath engineGenerateCertPath(List certificates) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Returns an Iterator of CertPath encodings supported by this + * factory, with the default encoding first. The returned Iterator + * cannot be modified. + * + * @return The Iterator of supported encodings. + */ + public Iterator engineGetCertPathEncodings() + { + throw new UnsupportedOperationException("not implemented"); + } +} + diff --git a/libjava/classpath/java/security/cert/CertificateNotYetValidException.java b/libjava/classpath/java/security/cert/CertificateNotYetValidException.java new file mode 100644 index 00000000000..dfb4b483785 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateNotYetValidException.java @@ -0,0 +1,71 @@ +/* CertificateNotYetValidException.java -- Certificate Not Yet Valid Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate that is not yet valid. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 +*/ +public class CertificateNotYetValidException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4355919900041064702L; + + /** + * Constructs an exception without a message string. + */ + public CertificateNotYetValidException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg A message to display with exception + */ + public CertificateNotYetValidException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateParsingException.java b/libjava/classpath/java/security/cert/CertificateParsingException.java new file mode 100644 index 00000000000..61faa44386e --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateParsingException.java @@ -0,0 +1,71 @@ +/* CertificateParsingException.java -- Certificate Parsing Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for parsing a DER-encoded Certificate. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 +*/ +public class CertificateParsingException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7989222416793322029L; + + /** + * Constructs an exception without a message string. + */ + public CertificateParsingException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateParsingException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java b/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java new file mode 100644 index 00000000000..bac1e3b3e4f --- /dev/null +++ b/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java @@ -0,0 +1,121 @@ +/* CollectionCertStoreParameters -- collection-based cert store parameters + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * An implementation of {@link CertStoreParameters} with a simple, + * in-memory {@link Collection} of certificates and certificate + * revocation list. + * + * <p>Note that this class is not thread-safe, and its underlying + * collection may be changed at any time. + * + * @see CertStore + */ +public class CollectionCertStoreParameters implements CertStoreParameters +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** The underlying collection. */ + private final Collection collection; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CollectionCertStoreParameters with an empty, + * immutable collection. + */ + public CollectionCertStoreParameters() + { + this(Collections.EMPTY_LIST); + } + + /** + * Create a new CollectionCertStoreParameters with the specified + * collection. The argument is not copied, and subsequent changes to + * the collection will change this class's collection. + * + * @param collection The collection. + * @throws NullPointerException If <i>collection</i> is null. + */ + public CollectionCertStoreParameters(Collection collection) + { + if (collection == null) + throw new NullPointerException(); + this.collection = collection; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public Object clone() + { + return new CollectionCertStoreParameters(new ArrayList(collection)); + } + + /** + * Return the underlying collection. The collection is not copied + * before being returned, so callers may update the collection that is + * returned. + * + * @return The collection. + */ + public Collection getCollection() + { + return collection; + } + + /** + * Return a string representation of these parameters. + * + * @return The string representation of these parameters. + */ + public String toString() + { + return "CollectionCertStoreParameters: [ collection: " + + collection + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java b/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java new file mode 100644 index 00000000000..4414e65a092 --- /dev/null +++ b/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java @@ -0,0 +1,140 @@ +/* LDAPCertStoreParameters.java -- LDAP CertStore parameters. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Parameters for CertStores that are retrieved via the <i>lightweight + * directory access protocol</i> (<b>LDAP</b>). + * + * @see CertStore + */ +public class LDAPCertStoreParameters implements CertStoreParameters +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** The default LDAP port. */ + private static final int LDAP_PORT = 389; + + /** The server name. */ + private final String serverName; + + /** The LDAP port. */ + private final int port; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new LDAPCertStoreParameters object, with a servername of + * "localhost" and a port of 389. + */ + public LDAPCertStoreParameters() + { + this("localhost", LDAP_PORT); + } + + /** + * Create a new LDAPCertStoreParameters object, with a specified + * server name and a port of 389. + * + * @param serverName The LDAP server name. + * @throws NullPointerException If <i>serverName</i> is null. + */ + public LDAPCertStoreParameters(String serverName) + { + this(serverName, LDAP_PORT); + } + + /** + * Create a new LDAPCertStoreParameters object, with a specified + * server name and port. + * + * @param serverName The LDAP server name. + * @param port The LDAP port. + * @throws NullPointerException If <i>serverName</i> is null. + */ + public LDAPCertStoreParameters(String serverName, int port) + { + if (serverName == null) + throw new NullPointerException(); + this.serverName = serverName; + this.port = port; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public Object clone() + { + return new LDAPCertStoreParameters(serverName, port); + } + + /** + * Return the server name. + * + * @return The server name. + */ + public String getServerName() + { + return serverName; + } + + /** + * Return the port. + * + * @return the port. + */ + public int getPort() + { + return port; + } + + /** + * Return a string representation of these parameters. + * + * @return The string representation of these parameters. + */ + public String toString() + { + return "LDAPCertStoreParameters: [ serverName: " + serverName + + "; port: " + port + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PKIXBuilderParameters.java b/libjava/classpath/java/security/cert/PKIXBuilderParameters.java new file mode 100644 index 00000000000..38b3df5e78a --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXBuilderParameters.java @@ -0,0 +1,145 @@ +/* PKIXBuilderParameters.java -- parameters for PKIX cert path builders + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import java.util.Set; + +/** + * Parameters for building certificate paths using the PKIX algorithm. + * + * @see CertPathBuilder + */ +public class PKIXBuilderParameters extends PKIXParameters +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The maximum path length. */ + private int maxPathLength; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new PKIXBuilderParameters object, populating the trusted + * certificates set with all X.509 certificates found in the given key + * store. All certificates found in the key store are assumed to be + * trusted by this constructor. + * + * @param keystore The key store. + * @param targetConstraints The target certificate constraints. + * @throws KeyStoreException If the certificates cannot be retrieved + * from the key store. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the key store. + * @throws NullPointerException If <i>keystore</i> is null. + */ + public PKIXBuilderParameters(KeyStore keystore, + CertSelector targetConstraints) + throws KeyStoreException, InvalidAlgorithmParameterException + { + super(keystore); + setTargetCertConstraints(targetConstraints); + maxPathLength = 5; + } + + /** + * Create a new PKIXBuilderParameters object, populating the trusted + * certificates set with the elements of the given set, each of which + * must be a {@link TrustAnchor}. + * + * @param trustAnchors The set of trust anchors. + * @param targetConstraints The target certificate constraints. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If <i>trustAnchors</i> is null. + * @throws ClassCastException If every element in <i>trustAnchors</i> + * is not a {@link TrustAnchor}. + */ + public PKIXBuilderParameters(Set trustAnchors, CertSelector targetConstraints) + throws InvalidAlgorithmParameterException + { + super(trustAnchors); + setTargetCertConstraints(targetConstraints); + maxPathLength = 5; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the maximum length of certificate paths to build. + * + * <p>If this value is 0 it is taken to mean that the certificate path + * should contain only one certificate. A value of -1 means that the + * certificate path length is unconstrained. The default value is 5. + * + * @return The maximum path length. + */ + public int getMaxPathLength() + { + return maxPathLength; + } + + /** + * Sets the maximum length of certificate paths to build. + * + * @param maxPathLength The new path length. + * @throws IllegalArgumentException If <i>maxPathLength</i> is less + * than -1. + */ + public void setMaxPathLength(int maxPathLength) + { + if (maxPathLength < -1) + throw new IllegalArgumentException(); + this.maxPathLength = maxPathLength; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(super.toString()); + buf.insert(buf.length() - 2, "; Max Path Length=" + maxPathLength); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java b/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java new file mode 100644 index 00000000000..5091dd41298 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java @@ -0,0 +1,102 @@ +/* PKIXCertPathBuilderResult.java -- PKIX cert path bulider result + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * The result of calling the {@link + * CertPathBuilder#build(java.security.cert.CertPathParameters)} method + * of PKIX {@link CertPathBuilder}s. + * + * @see CertPathBuilder + * @see CertPathBuilderResult + */ +public class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult + implements CertPathBuilderResult +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The certificate path. */ + private CertPath certPath; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new PKIXCertPathBuilderResult. + * + * @param certPath The certificate path. + * @param trustAnchor The trust anchor. + * @param policyTree The root node of the policy tree. + * @param subjectPublicKey The public key. + * @throws NullPointerException If <i>certPath</i>, <i>trustAnchor</i> or + * <i>subjectPublicKey</i> is null. + */ + public PKIXCertPathBuilderResult(CertPath certPath, + TrustAnchor trustAnchor, + PolicyNode policyTree, + java.security.PublicKey subjectPublicKey) + { + super(trustAnchor, policyTree, subjectPublicKey); + if (certPath == null) + throw new NullPointerException(); + this.certPath = certPath; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the certificate path that was built. + * + * @return The certificate path that was built. + */ + public CertPath getCertPath() + { + return certPath; + } + + public String toString() + { + StringBuffer buf = new StringBuffer(super.toString()); + buf.insert(buf.length() - 2, "; CertPath=" + certPath); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathChecker.java b/libjava/classpath/java/security/cert/PKIXCertPathChecker.java new file mode 100644 index 00000000000..7a33576e1bb --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathChecker.java @@ -0,0 +1,133 @@ +/* PKIXCertPathChecker.java -- checks X.509 certificate paths. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.Collection; +import java.util.Set; + +/** + * A validator for X.509 certificates when approving certificate chains. + * + * <p>Concrete subclasses can be passed to the {@link + * PKIXParameters#setCertPathCheckers(java.util.List)} and {@link + * PKIXParameters#addCertPathChecker(java.security.cert.PKIXCertPathChecker} + * methods, which are then used to set up PKIX certificate chain + * builders or validators. These classes then call the {@link + * #check(java.security.cert.Certificate,java.util.Collection)} method + * of this class, performing whatever checks on the certificate, + * throwing an exception if any check fails. + * + * <p>Subclasses of this must be able to perform their checks in the + * backward direction -- from the most-trusted certificate to the target + * -- and may optionally support forward checking -- from the target to + * the most-trusted certificate. + * + * @see PKIXParameters + */ +public abstract class PKIXCertPathChecker implements Cloneable +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** Default constructor. */ + protected PKIXCertPathChecker() + { + super(); + } + + // Cloneable interface. + // ------------------------------------------------------------------------ + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException cnse) + { + throw new InternalError(cnse.getMessage()); + } + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Initialize this PKIXCertPathChecker. If subclasses support forward + * checking, a value of true can be passed to this method, and + * certificates can be validated from the target certificate to the + * most-trusted certifcate. + * + * @param forward The direction of this PKIXCertPathChecker. + * @throws CertPathValidatorException If <i>forward</i> is true and + * this class does not support forward checking. + */ + public abstract void init(boolean forward) throws CertPathValidatorException; + + /** + * Returns whether or not this class supports forward checking. + * + * @return Whether or not this class supports forward checking. + */ + public abstract boolean isForwardCheckingSupported(); + + /** + * Returns an immutable set of X.509 extension object identifiers (OIDs) + * supported by this PKIXCertPathChecker. + * + * @return An immutable set of Strings of the supported X.509 OIDs, or + * null if no extensions are supported. + */ + public abstract Set getSupportedExtensions(); + + /** + * Checks a certificate, removing any critical extensions that are + * resolved in this check. + * + * @param cert The certificate to check. + * @param unresolvedCritExts The (mutable) collection of as-of-yet + * unresolved critical extensions, as OID strings. + * @throws CertPathValidatorException If this certificate fails this + * check. + */ + public abstract void check(Certificate cert, Collection unresolvedCritExts) + throws CertPathValidatorException; +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java b/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java new file mode 100644 index 00000000000..5a1660c3bc9 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java @@ -0,0 +1,142 @@ +/* PKIXCertPathValidatorResult.java -- PKIX cert path builder result + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.PublicKey; + +/** + * Results returned by the {@link + * CertPathValidator#validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)} + * method for PKIX {@link CertPathValidator}s. + * + * @see CertPathValidator + */ +public class PKIXCertPathValidatorResult implements CertPathValidatorResult +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The trust anchor. */ + private final TrustAnchor trustAnchor; + + /** The root node of the policy tree. */ + private final PolicyNode policyTree; + + /** The subject's public key. */ + private final PublicKey subjectPublicKey; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new PKIXCertPathValidatorResult. + * + * @param trustAnchor The trust anchor. + * @param policyTree The root node of the policy tree. + * @param subjectPublicKey The public key. + * @throws NullPointerException If either <i>trustAnchor</i> or + * <i>subjectPublicKey</i> is null. + */ + public PKIXCertPathValidatorResult(TrustAnchor trustAnchor, + PolicyNode policyTree, + PublicKey subjectPublicKey) + { + if (trustAnchor == null || subjectPublicKey == null) + throw new NullPointerException(); + this.trustAnchor = trustAnchor; + this.policyTree = policyTree; + this.subjectPublicKey = subjectPublicKey; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the trust anchor. + * + * @return The trust anchor. + */ + public TrustAnchor getTrustAnchor() + { + return trustAnchor; + } + + /** + * Returns the root node of the policy tree. + * + * @return The root node of the policy tree. + */ + public PolicyNode getPolicyTree() + { + return policyTree; + } + + /** + * Returns the subject public key. + * + * @return The subject public key. + */ + public PublicKey getPublicKey() + { + return subjectPublicKey; + } + + /** + * Returns a copy of this object. + * + * @return The copy. + */ + public Object clone() + { + return new PKIXCertPathValidatorResult(trustAnchor, policyTree, + subjectPublicKey); + } + + /** + * Returns a printable string representation of this result. + * + * @return A printable string representation of this result. + */ + public String toString() + { + return "[ Trust Anchor=" + trustAnchor + "; Policy Tree=" + + policyTree + "; Subject Public Key=" + subjectPublicKey + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PKIXParameters.java b/libjava/classpath/java/security/cert/PKIXParameters.java new file mode 100644 index 00000000000..4a987115a29 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXParameters.java @@ -0,0 +1,546 @@ +/* PKIXParameters.java -- parameters for the PKIX cert path algorithm + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * Parameters for verifying certificate paths using the PKIX + * (Public-Key Infrastructure (X.509)) algorithm. + * + * @see CertPathBulider + */ +public class PKIXParameters implements CertPathParameters +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The trusted certificates. */ + private final Set trustAnchors; + + /** The set of initial policy identifiers. */ + private final Set initPolicies; + + /** The list of certificate stores. */ + private final List certStores; + + /** The list of path checkers. */ + private final List pathCheckers; + + /** The revocation enabled flag. */ + private boolean revocationEnabled; + + /** The explicit policy required flag. */ + private boolean exPolicyRequired; + + /** The policy mapping inhibited flag. */ + private boolean policyMappingInhibited; + + /** The any policy inhibited flag. */ + private boolean anyPolicyInhibited; + + /** The policy qualifiers rejected flag. */ + private boolean policyQualRejected; + + /** The target validation date. */ + private Date date; + + /** The signature algorithm provider. */ + private String sigProvider; + + /** The target constraints. */ + private CertSelector targetConstraints; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new PKIXParameters object, populating the trusted + * certificates set with all certificates found in the given key + * store. All certificates found in the key store are assumed to be + * trusted by this constructor. + * + * @param keystore The key store. + * @throws KeyStoreException If the certificates cannot be retrieved + * from the key store. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the key store. + * @throws NullPointerException If <i>keystore</i> is null. + */ + public PKIXParameters(KeyStore keystore) + throws KeyStoreException, InvalidAlgorithmParameterException + { + this(); + for (Enumeration e = keystore.aliases(); e.hasMoreElements(); ) + { + String alias = (String) e.nextElement(); + if (!keystore.isCertificateEntry(alias)) + continue; + Certificate cert = keystore.getCertificate(alias); + if (cert instanceof X509Certificate) + trustAnchors.add(new TrustAnchor((X509Certificate) cert, null)); + } + if (trustAnchors.isEmpty()) + throw new InvalidAlgorithmParameterException("no certs in the key store"); + } + + /** + * Create a new PKIXParameters object, populating the trusted + * certificates set with the elements of the given set, each of which + * must be a {@link TrustAnchor}. + * + * @param trustAnchors The set of trust anchors. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If <i>trustAnchors</i> is null. + * @throws ClassCastException If every element in <i>trustAnchors</i> + * is not a {@link TrustAnchor}. + */ + public PKIXParameters(Set trustAnchors) + throws InvalidAlgorithmParameterException + { + this(); + setTrustAnchors(trustAnchors); + } + + /** + * Default constructor. + */ + private PKIXParameters() + { + trustAnchors = new HashSet(); + initPolicies = new HashSet(); + certStores = new LinkedList(); + pathCheckers = new LinkedList(); + revocationEnabled = true; + exPolicyRequired = false; + policyMappingInhibited = false; + anyPolicyInhibited = false; + policyQualRejected = true; + } + + /** + * Copying constructor for cloning. + * + * @param that The instance being cloned. + */ + private PKIXParameters(PKIXParameters that) + { + this(); + this.trustAnchors.addAll(that.trustAnchors); + this.initPolicies.addAll(that.initPolicies); + this.certStores.addAll(that.certStores); + this.pathCheckers.addAll(that.pathCheckers); + this.revocationEnabled = that.revocationEnabled; + this.exPolicyRequired = that.exPolicyRequired; + this.policyMappingInhibited = that.policyMappingInhibited; + this.anyPolicyInhibited = that.anyPolicyInhibited; + this.policyQualRejected = that.policyQualRejected; + this.date = that.date; + this.sigProvider = that.sigProvider; + this.targetConstraints = that.targetConstraints != null + ? (CertSelector) that.targetConstraints.clone() : null; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns an immutable set of trust anchors. The set returned will + * never be null and will never be empty. + * + * @return A (never null, never empty) immutable set of trust anchors. + */ + public Set getTrustAnchors() + { + return Collections.unmodifiableSet(trustAnchors); + } + + /** + * Sets the trust anchors of this class, replacing the current trust + * anchors with those in the given set. The supplied set is copied to + * prevent modification. + * + * @param trustAnchors The new set of trust anchors. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If <i>trustAnchors</i> is null. + * @throws ClassCastException If every element in <i>trustAnchors</i> + * is not a {@link TrustAnchor}. + */ + public void setTrustAnchors(Set trustAnchors) + throws InvalidAlgorithmParameterException + { + if (trustAnchors.isEmpty()) + throw new InvalidAlgorithmParameterException("no trust anchors"); + this.trustAnchors.clear(); + for (Iterator i = trustAnchors.iterator(); i.hasNext(); ) + { + this.trustAnchors.add((TrustAnchor) i.next()); + } + } + + /** + * Returns the set of initial policy identifiers (as OID strings). If + * any policy is accepted, this method returns the empty set. + * + * @return An immutable set of initial policy OID strings, or the + * empty set if any policy is acceptable. + */ + public Set getInitialPolicies() + { + return Collections.unmodifiableSet(initPolicies); + } + + /** + * Sets the initial policy identifiers (as OID strings). If the + * argument is null or the empty set, then any policy identifier will + * be accepted. + * + * @param initPolicies The new set of policy strings, or null. + * @throws ClassCastException If any element in <i>initPolicies</i> is + * not a string. + */ + public void setInitialPolicies(Set initPolicies) + { + this.initPolicies.clear(); + if (initPolicies == null) + return; + for (Iterator i = initPolicies.iterator(); i.hasNext(); ) + { + this.initPolicies.add((String) i.next()); + } + } + + /** + * Add a {@link CertStore} to the list of cert stores. + * + * @param store The CertStore to add. + */ + public void addCertStore(CertStore store) + { + if (store != null) + certStores.add(store); + } + + /** + * Returns an immutable list of cert stores. This method never returns + * null. + * + * @return The list of cert stores. + */ + public List getCertStores() + { + return Collections.unmodifiableList(certStores); + } + + /** + * Set the cert stores. If the argument is null the list of cert + * stores will be empty. + * + * @param certStores The cert stores. + */ + public void setCertStores(List certStores) + { + this.certStores.clear(); + if (certStores == null) + return; + for (Iterator i = certStores.iterator(); i.hasNext(); ) + { + this.certStores.add((CertStore) i.next()); + } + } + + /** + * Returns the value of the <i>revocation enabled</i> flag. The default + * value for this flag is <code>true</code>. + * + * @return The <i>revocation enabled</i> flag. + */ + public boolean isRevocationEnabled() + { + return revocationEnabled; + } + + /** + * Sets the value of the <i>revocation enabled</i> flag. + * + * @param value The new value. + */ + public void setRevocationEnabled(boolean value) + { + revocationEnabled = value; + } + + /** + * Returns the value of the <i>explicit policy required</i> flag. The + * default value of this flag is <code>false</code>. + * + * @return The <i>explicit policy required</i> flag. + */ + public boolean isExplicitPolicyRequired() + { + return exPolicyRequired; + } + + /** + * Sets the value of the <i>explicit policy required</i> flag. + * + * @param value The new value. + */ + public void setExplicitPolicyRequired(boolean value) + { + exPolicyRequired = value; + } + + /** + * Returns the value of the <i>policy mapping inhibited</i> flag. The + * default value of this flag is <code>false</code>. + * + * @return The <i>policy mapping inhibited</i> flag. + */ + public boolean isPolicyMappingInhibited() + { + return policyMappingInhibited; + } + + /** + * Sets the value of the <i>policy mapping inhibited</i> flag. + * + * @param value The new value. + */ + public void setPolicyMappingInhibited(boolean value) + { + policyMappingInhibited = value; + } + + /** + * Returns the value of the <i>any policy inhibited</i> flag. The + * default value of this flag is <code>false</code>. + * + * @return The <i>any policy inhibited</i> flag. + */ + public boolean isAnyPolicyInhibited() + { + return anyPolicyInhibited; + } + + /** + * Sets the value of the <i>any policy inhibited</i> flag. + * + * @param value The new value. + */ + public void setAnyPolicyInhibited(boolean value) + { + anyPolicyInhibited = value; + } + + /** + * Returns the value of the <i>policy qualifiers enabled</i> flag. The + * default value of this flag is <code>true</code>. + * + * @return The <i>policy qualifiers enabled</i> flag. + */ + public boolean getPolicyQualifiersRejected() + { + return policyQualRejected; + } + + /** + * Sets the value of the <i>policy qualifiers enabled</i> flag. + * + * @param value The new value. + */ + public void setPolicyQualifiersRejected(boolean value) + { + policyQualRejected = value; + } + + /** + * Returns the date for which the certificate path should be + * validated, or null if the current time should be used. The date + * object is copied to prevent subsequent modification. + * + * @return The date, or null if not set. + */ + public Date getDate() + { + return date != null ? (Date) date.clone() : null; + } + + /** + * Sets the date for which the certificate path should be validated, + * or null if the current time should be used. + * + * @param date The new date, or null. + */ + public void setDate(Date date) + { + if (date != null) + this.date = (Date) date.clone(); + else + this.date = null; + } + + /** + * Add a certificate path checker. + * + * @param checker The certificate path checker to add. + */ + public void addCertPathChecker(PKIXCertPathChecker checker) + { + if (checker != null) + pathCheckers.add(checker); + } + + /** + * Returns an immutable list of all certificate path checkers. + * + * @return An immutable list of all certificate path checkers. + */ + public List getCertPathCheckers() + { + return Collections.unmodifiableList(pathCheckers); + } + + /** + * Sets the certificate path checkers. If the argument is null, the + * list of checkers will merely be cleared. + * + * @param pathCheckers The new list of certificate path checkers. + * @throws ClassCastException If any element of <i>pathCheckers</i> is + * not a {@link PKIXCertPathChecker}. + */ + public void setCertPathCheckers(List pathCheckers) + { + this.pathCheckers.clear(); + if (pathCheckers == null) + return; + for (Iterator i = pathCheckers.iterator(); i.hasNext(); ) + { + this.pathCheckers.add((PKIXCertPathChecker) i.next()); + } + } + + /** + * Returns the signature algorithm provider, or null if not set. + * + * @return The signature algorithm provider, or null if not set. + */ + public String getSigProvider() + { + return sigProvider; + } + + /** + * Sets the signature algorithm provider, or null if there is no + * preferred provider. + * + * @param sigProvider The signature provider name. + */ + public void setSigProvider(String sigProvider) + { + this.sigProvider = sigProvider; + } + + /** + * Returns the constraints placed on the target certificate, or null + * if there are none. The target constraints are copied to prevent + * subsequent modification. + * + * @return The target constraints, or null. + */ + public CertSelector getTargetCertConstraints() + { + return targetConstraints != null + ? (CertSelector) targetConstraints.clone() : null; + } + + /** + * Sets the constraints placed on the target certificate. + * + * @param targetConstraints The target constraints. + */ + public void setTargetCertConstraints(CertSelector targetConstraints) + { + this.targetConstraints = targetConstraints != null + ? (CertSelector) targetConstraints.clone() : null; + } + + /** + * Returns a copy of these parameters. + * + * @return The copy. + */ + public Object clone() + { + return new PKIXParameters(this); + } + + /** + * Returns a printable representation of these parameters. + * + * @return A printable representation of these parameters. + */ + public String toString() { + return "[ Trust Anchors: " + trustAnchors + "; Initial Policy OIDs=" + + (initPolicies != null ? initPolicies.toString() : "any") + + "; Validity Date=" + date + "; Signature Provider=" + + sigProvider + "; Default Revocation Enabled=" + revocationEnabled + + "; Explicit Policy Required=" + exPolicyRequired + + "; Policy Mapping Inhibited=" + policyMappingInhibited + + "; Any Policy Inhibited=" + anyPolicyInhibited + + "; Policy Qualifiers Rejected=" + policyQualRejected + + "; Target Cert Contstraints=" + targetConstraints + + "; Certification Path Checkers=" + pathCheckers + + "; CertStores=" + certStores + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PolicyNode.java b/libjava/classpath/java/security/cert/PolicyNode.java new file mode 100644 index 00000000000..58d411cd3ad --- /dev/null +++ b/libjava/classpath/java/security/cert/PolicyNode.java @@ -0,0 +1,102 @@ +/* PolicyNode.java -- a single node in a policy tree + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +public interface PolicyNode +{ + + /** + * Get the iterator of the child nodes of this node. The returned + * iterator is (naturally) unmodifiable. + * + * @return An iterator over the child nodes. + */ + java.util.Iterator getChildren(); + + /** + * Get the depth of this node within the tree, starting at 0 for the + * root node. + * + * @return The depth of this node. + */ + int getDepth(); + + /** + * Returns a set of policies (string OIDs) that will satisfy this + * node's policy. The root node should always return the singleton set + * with the element "any-policy". + * + * @return The set of expected policies. + */ + java.util.Set getExpectedPolicies(); + + /** + * Returns the parent node of this node, or null if this is the root + * node. + * + * @return The parent node, or null. + */ + PolicyNode getParent(); + + /** + * Returns a set of {@link PolicyQualifierInfo} objects that qualify + * the valid policy of this node. The root node should always return + * the empty set. + * + * @return The set of {@link PolicyQualifierInfo} objects. + */ + java.util.Set getPolicyQualifiers(); + + /** + * Get the policy OID this node represents. The root node should return + * the special value "any-policy". + * + * @return The policy of this node. + */ + String getValidPolicy(); + + /** + * Return the criticality flag of this policy node. Nodes who return + * true for this method should be considered critical. The root node + * is never critical. + * + * @return The criticality flag. + */ + boolean isCritical(); +} diff --git a/libjava/classpath/java/security/cert/PolicyQualifierInfo.java b/libjava/classpath/java/security/cert/PolicyQualifierInfo.java new file mode 100644 index 00000000000..7dcf2315632 --- /dev/null +++ b/libjava/classpath/java/security/cert/PolicyQualifierInfo.java @@ -0,0 +1,168 @@ +/* PolicyQualifierInfo.java -- policy qualifier info object. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * The PolicyQualifierInfo X.509 certificate extension. + * PolicyQualifierInfo objects are represented by the ASN.1 structure: + * + * <pre> + * PolicyQualifierInfo ::= SEQUENCE { + * policyQualifierId PolicyQualifierId, + * qualifier ANY DEFINED BY policyQualifierId + * } + * + * PolicyQualifierId ::= OBJECT IDENTIFIER + * </pre> + * + * @since JDK 1.4 + */ +public final class PolicyQualifierInfo +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The <code>policyQualifierId</code> field. */ + private OID oid; + + /** The DER encoded form of this object. */ + private byte[] encoded; + + /** The DER encoded form of the <code>qualifier</code> field. */ + private DERValue qualifier; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new PolicyQualifierInfo object from the DER encoded form + * passed in the byte array. The argument is copied. + * + * <p>The ASN.1 form of PolicyQualifierInfo is: +<pre> +PolicyQualifierInfo ::= SEQUENCE { + policyQualifierId PolicyQualifierId, + qualifier ANY DEFINED BY policyQualifierId +} + +PolicyQualifierId ::= OBJECT IDENTIFIER +</pre> + * + * @param encoded The DER encoded form. + * @throws IOException If the structure cannot be parsed from the + * encoded bytes. + */ + public PolicyQualifierInfo(byte[] encoded) throws IOException + { + if (encoded == null) + throw new IOException("null bytes"); + this.encoded = (byte[]) encoded.clone(); + DERReader in = new DERReader(new ByteArrayInputStream(this.encoded)); + DERValue qualInfo = in.read(); + if (!qualInfo.isConstructed()) + throw new ASN1ParsingException("malformed PolicyQualifierInfo"); + DERValue val = in.read(); + if (!(val.getValue() instanceof OID)) + throw new ASN1ParsingException("value read not an OBJECT IDENTIFIER"); + oid = (OID) val.getValue(); + if (val.getEncodedLength() < val.getLength()) + qualifier = in.read(); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the <code>policyQualifierId</code> field of this structure, + * as a dotted-decimal representation of the object identifier. + * + * @return This structure's OID field. + */ + public String getPolicyQualifierId() + { + return oid.toString(); + } + + /** + * Returns the DER encoded form of this object; the contents of the + * returned byte array are equivalent to those that were passed to the + * constructor. The byte array is cloned every time this method is + * called. + * + * @return The encoded form. + */ + public byte[] getEncoded() + { + return (byte[]) encoded.clone(); + } + + /** + * Get the <code>qualifier</code> field of this object, as a DER + * encoded byte array. The byte array returned is cloned every time + * this method is called. + * + * @return The encoded qualifier. + */ + public byte[] getPolicyQualifier() + { + if (qualifier == null) + return new byte[0]; + return qualifier.getEncoded(); + } + + /** + * Returns a printable string representation of this object. + * + * @return The string representation. + */ + public String toString() + { + return "PolicyQualifierInfo { policyQualifierId ::= " + oid + + ", qualifier ::= " + qualifier + " }"; + } +} diff --git a/libjava/classpath/java/security/cert/TrustAnchor.java b/libjava/classpath/java/security/cert/TrustAnchor.java new file mode 100644 index 00000000000..2110ed5181d --- /dev/null +++ b/libjava/classpath/java/security/cert/TrustAnchor.java @@ -0,0 +1,185 @@ +/* TrustAnchor.java -- an ultimately-trusted certificate. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.x509.X500DistinguishedName; + +import java.security.PublicKey; + +/** + * An ultimately-trusted certificate to serve as the root of a + * certificate chain. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class TrustAnchor +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The certificate authority's distinguished name. */ + private final X500DistinguishedName caName; + + /** The certficate authority's public key. */ + private final PublicKey caKey; + + /** The certficate authority's certificate. */ + private final X509Certificate trustedCert; + + /** The encoded name constraints bytes. */ + private final byte[] nameConstraints; + + // Constnuctors. + // ------------------------------------------------------------------------ + + /** + * Create a new trust anchor from a certificate and (optional) name + * constraints. + * + * <p>If the <i>nameConstraints</i> argument in non-null, it will be + * copied to prevent modification. + * + * @param trustedCert The trusted certificate. + * @param nameConstraints The encoded nameConstraints. + */ + public TrustAnchor(X509Certificate trustedCert, byte[] nameConstraints) + { + if (trustedCert == null) + throw new NullPointerException(); + this.trustedCert = trustedCert; + caName = null; + caKey = null; + if (nameConstraints != null) + this.nameConstraints = (byte[]) nameConstraints.clone(); + else + this.nameConstraints = null; + } + + /** + * Create a new trust anchor from a certificate authority's + * distinguished name, public key, and (optional) name constraints. + * + * <p>If the <i>nameConstraints</i> argument in non-null, it will be + * copied to prevent modification. + * + * @params caName The CA's distinguished name. + * @params caKey The CA's public key. + * @params nameConstraints The encoded nameConstraints. + */ + public TrustAnchor(String caName, PublicKey caKey, byte[] nameConstraints) + { + if (caName == null || caKey == null) + throw new NullPointerException(); + if (caName.length() == 0) + throw new IllegalArgumentException(); + trustedCert = null; + this.caName = new X500DistinguishedName(caName); + this.caKey = caKey; + if (nameConstraints != null) + this.nameConstraints = (byte[]) nameConstraints.clone(); + else + this.nameConstraints = null; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the trusted certificate, or null if none was specified. + * + * @return The trusted certificate. + */ + public final X509Certificate getTrustedCert() + { + return trustedCert; + } + + /** + * Return the certificate authority's distinguished name, or null if + * none was specified. + * + * @return The CA's distinguished name. + */ + public final String getCAName() + { + if (caName != null) + return caName.toString(); + return null; + } + + /** + * Return the certificate authority's public key, or null if none was + * specified. + * + * @return The CA's public key. + */ + public final PublicKey getCAPublicKey() + { + return caKey; + } + + /** + * Return the encoded name constraints, or null if none was specified. + * + * <p>The name constraints byte array is copied when this method is + * called to prevent modification. + * + * @return The encoded name constraints. + */ + public final byte[] getNameConstraints() + { + if (nameConstraints == null) + return null; + return (byte[]) nameConstraints.clone(); + } + + /** + * Return a printable representation of this trust anchor. + * + * @return The printable representation. + */ + public String toString() + { + if (trustedCert == null) + return "[ Trusted CA Public Key=" + caKey + ", Trusted CA Issuer Name=" + + caName.toString() + " ]"; + return "[ Trusted CA Certificate=" + trustedCert + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/X509CRL.java b/libjava/classpath/java/security/cert/X509CRL.java new file mode 100644 index 00000000000..5657b3eb3f5 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRL.java @@ -0,0 +1,397 @@ +/* X509CRL.java --- X.509 Certificate Revocation List + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.util.Date; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + The X509CRL class is the abstract class used to manage + X.509 Certificate Revocation Lists. The CRL is a list of + time stamped entries which indicate which lists have been + revoked. The list is signed by a Certificate Authority (CA) + and made publically available in a repository. + + Each revoked certificate in the CRL is identified by its + certificate serial number. When a piece of code uses a + certificate, the certificates validity is checked by + validating its signature and determing that it is not + only a recently acquired CRL. The recently aquired CRL + is depends on the local policy in affect. The CA issues + a new CRL periodically and entries are removed as the + certificate expiration date is reached + + + A description of the X.509 v2 CRL follows below from rfc2459. + + "The X.509 v2 CRL syntax is as follows. For signature calculation, + the data that is to be signed is ASN.1 DER encoded. ASN.1 DER + encoding is a tag, length, value encoding system for each element. + + CertificateList ::= SEQUENCE { + tbsCertList TBSCertList, + signatureAlgorithm AlgorithmIdentifier, + signatureValue BIT STRING } + + TBSCertList ::= SEQUENCE { + version Version OPTIONAL, + -- if present, shall be v2 + signature AlgorithmIdentifier, + issuer Name, + thisUpdate Time, + nextUpdate Time OPTIONAL, + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + crlExtensions [0] EXPLICIT Extensions OPTIONAL + -- if present, shall be v2 + }" + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public abstract class X509CRL extends CRL implements X509Extension +{ + + /** + Constructs a new X509CRL. + */ + protected X509CRL() + { + super("X.509"); + } + + /** + Compares this X509CRL to other. It checks if the + object if instanceOf X509CRL and then checks if + the encoded form matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof X509CRL ) { + try { + X509CRL x = (X509CRL) other; + if( getEncoded().length != x.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = x.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CRLException crle ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this X509CRL in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this X.509 CRL. + + @return byte array containg encoded form + + @throws CRLException if an error occurs + */ + public abstract byte[] getEncoded() throws CRLException; + + /** + Verifies that this CRL was properly signed with the + PublicKey that corresponds to its private key. + + @param key PublicKey to verify with + + @throws CRLException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException no provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key) + throws CRLException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Verifies that this CRL was properly signed with the + PublicKey that corresponds to its private key and uses + the signature engine provided by the provider. + + @param key PublicKey to verify with + @param sigProvider Provider to use for signature algorithm + + @throws CRLException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException incorrect provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key, + String sigProvider) + throws CRLException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Gets the version of this CRL. + + The ASN.1 encoding is: + + version Version OPTIONAL, + -- if present, shall be v2 + + Version ::= INTEGER { v1(0), v2(1), v3(2) } + + Consult rfc2459 for more information. + + @return the version number, Ex: 1 or 2 + */ + public abstract int getVersion(); + + /** + Returns the issuer (issuer distinguished name) of the CRL. + The issuer is the entity who signed and issued the + Certificate Revocation List. + + The ASN.1 DER encoding is: + + issuer Name, + + Name ::= CHOICE { + RDNSequence } + + RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + + RelativeDistinguishedName ::= + SET OF AttributeTypeAndValue + + AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + + AttributeType ::= OBJECT IDENTIFIER + + AttributeValue ::= ANY DEFINED BY AttributeType + + DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1.. MAX)), + bmpString BMPString (SIZE (1..MAX)) } + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getIssuerDN(); + + /** + Returns the thisUpdate date of the CRL. + + The ASN.1 DER encoding is: + + thisUpdate Time, + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the thisUpdate date + */ + public abstract Date getThisUpdate(); + + /* + Gets the nextUpdate field + + The ASN.1 DER encoding is: + + nextUpdate Time OPTIONAL, + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the nextUpdate date + */ + public abstract Date getNextUpdate(); + + /** + Gets the requeste dX509Entry for the specified + certificate serial number. + + @return a X509CRLEntry representing the X.509 CRL entry + */ + public abstract X509CRLEntry getRevokedCertificate(BigInteger serialNumber); + + /** + Returns a Set of revoked certificates. + + @return a set of revoked certificates. + */ + public abstract Set getRevokedCertificates(); + + /** + Returns the DER ASN.1 encoded tbsCertList which is + the basic information of the list and associated certificates + in the encoded state. See top for more information. + + The ASN.1 DER encoding is: + + tbsCertList TBSCertList, + + Consult rfc2459 for more information. + + @return byte array representing tbsCertList + */ + public abstract byte[] getTBSCertList() throws CRLException; + + + /** + Returns the signature for the CRL. + + The ASN.1 DER encoding is: + + signatureValue BIT STRING + + Consult rfc2459 for more information. + */ + public abstract byte[] getSignature(); + + /** + Returns the signature algorithm used to sign the CRL. + An examples is "SHA-1/DSA". + + The ASN.1 DER encoding is: + + signatureAlgorithm AlgorithmIdentifier, + + AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + Consult rfc2459 for more information. + + The algorithm name is determined from the OID. + + @return a string with the signature algorithm name + */ + public abstract String getSigAlgName(); + + /** + Returns the OID for the signature algorithm used. + Example "1.2.840.10040.4.3" is return for SHA-1 with DSA.\ + + The ASN.1 DER encoding for the example is: + + id-dsa-with-sha1 ID ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + Consult rfc2459 for more information. + + @return a string containing the OID. + */ + public abstract String getSigAlgOID(); + + /** + Returns the AlgorithmParameters in the encoded form + for the signature algorithm used. + + If access to the parameters is need, create an + instance of AlgorithmParameters. + + @return byte array containing algorithm parameters, null + if no parameters are present in CRL + */ + public abstract byte[] getSigAlgParams(); + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the X.500 distinguished name of this CRL's issuer. + * + * @return The issuer's X.500 distinguished name. + * @since JDK 1.4 + */ + public X500Principal getIssuerX500Principal() + { + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/security/cert/X509CRLEntry.java b/libjava/classpath/java/security/cert/X509CRLEntry.java new file mode 100644 index 00000000000..4c9cada4747 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRLEntry.java @@ -0,0 +1,169 @@ +/* X509CRLEntry.java --- X.509 Certificate Revocation List Entry + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.util.Date; + +/** + Abstract class for entries in the CRL (Certificate Revocation + List). The ASN.1 definition for <I>revokedCertificates</I> is + + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + + CertificateSerialNumber ::= INTEGER + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + + Extension ::= SEQUENCE { + extnID OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING } + + For more information consult rfc2459. + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public abstract class X509CRLEntry implements X509Extension +{ + + /** + Creates a new X509CRLEntry + */ + public X509CRLEntry() + {} + + /** + Compares this X509CRLEntry to other. It checks if the + object if instanceOf X509CRLEntry and then checks if + the encoded form( the inner SEQUENCE) matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof X509CRLEntry ) { + try { + X509CRLEntry xe = (X509CRLEntry) other; + if( getEncoded().length != xe.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = xe.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CRLException crle ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this X509CRLEntry in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this CRL Entry, + the inner SEQUENCE. + + @return byte array containg encoded form + + @throws CRLException if an error occurs + */ + public abstract byte[] getEncoded() throws CRLException; + + /** + Gets the serial number for <I>userCertificate</I> in + this X509CRLEntry. + + @return the serial number for this X509CRLEntry. + */ + public abstract BigInteger getSerialNumber(); + + + /** + Gets the revocation date in <I>revocationDate</I> for + this X509CRLEntry. + + @return the revocation date for this X509CRLEntry. + */ + public abstract Date getRevocationDate(); + + + /** + Checks if this X509CRLEntry has extensions. + + @return true if it has extensions, false otherwise + */ + public abstract boolean hasExtensions(); + + + /** + Returns a string that represents this X509CRLEntry. + + @return a string representing this X509CRLEntry. + */ + public abstract String toString(); + +} diff --git a/libjava/classpath/java/security/cert/X509CRLSelector.java b/libjava/classpath/java/security/cert/X509CRLSelector.java new file mode 100644 index 00000000000..3c79fba9cb8 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRLSelector.java @@ -0,0 +1,440 @@ +/* X509CRLSelector.java -- selects X.509 CRLs by criteria. + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.classpath.SystemProperties; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +/** + * A class for matching X.509 certificate revocation lists by criteria. + * + * <p>Use of this class requires extensive knowledge of the Internet + * Engineering Task Force's Public Key Infrastructure (X.509). The primary + * document describing this standard is <a + * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509 + * Public Key Infrastructure Certificate and Certificate Revocation List + * (CRL) Profile</a>. + * + * <p>Note that this class is not thread-safe. If multiple threads will + * use or modify this class then they need to synchronize on the object. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class X509CRLSelector implements CRLSelector, Cloneable +{ + + // Fields. + // ------------------------------------------------------------------------- + + private static final String CRL_NUMBER_ID = "2.5.29.20"; + + private List issuerNames; + private BigInteger maxCrlNumber; + private BigInteger minCrlNumber; + private Date date; + private X509Certificate cert; + + // Constructor. + // ------------------------------------------------------------------------- + + /** + * Creates a new CRL selector with no criteria enabled; i.e., every CRL + * will be matched. + */ + public X509CRLSelector() + { + } + + // Instance methods. + // ------------------------------------------------------------------------- + + /** + * Add an issuer name to the set of issuer names criteria, as the DER + * encoded form. + * + * @param name The name to add, as DER bytes. + * @throws IOException If the argument is not a valid DER-encoding. + */ + public void addIssuerName(byte[] name) throws IOException + { + X500Principal p = null; + try + { + p = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + if (issuerNames == null) + issuerNames = new LinkedList(); + issuerNames.add(p); + } + + /** + * Add an issuer name to the set of issuer names criteria, as a + * String representation. + * + * @param name The name to add. + * @throws IOException If the argument is not a valid name. + */ + public void addIssuerName(String name) throws IOException + { + X500Principal p = null; + try + { + p = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name: " + name); + ioe.initCause(iae); + throw ioe; + } + if (issuerNames == null) + issuerNames = new LinkedList(); + issuerNames.add(p); + } + + /** + * Sets the issuer names criterion. Pass <code>null</code> to clear this + * value. CRLs matched by this selector must have an issuer name in this + * set. + * + * @param names The issuer names. + * @throws IOException If any of the elements in the collection is not + * a valid name. + */ + public void setIssuerNames(Collection names) throws IOException + { + if (names == null) + { + issuerNames = null; + return; + } + List l = new ArrayList(names.size()); + for (Iterator it = names.iterator(); it.hasNext(); ) + { + Object o = it.next(); + if (o instanceof X500Principal) + l.add(o); + else if (o instanceof String) + { + try + { + l.add(new X500Principal((String) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name: " + o); + ioe.initCause(iae); + throw ioe; + } + } + else if (o instanceof byte[]) + { + try + { + l.add(new X500Principal((byte[]) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + } + else if (o instanceof InputStream) + { + try + { + l.add(new X500Principal((InputStream) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + } + else + throw new IOException("not a valid name: " + + (o != null ? o.getClass().getName() : "null")); + + } + issuerNames = l; + } + + /** + * Returns the set of issuer names that are matched by this selector, + * or <code>null</code> if this criteria is not set. The returned + * collection is not modifiable. + * + * @return The set of issuer names. + */ + public Collection getIssuerNames() + { + if (issuerNames != null) + return Collections.unmodifiableList(issuerNames); + else + return null; + } + + /** + * Returns the maximum value of the CRLNumber extension present in + * CRLs matched by this selector, or <code>null</code> if this + * criteria is not set. + * + * @return The maximum CRL number. + */ + public BigInteger getMaxCRL() + { + return maxCrlNumber; + } + + /** + * Returns the minimum value of the CRLNumber extension present in + * CRLs matched by this selector, or <code>null</code> if this + * criteria is not set. + * + * @return The minimum CRL number. + */ + public BigInteger getMinCRL() + { + return minCrlNumber; + } + + /** + * Sets the maximum value of the CRLNumber extension present in CRLs + * matched by this selector. Specify <code>null</code> to clear this + * criterion. + * + * @param maxCrlNumber The maximum CRL number. + */ + public void setMaxCRLNumber(BigInteger maxCrlNumber) + { + this.maxCrlNumber = maxCrlNumber; + } + + /** + * Sets the minimum value of the CRLNumber extension present in CRLs + * matched by this selector. Specify <code>null</code> to clear this + * criterion. + * + * @param minCrlNumber The minimum CRL number. + */ + public void setMinCRLNumber(BigInteger minCrlNumber) + { + this.minCrlNumber = minCrlNumber; + } + + /** + * Returns the date when this CRL must be valid; that is, the date + * must be after the thisUpdate date, but before the nextUpdate date. + * Returns <code>null</code> if this criterion is not set. + * + * @return The date. + */ + public Date getDateAndTime() + { + return date != null ? (Date) date.clone() : null; + } + + /** + * Sets the date at which this CRL must be valid. Specify + * <code>null</code> to clear this criterion. + * + * @param date The date. + */ + public void setDateAndTime(Date date) + { + this.date = date != null ? (Date) date.clone() : null; + } + + /** + * Returns the certificate being checked, or <code>null</code> if this + * value is not set. + * + * @return The certificate. + */ + public X509Certificate getCertificateChecking() + { + return cert; + } + + /** + * Sets the certificate being checked. This is not a criterion, but + * info used by certificate store implementations to aid in searching. + * + * @param cert The certificate. + */ + public void setCertificateChecking(X509Certificate cert) + { + this.cert = cert; + } + + /** + * Returns a string representation of this selector. The string will + * only describe the enabled criteria, so if none are enabled this will + * return a string that contains little else besides the class name. + * + * @return The string. + */ + public String toString() + { + StringBuffer str = new StringBuffer(X509CRLSelector.class.getName()); + String nl = SystemProperties.getProperty("line.separator"); + String eol = ";" + nl; + + str.append(" {").append(nl); + if (issuerNames != null) + str.append(" issuer names = ").append(issuerNames).append(eol); + if (maxCrlNumber != null) + str.append(" max CRL = ").append(maxCrlNumber).append(eol); + if (minCrlNumber != null) + str.append(" min CRL = ").append(minCrlNumber).append(eol); + if (date != null) + str.append(" date = ").append(date).append(eol); + if (cert != null) + str.append(" certificate = ").append(cert).append(eol); + str.append("}").append(nl); + return str.toString(); + } + + /** + * Checks a CRL against the criteria of this selector, returning + * <code>true</code> if the given CRL matches all the criteria. + * + * @param _crl The CRL being checked. + * @return True if the CRL matches, false otherwise. + */ + public boolean match(CRL _crl) + { + if (!(_crl instanceof X509CRL)) + return false; + X509CRL crl = (X509CRL) _crl; + if (issuerNames != null) + { + if (!issuerNames.contains(crl.getIssuerX500Principal())) + return false; + } + BigInteger crlNumber = null; + if (maxCrlNumber != null) + { + byte[] b = crl.getExtensionValue(CRL_NUMBER_ID); + if (b == null) + return false; + try + { + DERValue val = DERReader.read(b); + if (!(val.getValue() instanceof BigInteger)) + return false; + crlNumber = (BigInteger) val.getValue(); + } + catch (IOException ioe) + { + return false; + } + if (maxCrlNumber.compareTo(crlNumber) < 0) + return false; + } + if (minCrlNumber != null) + { + if (crlNumber == null) + { + byte[] b = crl.getExtensionValue(CRL_NUMBER_ID); + if (b == null) + return false; + try + { + DERValue val = DERReader.read(b); + if (!(val.getValue() instanceof BigInteger)) + return false; + crlNumber = (BigInteger) val.getValue(); + } + catch (IOException ioe) + { + return false; + } + } + if (minCrlNumber.compareTo(crlNumber) > 0) + return false; + } + if (date != null) + { + if (date.compareTo(crl.getThisUpdate()) < 0 || + date.compareTo(crl.getNextUpdate()) > 0) + return false; + } + return true; + } + + /** + * Returns a copy of this object. + * + * @return The copy. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } + } +} diff --git a/libjava/classpath/java/security/cert/X509CertSelector.java b/libjava/classpath/java/security/cert/X509CertSelector.java new file mode 100644 index 00000000000..4149a37643d --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CertSelector.java @@ -0,0 +1,1106 @@ +/* X509CertSelector.java -- selects X.509 certificates by criteria. + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.classpath.SystemProperties; +import gnu.java.security.OID; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + * A concrete implementation of {@link CertSelector} for X.509 certificates, + * which allows a number of criteria to be set when accepting certificates, + * from validity dates, to issuer and subject distinguished names, to some + * of the various X.509 extensions. + * + * <p>Use of this class requires extensive knowledge of the Internet + * Engineering Task Force's Public Key Infrastructure (X.509). The primary + * document describing this standard is <a + * href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509 + * Public Key Infrastructure Certificate and Certificate Revocation List + * (CRL) Profile</a>. + * + * <p>Note that this class is not thread-safe. If multiple threads will + * use or modify this class then they need to synchronize on the object. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class X509CertSelector implements CertSelector, Cloneable +{ + + // Constants and fields. + // ------------------------------------------------------------------------- + + private static final String AUTH_KEY_ID = "2.5.29.35"; + private static final String SUBJECT_KEY_ID = "2.5.29.14"; + private static final String NAME_CONSTRAINTS_ID = "2.5.29.30"; + + private int basicConstraints; + private X509Certificate cert; + private BigInteger serialNo; + private X500Principal issuer; + private X500Principal subject; + private byte[] subjectKeyId; + private byte[] authKeyId; + private boolean[] keyUsage; + private Date certValid; + private OID sigId; + private PublicKey subjectKey; + private X509EncodedKeySpec subjectKeySpec; + private Set keyPurposeSet; + private List altNames; + private boolean matchAllNames; + private byte[] nameConstraints; + private Set policy; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new X.509 certificate selector. The new selector will be + * empty, and will accept any certificate (provided that it is an + * {@link X509Certificate}). + */ + public X509CertSelector() + { + basicConstraints = -1; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the certificate criterion, or <code>null</code> if this value + * was not set. + * + * @return The certificate. + */ + public X509Certificate getCertificate() + { + return cert; + } + + /** + * Sets the certificate criterion. If set, only certificates that are + * equal to the certificate passed here will be accepted. + * + * @param cert The certificate. + */ + public void setCertificate(X509Certificate cert) + { + this.cert = cert; + } + + /** + * Returns the serial number criterion, or <code>null</code> if this + * value was not set. + * + * @return The serial number. + */ + public BigInteger getSerialNumber() + { + return serialNo; + } + + /** + * Sets the serial number of the desired certificate. Only certificates that + * contain this serial number are accepted. + * + * @param serialNo The serial number. + */ + public void setSerialNumber(BigInteger serialNo) + { + this.serialNo = serialNo; + } + + /** + * Returns the issuer criterion as a string, or <code>null</code> if this + * value was not set. + * + * @return The issuer. + */ + public String getIssuerAsString() + { + if (issuer != null) + return issuer.getName(); + else + return null; + } + + /** + * Returns the issuer criterion as a sequence of DER bytes, or + * <code>null</code> if this value was not set. + * + * @return The issuer. + */ + public byte[] getIssuerAsBytes() throws IOException + { + if (issuer != null) + return issuer.getEncoded(); + else + return null; + } + + /** + * Sets the issuer, specified as a string representation of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. + * + * @param name The string representation of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setIssuer(String name) throws IOException + { + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; + } + + /** + * Sets the issuer, specified as the DER encoding of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. + * + * @param name The DER encoding of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setIssuer(byte[] name) throws IOException + { + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; + } + + /** + * Returns the subject criterion as a string, of <code>null</code> if + * this value was not set. + * + * @return The subject. + */ + public String getSubjectAsString() + { + if (subject != null) + return subject.getName(); + else + return null; + } + + /** + * Returns the subject criterion as a sequence of DER bytes, or + * <code>null</code> if this value is not set. + * + * @return The subject. + */ + public byte[] getSubjectAsBytes() throws IOException + { + if (subject != null) + return subject.getEncoded(); + else + return null; + } + + /** + * Sets the subject, specified as a string representation of the + * subject's distinguished name. Only certificates with the given + * subject will be accepted. + * + * @param name The string representation of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(String name) throws IOException + { + if (name != null) + { + try + { + subject = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + subject = null; + } + + /** + * Sets the subject, specified as the DER encoding of the subject's + * distinguished name. Only certificates with the given subject will + * be accepted. + * + * @param name The DER encoding of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(byte[] name) throws IOException + { + if (name != null) + { + try + { + subject = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + subject = null; + } + + /** + * Returns the subject key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The subject key identifier. + */ + public byte[] getSubjectKeyIdentifier() + { + if (subjectKeyId != null) + return (byte[]) subjectKeyId.clone(); + else + return null; + } + + /** + * Sets the subject key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param subjectKeyId The subject key identifier. + */ + public void setSubjectKeyIdentifier(byte[] subjectKeyId) + { + this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : + null; + } + + /** + * Returns the authority key identifier criterion, or <code>null</code> if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The authority key identifier. + */ + public byte[] getAuthorityKeyIdentifier() + { + if (authKeyId != null) + return (byte[]) authKeyId.clone(); + else + return null; + } + + /** + * Sets the authority key identifier criterion, or <code>null</code> to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param subjectKeyId The subject key identifier. + */ + public void setAuthorityKeyIdentifier(byte[] authKeyId) + { + this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; + } + + /** + * Returns the date at which certificates must be valid, or <code>null</code> + * if this criterion was not set. + * + * @return The target certificate valitity date. + */ + public Date getCertificateValid() + { + if (certValid != null) + return (Date) certValid.clone(); + else + return null; + } + + /** + * Sets the date at which certificates must be valid. Specify + * <code>null</code> to clear this criterion. + * + * @param certValid The certificate validity date. + */ + public void setCertificateValid(Date certValid) + { + this.certValid = certValid != null ? (Date) certValid.clone() : null; + } + + /** + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. + * + * <p>Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation and simply + * returns <code>null</code>. + * + * @return Null. + */ + public Date getPrivateKeyValid() + { + return null; + } + + /** + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. + * + * <p>Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation. + * + * @param UNUSED Is silently ignored. + */ + public void setPrivateKeyValid(Date UNUSED) + { + } + + /** + * Returns the public key algorithm ID that matching certificates must have, + * or <code>null</code> if this criterion was not set. + * + * @return The public key algorithm ID. + */ + public String getSubjectPublicKeyAlgID() + { + return String.valueOf(sigId); + } + + /** + * Sets the public key algorithm ID that matching certificates must have. + * Specify <code>null</code> to clear this criterion. + * + * @param sigId The public key ID. + * @throws IOException If the specified ID is not a valid object identifier. + */ + public void setSubjectPublicKeyAlgID(String sigId) throws IOException + { + if (sigId != null) + { + try + { + OID oid = new OID(sigId); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + sigId); + this.sigId = oid; + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed OID: " + sigId); + ioe.initCause(iae); + throw ioe; + } + } + else + this.sigId = null; + } + + /** + * Returns the subject public key criterion, or <code>null</code> if this + * value is not set. + * + * @return The subject public key. + */ + public PublicKey getSubjectPublicKey() + { + return subjectKey; + } + + /** + * Sets the subject public key criterion as an opaque representation. + * Specify <code>null</code> to clear this criterion. + * + * @param key The public key. + */ + public void setSubjectPublicKey(PublicKey key) + { + this.subjectKey = key; + if (key == null) + { + subjectKeySpec = null; + return; + } + try + { + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKeySpec = (X509EncodedKeySpec) + enc.getKeySpec(key, X509EncodedKeySpec.class); + } + catch (Exception x) + { + subjectKey = null; + subjectKeySpec = null; + } + } + + /** + * Sets the subject public key criterion as a DER-encoded key. Specify + * <code>null</code> to clear this value. + * + * @param key The DER-encoded key bytes. + * @throws IOException If the argument is not a valid DER-encoded key. + */ + public void setSubjectPublicKey(byte[] key) throws IOException + { + if (key == null) + { + subjectKey = null; + subjectKeySpec = null; + return; + } + try + { + subjectKeySpec = new X509EncodedKeySpec(key); + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKey = enc.generatePublic(subjectKeySpec); + } + catch (Exception x) + { + subjectKey = null; + subjectKeySpec = null; + IOException ioe = new IOException(x.getMessage()); + ioe.initCause(x); + throw ioe; + } + } + + /** + * Returns the public key usage criterion, or <code>null</code> if this + * value is not set. Note that the array is cloned to prevent modification. + * + * @return The public key usage. + */ + public boolean[] getKeyUsage() + { + if (keyUsage != null) + return (boolean[]) keyUsage.clone(); + else + return null; + } + + /** + * Sets the public key usage criterion. Specify <code>null</code> to clear + * this value. + * + * @param keyUsage The public key usage. + */ + public void setKeyUsage(boolean[] keyUsage) + { + this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; + } + + /** + * Returns the set of extended key purpose IDs, as an unmodifiable set + * of OID strings. Returns <code>null</code> if this criterion is not + * set. + * + * @return The set of key purpose OIDs (strings). + */ + public Set getExtendedKeyUsage() + { + if (keyPurposeSet != null) + return Collections.unmodifiableSet(keyPurposeSet); + else + return null; + } + + /** + * Sets the extended key usage criterion, as a set of OID strings. Specify + * <code>null</code> to clear this value. + * + * @param keyPurposeSet The set of key purpose OIDs. + * @throws IOException If any element of the set is not a valid OID string. + */ + public void setExtendedKeyUsage(Set keyPurposeSet) throws IOException + { + if (keyPurposeSet == null) + { + this.keyPurposeSet = null; + return; + } + Set s = new HashSet(); + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + Object o = it.next(); + if (!(o instanceof String)) + throw new IOException("not a string: " + o); + try + { + OID oid = new OID((String) o); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + o); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed OID: " + o); + ioe.initCause(iae); + throw ioe; + } + } + this.keyPurposeSet = s; + } + + /** + * Returns whether or not all specified alternative names must match. + * If false, a certificate is considered a match if <em>one</em> of the + * specified alternative names matches. + * + * @return true if all names must match. + */ + public boolean getMatchAllSubjectAltNames() + { + return matchAllNames; + } + + /** + * Sets whether or not all subject alternative names must be matched. + * If false, then a certificate will be considered a match if one + * alternative name matches. + * + * @param matchAllNames Whether or not all alternative names must be + * matched. + */ + public void setMatchAllSubjectAltNames(boolean matchAllNames) + { + this.matchAllNames = matchAllNames; + } + + /** + * Sets the subject alternative names critertion. Each element of the + * argument must be a {@link java.util.List} that contains exactly two + * elements: the first an {@link Integer}, representing the type of + * name, and the second either a {@link String} or a byte array, + * representing the name itself. + * + * @param altNames The alternative names. + * @throws IOException If any element of the argument is invalid. + */ + public void setSubjectAlternativeNames(Collection altNames) + throws IOException + { + if (altNames == null) + { + this.altNames = null; + return; + } + List l = new ArrayList(altNames.size()); + for (Iterator it = altNames.iterator(); it.hasNext(); ) + { + Object o = it.next(); + if (!(o instanceof List) || ((List) o).size() != 2 || + !(((List) o).get(0) instanceof Integer) || + !(((List) o).get(1) instanceof String) || + !(((List) o).get(1) instanceof byte[])) + throw new IOException("illegal alternative name: " + o); + Integer i = (Integer) ((List) o).get(0); + if (i.intValue() < 0 || i.intValue() > 8) + throw new IOException("illegal alternative name: " + o + + ", bad id: " + i); + l.add(new ArrayList((List) o)); + } + this.altNames = l; + } + + /** + * Add a name to the subject alternative names criterion. + * + * @param id The type of name this is. Must be in the range [0,8]. + * @param name The name. + * @throws IOException If the id is out of range, or if the name + * is null. + */ + public void addSubjectAlternativeName(int id, String name) + throws IOException + { + if (id < 0 || id > 8 || name == null) + throw new IOException("illegal alternative name"); + if (altNames == null) + altNames = new LinkedList(); + ArrayList l = new ArrayList(2); + l.add(new Integer(id)); + l.add(name); + altNames.add(l); + } + + /** + * Add a name, as DER-encoded bytes, to the subject alternative names + * criterion. + * + * @param id The type of name this is. + */ + public void addSubjectAlternativeName(int id, byte[] name) + throws IOException + { + if (id < 0 || id > 8 || name == null) + throw new IOException("illegal alternative name"); + if (altNames == null) + altNames = new LinkedList(); + ArrayList l = new ArrayList(2); + l.add(new Integer(id)); + l.add(name); + altNames.add(l); + } + + /** + * Returns the name constraints criterion, or <code>null</code> if this + * value is not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The name constraints. + */ + public byte[] getNameConstraints() + { + if (nameConstraints != null) + return (byte[]) nameConstraints.clone(); + else + return null; + } + + /** + * Sets the name constraints criterion; specify <code>null</code> to + * clear this criterion. Note that if non-null, the argument will be + * cloned to prevent modification. + * + * @param nameConstraints The new name constraints. + * @throws IOException If the argument is not a valid DER-encoded + * name constraints. + */ + public void setNameConstraints(byte[] nameConstraints) + throws IOException + { + // FIXME check if the argument is valid. + this.nameConstraints = nameConstraints != null + ? (byte[]) nameConstraints.clone() : null; + } + + /** + * Returns the basic constraints criterion, or -1 if this value is not set. + * + * @return The basic constraints. + */ + public int getBasicConstraints() + { + return basicConstraints; + } + + /** + * Sets the basic constraints criterion. Specify -1 to clear this parameter. + * + * @param basicConstraints The new basic constraints value. + */ + public void setBasicConstraints(int basicConstraints) + { + if (basicConstraints < -1) + basicConstraints = -1; + this.basicConstraints = basicConstraints; + } + + // The last two criteria not yet implemented are certificate policies + // and path-to-names. Both of these are somewhat advanced extensions + // (you could probably count the applications that actually use them + // on one hand), and they both have no support in the X509Certificate + // class. + // + // Not having support in X509Certificate is not always a problem; for + // example, we can compare DER-encoded values as byte arrays for some + // extensions. We can't, however, compare them if they are specified + // in a set (as policies are). We need to parse the actual value in the + // certificate, and check it against the specified set. + + // FIXME +// public void setPolicy(Set policy) throws IOException +// { +// if (policy != null) +// { +// for (Iterator it = policy.iterator(); it.hasNext(); ) +// try +// { +// OID oid = new OID((String) it.next()); +// int[] i = oid.getIDs(); +// if (!checkOid(i)) +// throw new IOException("invalid OID"); +// } +// catch (Exception x) +// { +// throw new IOException("invalid OID"); +// } +// } +// this.policy = policy != null ? new HashSet(policy) : null; +// } + + // FIXME +// public void setPathToNames(Collection names) throws IOException +// { +// if (names == null) +// { +// this.names = null; +// return; +// } +// for (Iterator it = names.iterator(); it.hasNext(); ) +// { +// try +// { +// List l = (List) it.next(); +// if (l.get(1) instanceof String) +// addPathToName(((Integer)l.get(0)).intValue(), (String)l.get(1)); +// else +// addPathToName(((Integer)l.get(0)).intValue(), (byte[])l.get(1)); +// } +// catch (Exception x) +// { +// this.names = null; +// throw new IOException("invalid names"); +// } +// } +// } + + // FIXME +// public void addPathToName(int id, String name) throws IOException +// { +// } + + // FIXME +// public void addPathToName(int id, byte[] name) throws IOException +// { +// } + + // FIXME +// public Collection getSubjectAlternativeNames() +// { +// return null; +// } + + // FIXME +// public Set getPolicy() +// { +// return null; +// } + + // FIXME +// public Collection getPathToNames() +// { +// return null; +// } + + /** + * Match a certificate. This method will check the given certificate + * against all the enabled criteria of this selector, and will return + * <code>true</code> if the given certificate matches. + * + * @param certificate The certificate to check. + * @return true if the certificate matches all criteria. + */ + public boolean match(Certificate certificate) + { + if (!(certificate instanceof X509Certificate)) + return false; + X509Certificate cert = (X509Certificate) certificate; + if (this.cert != null) + { + try + { + byte[] e1 = this.cert.getEncoded(); + byte[] e2 = cert.getEncoded(); + if (!Arrays.equals(e1, e2)) + return false; + } + catch (CertificateEncodingException cee) + { + return false; + } + } + if (serialNo != null) + { + if (!serialNo.equals(cert.getSerialNumber())) + return false; + } + if (certValid != null) + { + try + { + cert.checkValidity(certValid); + } + catch (CertificateException ce) + { + return false; + } + } + if (issuer != null) + { + if (!issuer.equals(cert.getIssuerX500Principal())) + return false; + } + if (subject != null) + { + if (!subject.equals(cert.getSubjectX500Principal())) + return false; + } + if (sigId != null) + { + if (!sigId.equals(cert.getSigAlgOID())) + return false; + } + if (subjectKeyId != null) + { + byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); + if (!Arrays.equals(b, subjectKeyId)) + return false; + } + if (authKeyId != null) + { + byte[] b = cert.getExtensionValue(AUTH_KEY_ID); + if (!Arrays.equals(b, authKeyId)) + return false; + } + if (keyUsage != null) + { + boolean[] b = cert.getKeyUsage(); + if (!Arrays.equals(b, keyUsage)) + return false; + } + if (basicConstraints >= 0) + { + if (cert.getBasicConstraints() != basicConstraints) + return false; + } + if (keyPurposeSet != null) + { + List kp = null; + try + { + kp = cert.getExtendedKeyUsage(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (kp == null) + return false; + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + if (!kp.contains(it.next())) + return false; + } + } + if (altNames != null) + { + Collection an = null; + try + { + an = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (an == null) + return false; + int match = 0; + for (Iterator it = altNames.iterator(); it.hasNext(); ) + { + List l = (List) it.next(); + Integer id = (Integer) l.get(0); + String s = null; + byte[] b = null; + if (l.get(1) instanceof String) + s = (String) l.get(1); + else if (l.get(1) instanceof byte[]) + b = (byte[]) l.get(1); + else + return false; + for (Iterator it2 = an.iterator(); it2.hasNext(); ) + { + Object o = it2.next(); + if (!(o instanceof List)) + continue; + List l2 = (List) o; + if (l2.size() != 2) + continue; + if (!id.equals(l2.get(0))) + continue; + if (s != null && (l2.get(1) instanceof String) && + s.equals(l2.get(1))) + match++; + else if (b != null && (l2.get(1) instanceof byte[]) && + Arrays.equals(b, (byte[]) l2.get(1))) + match++; + } + if (match == 0 || (matchAllNames && match != altNames.size())) + return false; + } + } + if (nameConstraints != null) + { + byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); + if (!Arrays.equals(nameConstraints, nc)) + return false; + } + + // FIXME check policies. + // FIXME check path-to-names. + + return true; + } + + public String toString() + { + StringBuffer str = new StringBuffer(X509CertSelector.class.getName()); + String nl = SystemProperties.getProperty("line.separator"); + String eol = ";" + nl; + str.append(" {").append(nl); + if (cert != null) + str.append(" certificate = ").append(cert).append(eol); + if (basicConstraints >= 0) + str.append(" basic constraints = ").append(basicConstraints).append(eol); + if (serialNo != null) + str.append(" serial number = ").append(serialNo).append(eol); + if (certValid != null) + str.append(" valid date = ").append(certValid).append(eol); + if (issuer != null) + str.append(" issuer = ").append(issuer).append(eol); + if (subject != null) + str.append(" subject = ").append(subject).append(eol); + if (sigId != null) + str.append(" signature OID = ").append(sigId).append(eol); + if (subjectKey != null) + str.append(" subject public key = ").append(subjectKey).append(eol); + if (subjectKeyId != null) + { + str.append(" subject key ID = "); + for (int i = 0; i < subjectKeyId.length; i++) + { + str.append(Character.forDigit((subjectKeyId[i] & 0xF0) >>> 8, 16)); + str.append(Character.forDigit((subjectKeyId[i] & 0x0F), 16)); + if (i < subjectKeyId.length - 1) + str.append(':'); + } + str.append(eol); + } + if (authKeyId != null) + { + str.append(" authority key ID = "); + for (int i = 0; i < authKeyId.length; i++) + { + str.append(Character.forDigit((authKeyId[i] & 0xF0) >>> 8, 16)); + str.append(Character.forDigit((authKeyId[i] & 0x0F), 16)); + if (i < authKeyId.length - 1) + str.append(':'); + } + str.append(eol); + } + if (keyUsage != null) + { + str.append(" key usage = "); + for (int i = 0; i < keyUsage.length; i++) + str.append(keyUsage[i] ? '1' : '0'); + str.append(eol); + } + if (keyPurposeSet != null) + str.append(" key purpose = ").append(keyPurposeSet).append(eol); + if (altNames != null) + str.append(" alternative names = ").append(altNames).append(eol); + if (nameConstraints != null) + str.append(" name constraints = <blob of data>").append(eol); + str.append("}").append(nl); + return str.toString(); + } + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } + } + + // Own methods. + // ------------------------------------------------------------------------- + + private static boolean checkOid(int[] oid) + { + return (oid != null && oid.length > 2 && + (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); + } +} diff --git a/libjava/classpath/java/security/cert/X509Certificate.java b/libjava/classpath/java/security/cert/X509Certificate.java new file mode 100644 index 00000000000..f6c6fcfb9a4 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509Certificate.java @@ -0,0 +1,588 @@ +/* X509Certificate.java --- X.509 Certificate class + Copyright (C) 1999,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.security.Principal; +import java.util.Date; + +/** + * X509Certificate is the abstract class for X.509 certificates. + * This provides a stanard class interface for accessing all + * the attributes of X.509 certificates. + * + * <p>In June 1996, the basic X.509 v3 format was finished by + * ISO/IEC and ANSI X.9. The ASN.1 DER format is below: + * + * <blockquote><pre> + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + * </pre></blockquote> + * + * <p>These certificates are widely used in various Internet + * protocols to support authentication. It is used in + * Privacy Enhanced Mail (PEM), Transport Layer Security (TLS), + * Secure Sockets Layer (SSL), code signing for trusted software + * distribution, and Secure Electronic Transactions (SET). + * + * <p>The certificates are managed and vouched for by + * <I>Certificate Authorities</I> (CAs). CAs are companies or + * groups that create certificates by placing the data in the + * X.509 certificate format and signing it with their private + * key. CAs serve as trusted third parties by certifying that + * the person or group specified in the certificate is who + * they say they are. + * + * <p>The ASN.1 defintion for <I>tbsCertificate</I> is + * + * <blockquote><pre> + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + * } + * + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + * + * UniqueIdentifier ::= BIT STRING + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + * </pre></blockquote> + * + * Certificates are created with the CertificateFactory. + * + * <p>References: + * + * <ol> + * <li>Olivier Dubuisson, Philippe Fouquart (Translator) <i>ASN.1 - + * Communication between heterogeneous systems</i>, (C) September 2000, + * Morgan Kaufmann Publishers, ISBN 0-12-6333361-0. Available on-line at + * <a + * href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html</a></li> + * <li>R. Housley et al, <i><a href="http://www.ietf.org/rfc/rfc3280.txt">RFC + * 3280: Internet X.509 Public Key Infrastructure Certificate and CRL + * Profile</a></i>.</li> + * </ol> + * + * @since JDK 1.2 + * @author Mark Benvenuto + * @author Casey Marshall (rsdio@metastatic.org) + */ +public abstract class X509Certificate + extends java.security.cert.Certificate // XXX workaround for gcj bug #17845 + implements X509Extension +{ + private static final long serialVersionUID = -2491127588187038216L; + + /** + * Constructs a new certificate of the specified type. + */ + protected X509Certificate() + { + super( "X.509" ); + } + + /** + Checks the validity of the X.509 certificate. It is valid + if the current date and time are within the period specified + by the certificate. + + The ASN.1 DER encoding is: + + validity Validity, + + Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @throws CertificateExpiredException if the certificate expired + @throws CertificateNotYetValidException if the certificate is + not yet valid + */ + public abstract void checkValidity() + throws CertificateExpiredException, + CertificateNotYetValidException; + + /** + Checks the validity of the X.509 certificate for the + specified time and date. It is valid if the specified + date and time are within the period specified by + the certificate. + + @throws CertificateExpiredException if the certificate expired + based on the date + @throws CertificateNotYetValidException if the certificate is + not yet valid based on the date + */ + public abstract void checkValidity(Date date) + throws CertificateExpiredException, + CertificateNotYetValidException; + + /** + Returns the version of this certificate. + + The ASN.1 DER encoding is: + + version [0] EXPLICIT Version DEFAULT v1, + + Version ::= INTEGER { v1(0), v2(1), v3(2) } + + Consult rfc2459 for more information. + + @return version number of certificate + */ + public abstract int getVersion(); + + /** + Gets the serial number for serial Number in + this Certifcate. It must be a unique number + unique other serial numbers from the granting CA. + + The ASN.1 DER encoding is: + + serialNumber CertificateSerialNumber, + + CertificateSerialNumber ::= INTEGER + + Consult rfc2459 for more information. + + @return the serial number for this X509CRLEntry. + */ + public abstract BigInteger getSerialNumber(); + + /** + Returns the issuer (issuer distinguished name) of the + Certificate. The issuer is the entity who signed + and issued the Certificate. + + The ASN.1 DER encoding is: + + issuer Name, + + Name ::= CHOICE { + RDNSequence } + + RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + + RelativeDistinguishedName ::= + SET OF AttributeTypeAndValue + + AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + + AttributeType ::= OBJECT IDENTIFIER + + AttributeValue ::= ANY DEFINED BY AttributeType + + DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1.. MAX)), + bmpString BMPString (SIZE (1..MAX)) } + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getIssuerDN(); + + /** + Returns the subject (subject distinguished name) of the + Certificate. The subject is the entity who the Certificate + identifies. + + The ASN.1 DER encoding is: + + subject Name, + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getSubjectDN(); + + /** + Returns the date that this certificate is not to be used + before, <I>notBefore</I>. + + The ASN.1 DER encoding is: + + validity Validity, + + Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the date <I>notBefore</I> + */ + public abstract Date getNotBefore(); + + /** + Returns the date that this certificate is not to be used + after, <I>notAfter</I>. + + @return the date <I>notAfter</I> + */ + public abstract Date getNotAfter(); + + + /** + Returns the <I>tbsCertificate</I> from the certificate. + + @return the DER encoded tbsCertificate + + @throws CertificateEncodingException if encoding error occurred + */ + public abstract byte[] getTBSCertificate() throws CertificateEncodingException; + + /** + Returns the signature in its raw DER encoded format. + + The ASN.1 DER encoding is: + + signatureValue BIT STRING + + Consult rfc2459 for more information. + + @return byte array representing signature + */ + public abstract byte[] getSignature(); + + /** + Returns the signature algorithm used to sign the CRL. + An examples is "SHA-1/DSA". + + The ASN.1 DER encoding is: + + signatureAlgorithm AlgorithmIdentifier, + + AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + Consult rfc2459 for more information. + + The algorithm name is determined from the OID. + + @return a string with the signature algorithm name + */ + public abstract String getSigAlgName(); + + + /** + Returns the OID for the signature algorithm used. + Example "1.2.840.10040.4.3" is return for SHA-1 with DSA.\ + + The ASN.1 DER encoding for the example is: + + id-dsa-with-sha1 ID ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + Consult rfc2459 for more information. + + @return a string containing the OID. + */ + public abstract String getSigAlgOID(); + + + /** + Returns the AlgorithmParameters in the encoded form + for the signature algorithm used. + + If access to the parameters is need, create an + instance of AlgorithmParameters. + + @return byte array containing algorithm parameters, null + if no parameters are present in certificate + */ + public abstract byte[] getSigAlgParams(); + + + /** + Returns the issuer unique ID for this certificate. + + The ASN.1 DER encoding is: + + issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + + UniqueIdentifier ::= BIT STRING + + Consult rfc2459 for more information. + + @return bit representation of <I>issuerUniqueID</I> + */ + public abstract boolean[] getIssuerUniqueID(); + + /** + Returns the subject unique ID for this certificate. + + The ASN.1 DER encoding is: + + subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + + UniqueIdentifier ::= BIT STRING + + Consult rfc2459 for more information. + + @return bit representation of <I>subjectUniqueID</I> + */ + public abstract boolean[] getSubjectUniqueID(); + + /** + Returns a boolean array representing the <I>KeyUsage</I> + extension for the certificate. The KeyUsage (OID = 2.5.29.15) + defines the purpose of the key in the certificate. + + The ASN.1 DER encoding is: + + id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + + KeyUsage ::= BIT STRING { + digitalSignature (0), + nonRepudiation (1), + keyEncipherment (2), + dataEncipherment (3), + keyAgreement (4), + keyCertSign (5), + cRLSign (6), + encipherOnly (7), + decipherOnly (8) } + + Consult rfc2459 for more information. + + @return bit representation of <I>KeyUsage</I> + */ + public abstract boolean[] getKeyUsage(); + + /** + Returns the certificate constraints path length from the + critical BasicConstraints extension, (OID = 2.5.29.19). + + The basic constraints extensions is used to determine if + the subject of the certificate is a Certificate Authority (CA) + and how deep the certification path may exist. The + <I>pathLenConstraint</I> only takes affect if <I>cA</I> + is set to true. "A value of zero indicates that only an + end-entity certificate may follow in the path." (rfc2459) + + The ASN.1 DER encoding is: + + id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } + + BasicConstraints ::= SEQUENCE { + cA BOOLEAN DEFAULT FALSE, + pathLenConstraint INTEGER (0..MAX) OPTIONAL } + + Consult rfc2459 for more information. + + @return the length of the path constraint if BasicConstraints + is present and cA is TRUE. Otherwise returns -1. + */ + public abstract int getBasicConstraints(); + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the <code>ExtendedKeyUsage</code> extension of this + * certificate, or null if there is no extension present. The returned + * value is a {@link java.util.List} strings representing the object + * identifiers of the extended key usages. This extension has the OID + * 2.5.29.37. + * + * <p>The ASN.1 definition for this extension is: + * + * <blockquote><pre> + * ExtendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + * </pre></blockquote> + * + * @return The list of extension OIDs, or null if there are none + * present in this certificate. + * @throws CertificateParsingException If this extension cannot be + * parsed from its encoded form. + */ + public java.util.List getExtendedKeyUsage() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the alternative names for this certificate's subject (the + * owner), or null if there are none. + * + * <p>This is an X.509 extension with OID 2.5.29.17 and is defined by + * the ASN.1 construction: + * + * <blockquote><pre> + * SubjectAltNames ::= GeneralNames + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER + * } + * </pre></blockquote> + * + * <p>The returned collection contains one or more two-element Lists, + * with the first object being an Integer representing the choice + * above (with value 0 through 8) and the second being an (a) String + * if the <code>GeneralName</code> is a rfc822Name, dNSName, + * uniformResourceIdentifier, iPAddress, or registeredID, or (b) a + * byte array of the DER encoded form for any others. + * + * @return The collection of alternative names, or null if there are + * none. + * @throws CertificateParsingException If the encoded extension cannot + * be parsed. + * @since JDK 1.4 + */ + public java.util.Collection getSubjectAlternativeNames() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the alternative names for this certificate's issuer, or + * null if there are none. + * + * <p>This is an X.509 extension with OID 2.5.29.18, and is defined by + * the ASN.1 construction: + * + * <blockquote><pre> + * IssuerAltNames ::= GeneralNames + * </pre></blockquote> + * + * <p>The <code>GeneralNames</code> construct and the form of the + * returned collection are the same as with {@link + * #getSubjectAlternativeNames()}. + * + * @return The collection of alternative names, or null if there are + * none. + * @throws CertificateParsingException If the encoded extension cannot + * be parsed. + * @since JDK 1.4 + */ + public java.util.Collection getIssuerAlternativeNames() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the X.500 distinguished name of this certificate's subject. + * + * @return The subject's X.500 distinguished name. + * @since JDK 1.4 + */ + public javax.security.auth.x500.X500Principal getSubjectX500Principal() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the X.500 distinguished name of this certificate's issuer. + * + * @return The issuer's X.500 distinguished name. + * @since JDK 1.4 + */ + public javax.security.auth.x500.X500Principal getIssuerX500Principal() + { + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/security/cert/X509Extension.java b/libjava/classpath/java/security/cert/X509Extension.java new file mode 100644 index 00000000000..d2cb80a9f57 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509Extension.java @@ -0,0 +1,113 @@ +/* X509Extension.java --- X.509 Extension + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; +import java.util.Set; + +/** + Public interface for the X.509 Extension. + + This is used for X.509 v3 Certificates and CRL v2 (Certificate + Revocation Lists) for managing attributes assoicated with + Certificates, for managing the hierarchy of certificates, + and for managing the distribution of CRL. This extension + format is used to define private extensions. + + Each extensions for a certificate or CRL must be marked + either critical or non-critical. If the certificate/CRL + system encounters a critical extension not recognized then + it must reject the certificate. A non-critical extension + may be just ignored if not recognized. + + + The ASN.1 definition for this class is: + + Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + + Extension ::= SEQUENCE { + extnId OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING + -- contains a DER encoding of a value + -- of the type registered for use with + -- the extnId object identifier value + } + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public interface X509Extension +{ + + /** + Returns true if the certificate contains a critical extension + that is not supported. + + @return true if has unsupported extension, false otherwise + */ + boolean hasUnsupportedCriticalExtension(); + + /** + Returns a set of the CRITICAL extension OIDs from the + certificate/CRL that the object implementing this interface + manages. + + @return A Set containing the OIDs. If there are no CRITICAL + extensions or extensions at all this returns null. + */ + Set getCriticalExtensionOIDs(); + + /** + Returns a set of the NON-CRITICAL extension OIDs from the + certificate/CRL that the object implementing this interface + manages. + + @return A Set containing the OIDs. If there are no NON-CRITICAL + extensions or extensions at all this returns null. + */ + Set getNonCriticalExtensionOIDs(); + + /** + Returns the DER encoded OCTET string for the specified + extension value identified by a OID. The OID is a string + of number separated by periods. Ex: 12.23.45.67 + */ + byte[] getExtensionValue(String oid); + +} diff --git a/libjava/classpath/java/security/cert/package.html b/libjava/classpath/java/security/cert/package.html new file mode 100644 index 00000000000..14b12d16cc3 --- /dev/null +++ b/libjava/classpath/java/security/cert/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.security.cert package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.security.cert</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/security/interfaces/DSAKey.java b/libjava/classpath/java/security/interfaces/DSAKey.java new file mode 100644 index 00000000000..c6e819eb0e5 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAKey.java @@ -0,0 +1,56 @@ +/* DSAKey.java -- Interface for Digital Signature Algorithm key + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +/** + * This interface is implemented by a class to return the parameters + * of a Digital Signature Algorithm (DSA) public or private key. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAKey +{ + /** + * This method returns non-secret parameters of the DSA key + * + * @return The DSA parameters + */ + DSAParams getParams(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java b/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java new file mode 100644 index 00000000000..e657c54b4e6 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java @@ -0,0 +1,85 @@ +/* DSAKeyPairGenerator.java -- Initialize a DSA key generator + Copyright (C) 1998, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.security.InvalidParameterException; +import java.security.SecureRandom; + +/** + * This interface contains methods for intializing a Digital Signature + * Algorithm key generation engine. The initialize methods may be called + * any number of times. If no explicity initialization call is made, then + * the engine defaults to generating 1024-bit keys using pre-calculated + * base, prime, and subprime values. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAKeyPairGenerator +{ + /** + * Initializes the key generator with the specified DSA parameters and + * random bit source + * + * @param params The DSA parameters to use + * @param random The random bit source to use + * + * @exception InvalidParameterException If the parameters passed are not valid + */ + void initialize (DSAParams params, SecureRandom random) + throws InvalidParameterException; + + /** + * Initializes the key generator to a give modulus. If the <code>genParams</code> + * value is <code>true</code> then new base, prime, and subprime values + * will be generated for the given modulus. If not, the pre-calculated + * values will be used. If no pre-calculated values exist for the specified + * modulus, an exception will be thrown. It is guaranteed that there will + * always be pre-calculated values for all modulus values between 512 and + * 1024 bits inclusives. + * + * @param modlen The modulus length + * @param genParams <code>true</code> to generate new DSA parameters, <code>false</code> otherwise + * @param random The random bit source to use + * + * @exception InvalidParameterException If a parameter is invalid + */ + void initialize (int modlen, boolean genParams, SecureRandom random) + throws InvalidParameterException; +} diff --git a/libjava/classpath/java/security/interfaces/DSAParams.java b/libjava/classpath/java/security/interfaces/DSAParams.java new file mode 100644 index 00000000000..42baeeb9570 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAParams.java @@ -0,0 +1,72 @@ +/* DSAParams.java -- Digital Signature Algorithm parameter access + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + * This interface allows the Digital Signature Algorithm (DSA) parameters + * to be queried. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAParams +{ + /** + * Returns the base, or 'g' value + * + * @return The DSA base value + */ + BigInteger getG(); + + /** + * Returns the prime, or 'p' value + * + * @return The DSA prime value + */ + BigInteger getP(); + + /** + * Returns the subprime, or 'q' value + * + * @return The DSA subprime value + */ + BigInteger getQ(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAPrivateKey.java b/libjava/classpath/java/security/interfaces/DSAPrivateKey.java new file mode 100644 index 00000000000..d79b34b9043 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAPrivateKey.java @@ -0,0 +1,61 @@ +/* DSAPublicKey.java -- A Digital Signature Algorithm private key + Copyright (C) 1998, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PrivateKey; + +/** + * This interface models a Digital Signature Algorithm (DSA) private key + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAPrivateKey extends DSAKey, PrivateKey +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 7776497482533790279L; + + /** + * This method returns the value of the DSA private key + */ + BigInteger getX(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAPublicKey.java b/libjava/classpath/java/security/interfaces/DSAPublicKey.java new file mode 100644 index 00000000000..d73e189f607 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAPublicKey.java @@ -0,0 +1,61 @@ +/* DSAPublicKey.java -- A Digital Signature Algorithm public key + Copyright (C) 1998, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PublicKey; + +/** + * This interface models a Digital Signature Algorithm (DSA) public key + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAPublicKey extends DSAKey, PublicKey +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 1234526332779022332L; + + /** + * This method returns the value of the DSA public key + */ + BigInteger getY(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAKey.java b/libjava/classpath/java/security/interfaces/RSAKey.java new file mode 100644 index 00000000000..485fa81e05a --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAKey.java @@ -0,0 +1,57 @@ +/* RSAKey.java --- A generic RSA Key interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + A generic RSA Key interface for public and private keys + + @since JDK 1.3 + + @author Mark Benvenuto + */ +public interface RSAKey +{ + /** + Generates a modulus. + + @returns a modulus + */ + BigInteger getModulus(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java b/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java new file mode 100644 index 00000000000..d80b962d012 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java @@ -0,0 +1,111 @@ +/* RSAMultiPrimePrivateCrtKey.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.spec.RSAOtherPrimeInfo; + +/** + * The interface to an RSA multi-prime private key, as defined in the PKCS#1 + * v2.1, using the <i>Chinese Remainder Theorem</i> (CRT) information values. + * + * @since 1.4 + * @see java.security.spec.RSAPrivateKeySpec + * @see java.security.spec.RSAMultiPrimePrivateCrtKeySpec + * @see RSAPrivateKey + * @see RSAPrivateCrtKey + */ +public interface RSAMultiPrimePrivateCrtKey extends RSAPrivateKey +{ + // Constants + // -------------------------------------------------------------------------- + long serialVersionUID = 618058533534628008L; + + // Methods + // -------------------------------------------------------------------------- + + /** + * Returns the public exponent. + * + * @return the public exponent. + */ + BigInteger getPublicExponent(); + + /** + * Returns the primeP. + * + * @return the primeP. + */ + BigInteger getPrimeP(); + + /** + * Returns the primeQ. + * + * @return the primeQ. + */ + BigInteger getPrimeQ(); + + /** + * Returns the primeExponentP. + * + * @return the primeExponentP. + */ + BigInteger getPrimeExponentP(); + + /** + * Returns the primeExponentQ. + * + * @return the primeExponentQ. + */ + BigInteger getPrimeExponentQ(); + + /** + * Returns the crtCoefficient. + * + * @return the crtCoefficient. + */ + BigInteger getCrtCoefficient(); + + /** + * Returns the otherPrimeInfo or <code>null</code> if there are only two + * prime factors (p and q). + * + * @return the otherPrimeInfo. + */ + RSAOtherPrimeInfo[] getOtherPrimeInfo(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java b/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java new file mode 100644 index 00000000000..96a1496cf92 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java @@ -0,0 +1,95 @@ +/* RSAPrivateCrtKey.java -- An RSA private key in CRT format + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + * This interface provides access to information about an RSA private + * key in Chinese Remainder Theorem (CRT) format. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPrivateCrtKey extends RSAPrivateKey +{ + long serialVersionUID = -5682214253527700368L; + + /** + * Returns the public exponent for this key + * + * @return The public exponent for this key + */ + BigInteger getPublicExponent(); + + /** + * Returns the primeP value + * + * @return The primeP value + */ + BigInteger getPrimeP(); + + /** + * Returns the primeQ value + * + * @return The primeQ value + */ + BigInteger getPrimeQ(); + + /** + * Returns the primeExponentP + * + * @return The primeExponentP + */ + BigInteger getPrimeExponentP(); + + /** + * Returns the primeExponentQ + * + * @return The primeExponentQ + */ + BigInteger getPrimeExponentQ(); + + /** + * Returns the CRT coefficient + * + * @return The CRT coefficient + */ + BigInteger getCrtCoefficient(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPrivateKey.java b/libjava/classpath/java/security/interfaces/RSAPrivateKey.java new file mode 100644 index 00000000000..514987625a5 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPrivateKey.java @@ -0,0 +1,60 @@ +/* RSAPrivateKey.java -- An RSA private key + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PrivateKey; + +/** + * This interface provides access to information about an RSA private key. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPrivateKey extends PrivateKey, RSAKey +{ + long serialVersionUID = 5187144804936595022L; + + /** + * Returns the private exponent value for this key + * + * @return The private exponent value for this key + */ + BigInteger getPrivateExponent(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPublicKey.java b/libjava/classpath/java/security/interfaces/RSAPublicKey.java new file mode 100644 index 00000000000..5fb569d1dec --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPublicKey.java @@ -0,0 +1,60 @@ +/* RSAPublicKey.java -- An RSA public key + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PublicKey; + +/** + * This interface provides access to information about an RSA public key. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPublicKey extends PublicKey, RSAKey +{ + long serialVersionUID = -8727434096241101194L; + + /** + * Returns the public exponent value for this key + * + * @return The public exponent value for this key + */ + BigInteger getPublicExponent(); +} diff --git a/libjava/classpath/java/security/interfaces/package.html b/libjava/classpath/java/security/interfaces/package.html new file mode 100644 index 00000000000..aab0d63752e --- /dev/null +++ b/libjava/classpath/java/security/interfaces/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.security.interfaces package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.security.interfaces</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/security/package.html b/libjava/classpath/java/security/package.html new file mode 100644 index 00000000000..328b7044b81 --- /dev/null +++ b/libjava/classpath/java/security/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.security package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.security</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java b/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java new file mode 100644 index 00000000000..25506f55cfe --- /dev/null +++ b/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java @@ -0,0 +1,52 @@ +/* AlgorithmParameterSpec.java --- Algorithm Parameter Spec Interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + A transparent interface for Algorithm Parameter Specifications. + It contains no member functions. It is used to group + algorithm parameter classes. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public interface AlgorithmParameterSpec +{ +} diff --git a/libjava/classpath/java/security/spec/DSAParameterSpec.java b/libjava/classpath/java/security/spec/DSAParameterSpec.java new file mode 100644 index 00000000000..7e263291c03 --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAParameterSpec.java @@ -0,0 +1,101 @@ +/* DSAParameterSpec.java --- DSA Parameter Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.math.BigInteger; +import java.security.interfaces.DSAParams; + +/** + * DSA Parameter class Specification. Used to maintain the DSA + * Parameters. + * + * @since 1.2 + * + * @author Mark Benvenuto +*/ +public class DSAParameterSpec implements AlgorithmParameterSpec, DSAParams +{ + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + * Constructs a new DSAParameterSpec with the specified p, q, and g. + * + * @param p the prime + * @param q the sub-prime + * @param g the base + */ + public DSAParameterSpec(BigInteger p, BigInteger q, BigInteger g) + { + this.p = p; + this.q = q; + this.g = g; + } + + /** + * Returns p for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + * Returns p for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + * Returns g for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } +}
\ No newline at end of file diff --git a/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java b/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java new file mode 100644 index 00000000000..7415fa11a45 --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java @@ -0,0 +1,113 @@ +/* DSAPrivateKeySpec.java --- DSA Private Key Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + DSA Private Key class Specification. Used to maintain the DSA + Private Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class DSAPrivateKeySpec implements KeySpec +{ + private BigInteger x = null; + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + Constructs a new DSAPrivateKeySpec with the specified x, p, q, and g. + + @param x the private key + @param p the prime + @param q the sub-prime + @param g the base + */ + public DSAPrivateKeySpec(BigInteger x, BigInteger p, BigInteger q, BigInteger g) + { + this.x = x; + this.p = p; + this.q = q; + this.g = g; + } + + /** + Returns private key x for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getX() + { + return this.x; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + Returns g for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } + +} diff --git a/libjava/classpath/java/security/spec/DSAPublicKeySpec.java b/libjava/classpath/java/security/spec/DSAPublicKeySpec.java new file mode 100644 index 00000000000..ac1310c1caa --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAPublicKeySpec.java @@ -0,0 +1,113 @@ +/* DSAPublicKeySpec.java --- DSA Public Key Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + DSA Public Key class Specification. Used to maintain the DSA + Public Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class DSAPublicKeySpec implements KeySpec +{ + private BigInteger y = null; + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + Constructs a new DSAPublicKeySpec with the specified y, p, q, and g. + + @param y the public key + @param p the prime + @param q the sub-prime + @param g the base + */ + public DSAPublicKeySpec(BigInteger y, BigInteger p, BigInteger q, BigInteger g) + { + this.y = y; + this.p = p; + this.q = q; + this.g = g; + } + + /** + Returns public key y for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getY() + { + return this.y; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + Returns g for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } + +} diff --git a/libjava/classpath/java/security/spec/EncodedKeySpec.java b/libjava/classpath/java/security/spec/EncodedKeySpec.java new file mode 100644 index 00000000000..c5baf55fd7a --- /dev/null +++ b/libjava/classpath/java/security/spec/EncodedKeySpec.java @@ -0,0 +1,85 @@ +/* EncodedKeySpec.java --- Encoded Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + Encoded Key Specification class which is used to store + byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public abstract class EncodedKeySpec implements KeySpec +{ + + private byte[] encodedKey; + + /** + Constructs a new EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store + */ + public EncodedKeySpec(byte[] encodedKey) + { + this.encodedKey = encodedKey; + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key + */ + public byte[] getEncoded() + { + return this.encodedKey; + } + + /** + Returns the name of the key format used. + + This name is the format such as "PKCS#8" or "X.509" which + if it matches a Key class name of the same type can be + transformed using the apporiate KeyFactory. + + @return a string representing the name + */ + public abstract String getFormat(); + +} diff --git a/libjava/classpath/java/security/spec/InvalidKeySpecException.java b/libjava/classpath/java/security/spec/InvalidKeySpecException.java new file mode 100644 index 00000000000..c2ec6b03b5a --- /dev/null +++ b/libjava/classpath/java/security/spec/InvalidKeySpecException.java @@ -0,0 +1,74 @@ +/* InvalidKeySpecException.java -- invalid KeySpec Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.security.GeneralSecurityException; + +/** + * Exception for an invalid key specification. + * + * @author Mark Benvenuto + * @see KeySpec + * @since 1.2 + * @status updated to 1.4 + */ +public class InvalidKeySpecException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3546139293998810778L; + + /** + * Constructs an InvalidKeySpecException without a message string. + */ + public InvalidKeySpecException() + { + } + + /** + * Constructs an InvalidKeySpecException with a message string. + * + * @param msg a message to display with exception + */ + public InvalidKeySpecException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/spec/InvalidParameterSpecException.java b/libjava/classpath/java/security/spec/InvalidParameterSpecException.java new file mode 100644 index 00000000000..481e11e306b --- /dev/null +++ b/libjava/classpath/java/security/spec/InvalidParameterSpecException.java @@ -0,0 +1,76 @@ +/* InvalidParameterSpecException.java --- invalid ParameterSpec Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.security.GeneralSecurityException; + +/** + * Exception for an invalid algorithm specification. + * + * @author Mark Benvenuto + * @see AlogorithmParameters + * @see AlogorithmParameterSpec + * @see DSAParameterSpec + * @since 1.2 + * @status updated to 1.4 +*/ +public class InvalidParameterSpecException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -970468769593399342L; + + /** + * Constructs an InvalidParameterSpecException without a message string. + */ + public InvalidParameterSpecException() + { + } + + /** + * Constructs an InvalidParameterSpecException with a message string. + * + * @param msg a message to display with exception + */ + public InvalidParameterSpecException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/spec/KeySpec.java b/libjava/classpath/java/security/spec/KeySpec.java new file mode 100644 index 00000000000..93f1a6db277 --- /dev/null +++ b/libjava/classpath/java/security/spec/KeySpec.java @@ -0,0 +1,52 @@ +/* KeySpec.java --- Key Specification interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + A transparent interface for Key Specifications. + It contains no member functions. It is used to group + key classes. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public interface KeySpec +{ +} diff --git a/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java b/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java new file mode 100644 index 00000000000..4a4f1eccea7 --- /dev/null +++ b/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java @@ -0,0 +1,81 @@ +/* PKCS8EncodedKeySpec.java --- PKCS8 Encoded Key Specificaton class + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + PKCS8 Encoded Key Specification class which is used to store + "PKCS#8" byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class PKCS8EncodedKeySpec extends EncodedKeySpec +{ + /** + Constructs a new PKCS8EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store, assumed to be "PKCS#8" + */ + public PKCS8EncodedKeySpec(byte[] encodedKey) + { + super( encodedKey ); + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key +*/ + public byte[] getEncoded() + { + return super.getEncoded(); + } + + /** + Returns the name of the key format used which is "PKCS#8" + + @return a string representing the name +*/ + public final String getFormat() + { + return "PKCS#8"; + } + +} diff --git a/libjava/classpath/java/security/spec/PSSParameterSpec.java b/libjava/classpath/java/security/spec/PSSParameterSpec.java new file mode 100644 index 00000000000..7a14a24fbe4 --- /dev/null +++ b/libjava/classpath/java/security/spec/PSSParameterSpec.java @@ -0,0 +1,90 @@ +/* PSSParameterSpec.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +/** + * This class specifies a parameter spec for RSA PSS encoding scheme, as + * defined in the PKCS#1 v2.1. + * + * @since 1.4 + * @see AlgorithmParameterSpec + * @see java.security.Signature + */ +public class PSSParameterSpec implements AlgorithmParameterSpec +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private int saltLen; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Creates a new <code>PSSParameterSpec</code> given the salt length as + * defined in PKCS#1. + * + * @param saltLen the length of salt in bits to be used in PKCS#1 PSS encoding. + * @throws IllegalArgumentException if <code>saltLen</code> is less than + * <code>0</code>. + */ + public PSSParameterSpec(int saltLen) + { + super(); + + if (saltLen < 0) + throw new IllegalArgumentException(); + this.saltLen = saltLen; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Returns the salt length in bits. + * + * @return the salt length. + */ + public int getSaltLength() + { + return this.saltLen; + } +} diff --git a/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java b/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java new file mode 100644 index 00000000000..0df8dec783e --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java @@ -0,0 +1,97 @@ +/* RSAKeyGenParameterSpec.java --- RSA Key Generator Parameter Spec Class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + This class generates a set of RSA Key parameters used in the generation + of RSA keys. + + @since JDK 1.3 + + @author Mark Benvenuto +*/ +public class RSAKeyGenParameterSpec implements AlgorithmParameterSpec +{ + private int keysize; + private BigInteger publicExponent; + + /** + Public Exponent F0 = 3 + */ + public static final BigInteger F0 = new BigInteger("3"); + + /** + Public Exponent F4 = 3 + */ + public static final BigInteger F4 = new BigInteger("65537"); + + /** + Create a new RSAKeyGenParameterSpec to store the RSA key's keysize + and public exponent + + @param keysize Modulus size of key in bits + @param publicExponent - the exponent + */ + public RSAKeyGenParameterSpec(int keysize, BigInteger publicExponent) + { + this.keysize = keysize; + this.publicExponent = publicExponent; + } + + /** + Return the size of the key. + + @return the size of the key. + */ + public int getKeysize() + { + return keysize; + } + + /** + Return the public exponent. + + @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return publicExponent; + } +} diff --git a/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java b/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java new file mode 100644 index 00000000000..519a0291373 --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java @@ -0,0 +1,217 @@ +/* PSSParameterSpec.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +import java.math.BigInteger; + +/** + * This class specifies an RSA multi-prime private key, as defined in the + * PKCS#1 v2.1, using the <i>Chinese Remainder Theorem</i> (CRT) information + * values for efficiency. + * + * @since 1.4 + * @see java.security.Key + * @see java.security.KeyFactory + * @see KeySpec + * @see PKCS8EncodedKeySpec + * @see RSAPrivateKeySpec + * @see RSAPublicKeySpec + * @see RSAOtherPrimeInfo + */ +public class RSAMultiPrimePrivateCrtKeySpec extends RSAPrivateKeySpec +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private BigInteger publicExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + private RSAOtherPrimeInfo[] otherPrimeInfo; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * <p>Creates a new <code>RSAMultiPrimePrivateCrtKeySpec</code> given the + * modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, + * primeExponentQ, crtCoefficient, and otherPrimeInfo as defined in PKCS#1 + * v2.1.</p> + * + * <p>Note that <code>otherPrimeInfo</code> is cloned when constructing this + * object.</p> + * + * @param modulus the modulus n. + * @param publicExponent the public exponent e. + * @param privateExponent the private exponent d. + * @param primeP the prime factor p of n. + * @param primeQ the prime factor q of n. + * @param primeExponentP this is d mod (p-1). + * @param primeExponentQ this is d mod (q-1). + * @param crtCoefficient the Chinese Remainder Theorem coefficient q-1 mod p. + * @param otherPrimeInfo triplets of the rest of primes, <code>null</code> + * can be specified if there are only two prime factors (p and q). + * @throws NullPointerException if any of the parameters, i.e. modulus, + * publicExponent, privateExponent, primeP, primeQ, primeExponentP, + * primeExponentQ, crtCoefficient, is <code>null</code>. + * @throws IllegalArgumentException if an empty, i.e. 0-length, + * otherPrimeInfo is specified. + */ + public RSAMultiPrimePrivateCrtKeySpec(BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger primeP, + BigInteger primeQ, + BigInteger primeExponentP, + BigInteger primeExponentQ, + BigInteger crtCoefficient, + RSAOtherPrimeInfo[] otherPrimeInfo) + { + super(modulus, privateExponent); + + if (modulus == null) + throw new NullPointerException("modulus"); + if (publicExponent == null) + throw new NullPointerException("publicExponent"); + if (privateExponent == null) + throw new NullPointerException("privateExponent"); + if (primeP == null) + throw new NullPointerException("primeP"); + if (primeQ == null) + throw new NullPointerException("primeQ"); + if (primeExponentP == null) + throw new NullPointerException("primeExponentP"); + if (primeExponentQ == null) + throw new NullPointerException("primeExponentQ"); + if (crtCoefficient == null) + throw new NullPointerException("crtCoefficient"); + if (otherPrimeInfo != null) + if (otherPrimeInfo.length == 0) + throw new IllegalArgumentException(); + else + this.otherPrimeInfo = (RSAOtherPrimeInfo[]) otherPrimeInfo.clone(); + + this.publicExponent = publicExponent; + this.primeP = primeP; + this.primeQ = primeQ; + this.primeExponentP = primeExponentP; + this.primeExponentQ = primeExponentQ; + this.crtCoefficient = crtCoefficient; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Returns the public exponent. + * + * @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + + /** + * Returns the primeP. + * + * @return the primeP. + */ + public BigInteger getPrimeP() + { + return this.primeP; + } + + /** + * Returns the primeQ. + * + * @return the primeQ. + */ + public BigInteger getPrimeQ() + { + return this.primeQ; + } + + /** + * Returns the primeExponentP. + * + * @return the primeExponentP. + */ + public BigInteger getPrimeExponentP() + { + return this.primeExponentP; + } + + /** + * Returns the primeExponentQ. + * + * @return the primeExponentQ. + */ + public BigInteger getPrimeExponentQ() + { + return this.primeExponentQ; + } + + /** + * Returns the crtCoefficient. + * + * @return the crtCoefficient. + */ + public BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } + + /** + * Returns a copy of the otherPrimeInfo or <code>null</code> if there are + * only two prime factors (p and q). + * + * @return the otherPrimeInfo. + */ + public RSAOtherPrimeInfo[] getOtherPrimeInfo() + { + return this.otherPrimeInfo == null + ? null + : (RSAOtherPrimeInfo[]) this.otherPrimeInfo.clone(); + } +} diff --git a/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java b/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java new file mode 100644 index 00000000000..654bcb574d8 --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java @@ -0,0 +1,133 @@ +/* RSAOtherPrimeInfo.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +import java.math.BigInteger; + +/** + * This class represents the triplet (prime, exponent, and coefficient) inside + * RSA's OtherPrimeInfo structure, as defined in the PKCS#1 v2.1. The ASN.1 + * syntax of RSA's OtherPrimeInfo is as follows: + * + * <pre> + * OtherPrimeInfo ::= SEQUENCE { + * prime INTEGER, + * exponent INTEGER, + * coefficient INTEGER + * } + * </pre> + * + * @since 1.4 + * @see RSAPrivateCrtKeySpec + * @see java.security.interfaces.RSAMultiPrimePrivateCrtKey + */ +public class RSAOtherPrimeInfo +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private BigInteger prime; + private BigInteger primeExponent; + private BigInteger crtCoefficient; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Creates a new <code>RSAOtherPrimeInfo</code> given the prime, + * primeExponent, and crtCoefficient as defined in PKCS#1. + * + * @param prime the prime factor of n. + * @param primeExponent the exponent. + * @param crtCoefficient the Chinese Remainder Theorem coefficient. + * @throws NullPointerException if any of the parameters, i.e. prime, + * primeExponent, crtCoefficient, is <code>null</code>. + */ + public RSAOtherPrimeInfo(BigInteger prime, BigInteger primeExponent, + BigInteger crtCoefficient) + { + super(); + + if (prime == null) + throw new NullPointerException("prime"); + if (primeExponent == null) + throw new NullPointerException("primeExponent"); + if (crtCoefficient == null) + throw new NullPointerException("crtCoefficient"); + + this.prime = prime; + this.primeExponent = primeExponent; + this.crtCoefficient = crtCoefficient; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Returns the prime. + * + * @return the prime. + */ + public final BigInteger getPrime() + { + return this.prime; + } + + /** + * Returns the prime's exponent. + * + * @return the primeExponent. + */ + public final BigInteger getExponent() + { + return this.primeExponent; + } + + /** + * Returns the prime's crtCoefficient. + * + * @return the crtCoefficient. + */ + public final BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } +} diff --git a/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java b/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java new file mode 100644 index 00000000000..a904c305d65 --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java @@ -0,0 +1,151 @@ +/* RSAPrivateCrtKeySpec.java --- RSA Private Certificate Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Private Certificate Key class Specification. Used to + maintain the RSA Private Certificate Keys with the + <I>Chinese Remainder Theorem</I>(CRT) as specified by PKCS#1. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPrivateCrtKeySpec extends RSAPrivateKeySpec +{ + private BigInteger publicExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + + /** + Constructs a new RSAPrivateKeySpec with the specified + variables. + + @param modulus the RSA modulus + @param publicExponent the public key exponent + @param privateExponent the private key exponent + @param primeP the prime P + @param primeQ the prime Q + @param primeExponentP the prime exponent P + @param primeExponentQ the prime exponent P + @param crtCoefficient the CRT coefficient + */ + public RSAPrivateCrtKeySpec(BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger primeP, + BigInteger primeQ, + BigInteger primeExponentP, + BigInteger primeExponentQ, + BigInteger crtCoefficient) + { + super( modulus, privateExponent); + this.publicExponent = publicExponent; + this.primeP = primeP; + this.primeQ = primeQ; + this.primeExponentP = primeExponentP; + this.primeExponentQ = primeExponentQ; + this.crtCoefficient = crtCoefficient; + } + + /** + Gets the RSA public exponent. + + @return the RSA public exponent + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + + /** + Gets the RSA prime P. + + @return the RSA prime P + */ + public BigInteger getPrimeP() + { + return this.primeP; + } + + /** + Gets the RSA prime Q. + + @return the RSA prime Q + */ + public BigInteger getPrimeQ() + { + return this.primeQ; + } + + /** + Gets the RSA prime exponent P. + + @return the RSA prime exponent P + */ + public BigInteger getPrimeExponentP() + { + return this.primeExponentP; + } + + /** + Gets the RSA prime exponent P. + + @return the RSA prime exponent Q + */ + public BigInteger getPrimeExponentQ() + { + return this.primeExponentQ; + } + + /** + Gets the RSA CRT coefficient. + + @return the RSA CRT coefficient + */ + public BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } + +} diff --git a/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java b/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java new file mode 100644 index 00000000000..d29f261cfb3 --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java @@ -0,0 +1,88 @@ +/* RSAPrivateKeySpec.java --- RSA Private Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Private Key class Specification. Used to maintain the RSA + Private Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPrivateKeySpec implements KeySpec +{ + private BigInteger modulus; + private BigInteger privateExponent; + + /** + Constructs a new RSAPrivateKeySpec with the specified + modulus and privateExponent. + + @param modulus the RSA modulus + @param privateExponent the private key exponent + */ + public RSAPrivateKeySpec(BigInteger modulus, BigInteger privateExponent) + { + this.modulus = modulus; + this.privateExponent = privateExponent; + } + + /** + Gets the RSA modulus. + + @return the RSA modulus + */ + public BigInteger getModulus() + { + return this.modulus; + } + + /** + Gets the RSA private exponent. + + @return the RSA private exponent + */ + public BigInteger getPrivateExponent() + { + return this.privateExponent; + } + +} diff --git a/libjava/classpath/java/security/spec/RSAPublicKeySpec.java b/libjava/classpath/java/security/spec/RSAPublicKeySpec.java new file mode 100644 index 00000000000..21283aa643b --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPublicKeySpec.java @@ -0,0 +1,88 @@ +/* RSAPublicKeySpec.java --- RSA Public Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Public Key class Specification. Used to maintain the RSA + Public Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPublicKeySpec implements KeySpec +{ + private BigInteger modulus; + private BigInteger publicExponent; + + /** + Constructs a new RSAPublicKeySpec with the specified + modulus and publicExponent. + + @param modulus the RSA modulus + @param publicExponent the public key exponent + */ + public RSAPublicKeySpec(BigInteger modulus, BigInteger publicExponent) + { + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + /** + Gets the RSA modulus. + + @return the RSA modulus + */ + public BigInteger getModulus() + { + return this.modulus; + } + + /** + Gets the RSA public exponent. + + @return the RSA public exponent + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + +} diff --git a/libjava/classpath/java/security/spec/X509EncodedKeySpec.java b/libjava/classpath/java/security/spec/X509EncodedKeySpec.java new file mode 100644 index 00000000000..de35960296d --- /dev/null +++ b/libjava/classpath/java/security/spec/X509EncodedKeySpec.java @@ -0,0 +1,82 @@ +/* X509EncodedKeySpec.java --- X.509 Encoded Key Specificaton class + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + X.509 Encoded Key Specification class which is used to store + "X.509" byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class X509EncodedKeySpec extends EncodedKeySpec +{ + + /** + Constructs a new X509EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store, assumed to be "X.509" + */ + public X509EncodedKeySpec(byte[] encodedKey) + { + super( encodedKey ); + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key + */ + public byte[] getEncoded() + { + return super.getEncoded(); + } + + /** + Returns the name of the key format used which is "X.509" + + @return a string representing the name + */ + public final String getFormat() + { + return "X.509"; + } + +} diff --git a/libjava/classpath/java/security/spec/package.html b/libjava/classpath/java/security/spec/package.html new file mode 100644 index 00000000000..8e818896a15 --- /dev/null +++ b/libjava/classpath/java/security/spec/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.security.spec package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.security.spec</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/java/sql/Array.java b/libjava/classpath/java/sql/Array.java new file mode 100644 index 00000000000..51628757885 --- /dev/null +++ b/libjava/classpath/java/sql/Array.java @@ -0,0 +1,185 @@ +/* Array.java -- Interface for accessing SQL array object + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Map; + +/** + * This interface provides methods for accessing SQL array types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Array +{ + /** + * Returns the name of the SQL type of the elements in this + * array. This name is database specific. + * + * @param The name of the SQL type of the elements in this array. + * @exception SQLException If an error occurs. + */ + String getBaseTypeName() throws SQLException; + + /** + * Returns the JDBC type identifier of the elements in this + * array. This will be one of the values defined in the + * <code>Types</code> class. + * + * @return The JDBC type of the elements in this array. + * @exception SQLException If an error occurs. + * @see Types + */ + int getBaseType() throws SQLException; + + /** + * Returns the contents of this array. This object returned + * will be an array of Java objects of the appropriate types. + * + * @return The contents of the array as an array of Java objects. + * @exception SQLException If an error occurs. + */ + Object getArray() throws SQLException; + + /** + * Returns the contents of this array. The specified + * <code>Map</code> will be used to override selected mappings + * between SQL types and Java classes. + * + * @param map A mapping of SQL types to Java classes. + * @return The contents of the array as an array of Java objects. + * @exception SQLException If an error occurs. + */ + Object getArray(Map map) throws SQLException; + + /** + * Returns a portion of this array starting at <code>index</code> + * into the array and continuing for <code>count</code> + * elements. Fewer than the requested number of elements will be + * returned if the array does not contain the requested number of elements. + * The object returned will be an array of Java objects of + * the appropriate types. + * + * @param offset The offset into this array to start returning elements from. + * @param count The requested number of elements to return. + * @return The requested portion of the array. + * @exception SQLException If an error occurs. + */ + Object getArray(long index, int count) throws SQLException; + + /** + * This method returns a portion of this array starting at <code>index</code> + * into the array and continuing for <code>count</code> + * elements. Fewer than the requested number of elements will be + * returned if the array does not contain the requested number of elements. + * The object returned will be an array of Java objects. The specified + * <code>Map</code> will be used for overriding selected SQL type to + * Java class mappings. + * + * @param offset The offset into this array to start returning elements from. + * @param count The requested number of elements to return. + * @param map A mapping of SQL types to Java classes. + * @return The requested portion of the array. + * @exception SQLException If an error occurs. + */ + Object getArray(long index, int count, Map map) throws SQLException; + + /** + * Returns the elements in the array as a <code>ResultSet</code>. + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. + * + * @return The elements of this array as a <code>ResultSet</code>. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet() throws SQLException; + + /** + * This method returns the elements in the array as a <code>ResultSet</code>. + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. The specified <code>Map</code> + * will be used to override selected default mappings of SQL types to + * Java classes. + * + * @param map A mapping of SQL types to Java classes. + * @return The elements of this array as a <code>ResultSet</code>. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(Map map) throws SQLException; + + /** + * This method returns a portion of the array as a <code>ResultSet</code>. + * The returned portion will start at <code>index</code> into the + * array and up to <code>count</code> elements will be returned. + * <p> + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. + * + * @param offset The index into the array to start returning elements from. + * @param length The requested number of elements to return. + * @return The requested elements of this array as a <code>ResultSet</code>. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(long index, int count) throws SQLException; + + /** + * This method returns a portion of the array as a <code>ResultSet</code>. + * The returned portion will start at <code>index</code> into the + * array and up to <code>count</code> elements will be returned. + * + * <p> Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. The specified <code>Map</code> + * will be used to override selected default mappings of SQL types to + * Java classes.</p> + * + * @param offset The index into the array to start returning elements from. + * @param length The requested number of elements to return. + * @param map A mapping of SQL types to Java classes. + * @return The requested elements of this array as a <code>ResultSet</code>. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(long index, int count, Map map) + throws SQLException; +} diff --git a/libjava/classpath/java/sql/BatchUpdateException.java b/libjava/classpath/java/sql/BatchUpdateException.java new file mode 100644 index 00000000000..c2e186548db --- /dev/null +++ b/libjava/classpath/java/sql/BatchUpdateException.java @@ -0,0 +1,141 @@ +/* BatchUpdateException.java -- Exception for batch oriented SQL errors + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * This class extends <code>SQLException</code> to count the successful + * updates in each statement in a batch that was successfully updated prior + * to the error. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BatchUpdateException extends SQLException +{ + static final long serialVersionUID = 5977529877145521757L; + + /** + * This is the array of update counts for the commands which completed + * successfully prior to the error. + */ + private int[] updateCounts; + + /** + * This method initializes a new instance of <code>BatchUpdateException</code> + * with the specified descriptive error message, SQL state, and update count + * information. The vendor specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param SQLState The SQL state information for this error. + * @param vendorCode + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, String SQLState, int vendorCode, + int[] updateCounts) + { + super(message, SQLState, vendorCode); + this.updateCounts = updateCounts; + } + + /** + * This method initializes a new instance of <code>BatchUpdateException</code> + * with the specified descriptive error message, SQL state, and update count + * information. The vendor specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param SQLState The SQL state information for this error. + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, String SQLState, + int[] updateCounts) + { + super(message, SQLState); + this.updateCounts = updateCounts; + } + + /** + * This method initializes a new instance of <code>BatchUpdateException</code> + * with the specified descriptive error message and update count information. + * The SQL state will be initialized to <code>null</code> and the vendor + * specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, int[] updateCounts) + { + super(message); + this.updateCounts = updateCounts; + } + + /** + * Initializes a new instance of <code>BatchUpdateException</code> + * with the specified update count information and no descriptive error + * message. This SQL state will be initialized to <code>null</code> and + * the vendor specific error code will be initialized to 0. + * + * @param updateCounts The update count array. + */ + public BatchUpdateException(int[] updateCounts) + { + this.updateCounts = updateCounts; + } + + /** + * Initializes a new instance of <code>BatchUpdateException</code> + * with no descriptive error message. The SQL state and update count will + * be initialized to <code>null</code> and the vendor specific error code will + * initialized to 0. + */ + public BatchUpdateException() + { + super(); + } + + /** + * This method returns the update count information for this error. If + * not <code>null</code> this is an array of <code>int</code>'s that are + * the update accounts for each command that was successfully executed. + * The array elements are in the order that the commands were executed. + * + * @return The update count information, which may be <code>null</code>. + */ + public int[] getUpdateCounts() + { + return updateCounts; + } +} diff --git a/libjava/classpath/java/sql/Blob.java b/libjava/classpath/java/sql/Blob.java new file mode 100644 index 00000000000..616839d01be --- /dev/null +++ b/libjava/classpath/java/sql/Blob.java @@ -0,0 +1,131 @@ +/* Blob.java -- Access a SQL Binary Large OBject. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This interface specified methods for accessing a SQL BLOB (Binary + * Large OBject) type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public interface Blob +{ + /** + * This method returns the number of bytes in the BLOB. + * + * @return The number of bytes in the BLOB. + * @exception SQLException If an error occurs. + */ + long length() throws SQLException; + + /** + * This method returns up to the requested bytes of this BLOB as a + * <code>byte</code> array. + * + * @param pos The index into the BLOB to start returning bytes from. + * @param length The requested number of bytes to return. + * @return The requested bytes from the BLOB. + * @exception SQLException If an error occurs. + */ + byte[] getBytes(long pos, int length) throws SQLException; + + /** + * This method returns a stream that will read the bytes of the BLOB. + * + * @return A stream that will read the bytes of the BLOB. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream() throws SQLException; + + /** + * This method returns the index into the BLOB at which the first instance + * of the specified bytes occur. The searching starts at the specified + * index into the BLOB. + * + * @param pattern The byte pattern to search for. + * @param offset The index into the BLOB to starting searching for the pattern. + * @return The offset at which the pattern is first found, or -1 if the + * pattern is not found. + * @exception SQLException If an error occurs. + */ + long position(byte[] pattern, long start) throws SQLException; + + /** + * This method returns the index into the BLOB at which the first instance + * of the specified pattern occurs. The searching starts at the specified + * index into this BLOB. The bytes in the specified <code>Blob</code> are + * used as the search pattern. + * + * @param pattern The <code>Blob</code> containing the byte pattern to + * search for. + * @param offset The index into the BLOB to starting searching for the pattern. + * @return The offset at which the pattern is first found, or -1 if the + * pattern is not found. + * @exception SQLException If an error occurs. + */ + long position(Blob pattern, long start) throws SQLException; + + /** + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setBytes(long pos, byte[] bytes) throws SQLException; + + /** + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setBytes(long pos, byte[] bytes, int offset, int len) + throws SQLException; + + /** + * @exception SQLException If an error occurs. + * @since 1.4 + */ + OutputStream setBinaryStream(long pos) throws SQLException; + + /** + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void truncate(long len) throws SQLException; +} diff --git a/libjava/classpath/java/sql/CallableStatement.java b/libjava/classpath/java/sql/CallableStatement.java new file mode 100644 index 00000000000..452294144a0 --- /dev/null +++ b/libjava/classpath/java/sql/CallableStatement.java @@ -0,0 +1,651 @@ +/* CallableStatement.java -- A statement for calling stored procedures. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; +import java.util.Map; + +/** + * This interface provides a mechanism for calling stored procedures. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface CallableStatement extends PreparedStatement +{ + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param index The index of the parameter to register as output. + * @param type The SQL type value from <code>Types</code>. + * @exception SQLException If an error occurs. + */ + void registerOutParameter(int parameterIndex, int sqlType) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type and scale. + * + * @param index The index of the parameter to register as output. + * @param type The SQL type value from <code>Types</code>. + * @param scale The scale of the value that will be returned. + * @exception SQLException If an error occurs. + */ + void registerOutParameter(int parameterIndex, int sqlType, int scale) + throws SQLException; + + /** + * This method tests whether the value of the last parameter that was fetched + * was actually a SQL NULL value. + * + * @return <code>true</code> if the last parameter fetched was a NULL, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>String</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>String</code>. + * @exception SQLException If an error occurs. + */ + String getString(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>boolean</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>boolean</code>. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>byte</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>byte</code>. + * @exception SQLException If an error occurs. + */ + byte getByte(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>short</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>short</code>. + * @exception SQLException If an error occurs. + */ + short getShort(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>int</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>int</code>. + * @exception SQLException If an error occurs. + */ + int getInt(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>long</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>long</code>. + * @exception SQLException If an error occurs. + */ + long getLong(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>float</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>float</code>. + * @exception SQLException If an error occurs. + */ + float getFloat(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>double</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>double</code>. + * @exception SQLException If an error occurs. + */ + double getDouble(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>BigDecimal</code>. + * + * @param parameterIndex The index of the parameter to return. + * @param scale The number of digits to the right of the decimal to return. + * @return The parameter value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + * @deprecated Use getBigDecimal(int parameterIndex) + * or getBigDecimal(String parameterName) instead. + */ + BigDecimal getBigDecimal(int parameterIndex, int scale) + throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * byte array. + * + * @param parameterIndex The index of the parameter to return. + * @return The parameter value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Date</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date getDate(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Time</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time getTime(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Timestamp</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Object</code>. + * + * @param parameterIndex The index of the parameter to return. + * @return The parameter value as an <code>Object</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Object getObject(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>BigDecimal</code>. + * + * @param parameterIndex The index of the parameter to return. + * @return The parameter value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + BigDecimal getBigDecimal(int parameterIndex) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Object</code>. + * + * @param index The index of the parameter to return. + * @param map The mapping to use for conversion from SQL to Java types. + * @return The parameter value as an <code>Object</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Object getObject(int index, Map map) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Ref</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>Ref</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Ref getRef(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Blob</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>Blob</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Blob getBlob(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Clob</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>Clob</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Clob getClob(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>Array</code>. + * + * @param parameterIndex The index of the parameter to return. + * @return The parameter value as a <code>Array</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Array getArray(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Date</code>. + * + * @param parameterIndex The index of the parameter to return. + * @param cal The <code>Calendar</code> to use for timezone and locale. + * @return The parameter value as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Date getDate(int parameterIndex, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Time</code>. + * + * @param parameterIndex The index of the parameter to return. + * @param cal The <code>Calendar</code> to use for timezone and locale. + * @return The parameter value as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Time getTime(int parameterIndex, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * <code>java.sql.Timestamp</code>. + * + * @param index The index of the parameter to return. + * @return The parameter value as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Timestamp getTimestamp(int parameterIndex, Calendar cal) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param index The index of the parameter to register as output. + * @param type The SQL type value from <code>Types</code>. + * @param name The user defined data type name. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + void registerOutParameter(int paramIndex, int sqlType, + String typeName) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param parameterName The name of the parameter to register as output. + * @param sqlType The SQL type value from <code>Types</code>. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String parameterName, int sqlType) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. This version of registerOutParameter is used + * for NUMERIC or DECIMAL types. + * + * @param parameterName The name of the parameter to register as output. + * @param sqlType The SQL type value from <code>Types</code>. + * @param scale Number of digits to the right of the decimal point. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String parameterName, int sqlType, + int scale) + throws SQLException; + + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. This version of registerOutParameter is used + * for user-named or REF types. If the type of the output parameter does + * not have such a type, the typeName argument is ignored. + * + * @param parameterName The name of the parameter to register as output. + * @param sqlType The SQL type value from <code>Types</code>. + * @param typeName The SQL structured type name. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String parameterName, int sqlType, + String typeName) + throws SQLException; + + /** + * @since 1.4 + */ + URL getURL(int parameterIndex) throws SQLException; + + /** + * @since 1.4 + */ + void setURL(String parameterName, URL val) throws SQLException; + + /** + * @since 1.4 + */ + void setNull(String parameterName, int sqlType) throws SQLException; + + /** + * @since 1.4 + */ + void setBoolean(String parameterName, boolean x) throws SQLException; + + /** + * @since 1.4 + */ + void setByte(String parameterName, byte x) throws SQLException; + + /** + * @since 1.4 + */ + void setShort(String parameterName, short x) throws SQLException; + + /** + * @since 1.4 + */ + void setInt(String parameterName, int x) throws SQLException; + + /** + * @since 1.4 + */ + void setLong(String parameterName, long x) throws SQLException; + + /** + * @since 1.4 + */ + void setFloat(String parameterName, float x) throws SQLException; + + /** + * @since 1.4 + */ + void setDouble(String parameterName, double x) throws SQLException; + + /** + * @since 1.4 + */ + void setBigDecimal(String parameterName, BigDecimal x) + throws SQLException; + + /** + * @since 1.4 + */ + void setString(String parameterName, String x) throws SQLException; + + /** + * @since 1.4 + */ + void setBytes(String parameterName, byte[] x) throws SQLException; + + /** + * @since 1.4 + */ + void setDate(String parameterName, Date x) throws SQLException; + + /** + * @since 1.4 + */ + void setTime(String parameterName, Time x) throws SQLException; + + /** + * @since 1.4 + */ + void setTimestamp(String parameterName, Timestamp x) + throws SQLException; + + /** + * @since 1.4 + */ + void setAsciiStream(String parameterName, InputStream x, int length) + throws SQLException; + + /** + * @since 1.4 + */ + void setBinaryStream(String parameterName, InputStream x, int length) + throws SQLException; + + /** + * @since 1.4 + */ + void setObject(String parameterName, Object x, int targetSqlType, + int scale) + throws SQLException; + + /** + * @since 1.4 + */ + void setObject(String parameterName, Object x, int targetSqlType) + throws SQLException; + + /** + * @since 1.4 + */ + void setObject(String parameterName, Object x) throws SQLException; + + /** + * @since 1.4 + */ + void setCharacterStream(String parameterName, Reader reader, + int length) + throws SQLException; + + /** + * @since 1.4 + */ + void setDate(String parameterName, Date x, Calendar cal) + throws SQLException; + + /** + * @since 1.4 + */ + void setTime(String parameterName, Time x, Calendar cal) + throws SQLException; + + /** + * @since 1.4 + */ + void setTimestamp(String parameterName, Timestamp x, Calendar cal) + throws SQLException; + + /** + * @since 1.4 + */ + void setNull(String parameterName, int sqlType, String typeName) + throws SQLException; + + /** + * @since 1.4 + */ + String getString(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + boolean getBoolean(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + byte getByte(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + short getShort(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + int getInt(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + long getLong(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + float getFloat(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + double getDouble(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + byte[] getBytes(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Date getDate(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Time getTime(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Timestamp getTimestamp(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Object getObject(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + BigDecimal getBigDecimal(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Object getObject(String parameterName, Map map) throws SQLException; + + /** + * @since 1.4 + */ + Ref getRef(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Blob getBlob(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Clob getClob(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Array getArray(String parameterName) throws SQLException; + + /** + * @since 1.4 + */ + Date getDate(String parameterName, Calendar cal) throws SQLException; + + /** + * @since 1.4 + */ + Time getTime(String parameterName, Calendar cal) throws SQLException; + + /** + * @since 1.4 + */ + Timestamp getTimestamp(String parameterName, Calendar cal) + throws SQLException; + + /** + * @since 1.4 + */ + URL getURL(String parameterName) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Clob.java b/libjava/classpath/java/sql/Clob.java new file mode 100644 index 00000000000..8789da5967a --- /dev/null +++ b/libjava/classpath/java/sql/Clob.java @@ -0,0 +1,152 @@ +/* Clob.java -- Access Character Large OBjects + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + +/** + * This interface contains methods for accessing a SQL CLOB (Character + * Large OBject) type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Clob +{ + /** + * This method returns the number of characters in the CLOB. + * + * @return The number of characters in the CLOB. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long length() throws SQLException; + + /** + * This method returns the specified portion of the CLOB as a + * <code>String</code>. + * + * @param offset The index into the CLOB (index values start at 1) to + * start returning characters from. + * @param length The requested number of characters to return. + * @return The requested CLOB section, as a <code>String</code>. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + String getSubString(long pos, int length) throws SQLException; + + /** + * This method returns a character stream that reads the contents of the + * CLOB. + * + * @return A character stream to read the CLOB's contents. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Reader getCharacterStream() throws SQLException; + + /** + * This method returns a byte stream that reads the contents of the + * CLOB as a series of ASCII bytes. + * + * @return A stream to read the CLOB's contents. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + InputStream getAsciiStream() throws SQLException; + + /** + * This method returns the index into the CLOB of the first occurrence of + * the specified character pattern (supplied by the caller as a + * <code>String</code>). The search begins at the specified index. + * + * @param searchstr The character pattern to search for, passed as a + * <code>String</code>. + * @param start. The index into the CLOB to start search (indexes start + * at 1). + * @return The index at which the pattern was found (indexes start at 1), + * or -1 if the pattern was not found. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long position(String searchstr, long start) throws SQLException; + + /** + * This method returns the index into the CLOB of the first occurrence of + * the specified character pattern (supplied by the caller as a + * <code>Clob</code>). The search begins at the specified index. + * + * @param searchstr The character pattern to search for, passed as a + * <code>Clob</code>. + * @param start. The index into the CLOB to start search (indexes start + * at 1). + * @return The index at which the pattern was found (indexes start at 1), + * or -1 if the pattern was not found. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long position(Clob searchstr, long start) throws SQLException; + + /** + * @since 1.4 + */ + int setString(long pos, String str) throws SQLException; + + /** + * @since 1.4 + */ + int setString(long pos, String str, int offset, int len) + throws SQLException; + + /** + * @since 1.4 + */ + OutputStream setAsciiStream(long pos) throws SQLException; + + /** + * @since 1.4 + */ + Writer setCharacterStream(long pos) throws SQLException; + + /** + * @since 1.4 + */ + void truncate(long len) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Connection.java b/libjava/classpath/java/sql/Connection.java new file mode 100644 index 00000000000..48ec12dd09f --- /dev/null +++ b/libjava/classpath/java/sql/Connection.java @@ -0,0 +1,420 @@ +/* Connection.java -- Manage a database connection. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.util.Map; + +/** + * This interface provides methods for managing a connection to a database. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Connection +{ + /** + * This transaction isolation level indicates that transactions are not + * supported. + */ + int TRANSACTION_NONE = 0; + + /** + * This transaction isolation level indicates that one transaction can + * read modifications by other transactions before the other transactions + * have committed their changes. This could result in invalid reads. + */ + int TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * This transaction isolation leve indicates that only committed data from + * other transactions will be read. If a transaction reads a row, then + * another transaction commits a change to that row, the first transaction + * would retrieve the changed row on subsequent reads of the same row. + */ + int TRANSACTION_READ_COMMITTED = 2; + + /** + * This transaction isolation level indicates that only committed data from + * other transactions will be read. It also ensures that data read from + * a row will not be different on a subsequent read even if another + * transaction commits a change. + */ + int TRANSACTION_REPEATABLE_READ = 4; + + /** + * This transaction isolation level indicates that only committed data from + * other transactions will be read. It also ensures that data read from + * a row will not be different on a subsequent read even if another + * transaction commits a change. Additionally, rows modified by other + * transactions will not affect the result set returned during subsequent + * executions of the same WHERE clause in this transaction. + */ + int TRANSACTION_SERIALIZABLE = 8; + + /** + * This method creates a new SQL statement. The default result set type + * and concurrency will be used. + * + * @return A new <code>Statement</code> object. + * @exception SQLException If an error occurs. + * @see Statement + */ + Statement createStatement() throws SQLException; + + /** + * This method creates a new <code>PreparedStatement</code> for the specified + * SQL string. This method is designed for use with parameterized + * statements. The default result set type and concurrency will be used. + * + * @param The SQL statement to use in creating this + * <code>PreparedStatement</code>. + * @return A new <code>PreparedStatement</code>. + * @exception SQLException If an error occurs. + * @see PreparedStatement + */ + PreparedStatement prepareStatement(String sql) throws SQLException; + + /** + * This method creates a new <code>CallableStatement</code> for the + * specified SQL string. Thie method is designed to be used with + * stored procedures. The default result set type and concurrency + * will be used. + * + * @param The SQL statement to use in creating this + * <code>CallableStatement</code>. + * @return A new <code>CallableStatement</code>. + * @exception SQLException If an error occurs. + * @see CallableStatement + */ + CallableStatement prepareCall(String sql) throws SQLException; + + /** + * This method converts the specified generic SQL statement into the + * native grammer of the database this object is connected to. + * + * @param The JDBC generic SQL statement. + * @return The native SQL statement. + * @exception SQLException If an error occurs. + */ + String nativeSQL(String sql) throws SQLException; + + /** + * This method turns auto commit mode on or off. In auto commit mode, + * every SQL statement is committed its own transaction. Otherwise a + * transaction must be explicitly committed or rolled back. + * + * @param autoCommit <code>true</code> to enable auto commit mode, + * <code>false</code> to disable it. + * @exception SQLException If an error occurs. + * @see commit + * @see rollback + */ + void setAutoCommit(boolean autoCommit) throws SQLException; + + /** + * This method tests whether or not auto commit mode is currently enabled. + * In auto commit mode, every SQL statement is committed its own transaction. + * Otherwise a transaction must be explicitly committed or rolled back. + * + * @return <code>true</code> if auto commit mode is enabled, + * <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + * + * @see commit + * @see rollback + */ + boolean getAutoCommit() throws SQLException; + + /** + * This method commits any SQL statements executed on this connection since + * the last commit or rollback. + * + * @exception SQLException If an error occurs. + */ + void commit() throws SQLException; + + /** + * This method rolls back any SQL statements executed on this connection + * since the last commit or rollback. + * + * @exception SQLException If an error occurs. + */ + void rollback() throws SQLException; + + /** + * This method immediately closes this database connection. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method tests whether or not this connection has been closed. + * + * @return <code>true</code> if the connection is closed, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isClosed() throws SQLException; + + /** + * This method returns the meta data for this database connection. + * + * @return The meta data for this database. + * @exception SQLException If an error occurs. + * @see DatabaseMetaData + */ + DatabaseMetaData getMetaData() throws SQLException; + + /** + * This method turns read only mode on or off. It may not be called while + * a transaction is in progress. + * + * @param readOnly <code>true</code> if this connection is read only, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + void setReadOnly(boolean readOnly) throws SQLException; + + /** + * This method tests whether or not this connection is in read only mode. + * + * @return <code>true</code> if the connection is read only <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly() throws SQLException; + + /** + * This method sets the name of the catalog in use by this connection. + * Note that this method does nothing if catalogs are not supported by + * this database. + * + * @param catalog The name of the catalog to use for this connection. + * @exception SQLException If an error occurs. + */ + void setCatalog(String catalog) throws SQLException; + + /** + * This method returns the name of the catalog in use by this connection, + * if any. + * + * @return The name of the catalog, or <code>null</code> if one does not + * exist or catalogs are not supported by this database. + * @exception SQLException If an error occurs. + */ + String getCatalog() throws SQLException; + + /** + * This method sets the current transaction isolation mode. This must + * be one of the constants defined in this interface. + * + * @param level The transaction isolation level. + * @exception SQLException If an error occurs. + */ + void setTransactionIsolation(int level) throws SQLException; + + /** + * This method returns the current transaction isolation mode. This will + * be one of the constants defined in this interface. + * + * @return The transaction isolation level. + * @exception SQLException If an error occurs. + */ + int getTransactionIsolation() throws SQLException; + + /** + * This method returns the first warning that occurred on this connection, + * if any. If there were any subsequence warnings, they will be chained + * to the first one. + * + * @return The first <code>SQLWarning</code> that occurred, or + * <code>null</code> if there have been no warnings. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears all warnings that have occurred on this connection. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method creates a new SQL statement with the specified type and + * concurrency. Valid values for these parameters are specified in the + * <code>ResultSet</code> class. + * + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency. The type of concurrency to be used in + * the result set for this statement. + * @return A new <code>Statement</code> object. + * @exception SQLException If an error occurs. + * @see Statement + * @see ResultSet + */ + Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException; + + /** + * This method creates a new <code>PreparedStatement</code> for the specified + * SQL string. This method is designed for use with parameterized + * statements. The specified result set type and concurrency will be used. + * Valid values for these parameters are specified in the + * <code>ResultSet</code> class. + * + * @param The SQL statement to use in creating this + * <code>PreparedStatement</code>. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency. The type of concurrency to be used in + * the result set for this statement. + * @return A new <code>PreparedStatement</code>. + * @exception SQLException If an error occurs. + * @see PreparedStatement + * @see ResultSet + */ + PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException; + + /** + * This method creates a new <code>CallableStatement</code> for the + * specified SQL string. Thie method is designed to be used with + * stored procedures. The specified result set type and concurrency + * will be used. Valid values for these parameters are specified in the + * <code>ResultSet</code> class. + * + * @param The SQL statement to use in creating this + * <code>PreparedStatement</code>. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency. The type of concurrency to be used in + * the result set for this statement. + * @return A new <code>CallableStatement</code>. + * @exception SQLException If an error occurs. + * @see CallableStatement + * @see ResultSet + */ + CallableStatement prepareCall(String sql, int resultSetType, int + resultSetConcurrency) throws SQLException; + + /** + * This method returns the mapping of SQL types to Java classes + * currently in use by this connection. This mapping will have no + * entries unless they have been manually added. + * + * @return The SQL type to Java class mapping. + * @exception SQLException If an error occurs. + */ + Map getTypeMap() throws SQLException; + + /** + * This method sets the mapping table for SQL types to Java classes. + * Any entries in this map override the defaults. + * + * @param map The new SQL mapping table. + * @exception SQLException If an error occurs. + */ + void setTypeMap(Map map) throws SQLException; + + /** + * @since 1.4 + */ + void setHoldability(int holdability) throws SQLException; + + /** + * @since 1.4 + */ + int getHoldability() throws SQLException; + + /** + * @since 1.4 + */ + Savepoint setSavepoint() throws SQLException; + + /** + * @since 1.4 + */ + Savepoint setSavepoint(String name) throws SQLException; + + /** + * @since 1.4 + */ + void rollback(Savepoint savepoint) throws SQLException; + + /** + * @since 1.4 + */ + void releaseSavepoint(Savepoint savepoint) throws SQLException; + + /** + * @since 1.4 + */ + Statement createStatement(int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * @since 1.4 + */ + CallableStatement prepareCall(String sql, int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException; +} diff --git a/libjava/classpath/java/sql/DataTruncation.java b/libjava/classpath/java/sql/DataTruncation.java new file mode 100644 index 00000000000..ec522c0251c --- /dev/null +++ b/libjava/classpath/java/sql/DataTruncation.java @@ -0,0 +1,157 @@ +/* DataTruncation.java -- Warning when data has been truncated. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * This exception is thrown when a piece of data is unexpectedly + * truncated in JDBC. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DataTruncation extends SQLWarning +{ + static final long serialVersionUID = 6464298989504059473L; + + /** + * The original size of the data. + */ + private int dataSize; + + /** + * The index of the parameter or column whose value was truncated. + */ + private int index; + + /** + * Indicates whether or not a parameter value was truncated. + */ + private boolean parameter; + + /** + * Indicates whether or not a data column value was truncated. + */ + private boolean read; + + /** + * This is the size of the data after truncation. + */ + private int transferSize; + + /** + * This method initializes a new instance of <code>DataTruncation</code> + * with the specified values. The descriptive error message for this + * exception will be "Data truncation", the SQL state will be "01004" + * and the vendor specific error code will be set to 0. + * + * @param index The index of the parameter or column that was truncated. + * @param parameter <code>true</code> if a parameter was truncated, + * <code>false</code> otherwise. + * @param read <code>true</code> if a data column was truncated, + * <code>false</code> otherwise. + * @param dataSize The original size of the data. + * @param transferSize The size of the data after truncation. + */ + public DataTruncation(int index, boolean parameter, boolean read, int + dataSize, int transferSize) + { + super("Data truncation", "01004"); + + this.index = index; + this.parameter = parameter; + this.read = read; + this.dataSize = dataSize; + this.transferSize = transferSize; + } + + /** + * This method returns the index of the column or parameter that was + * truncated. + * + * @return The index of the column or parameter that was truncated. + */ + public int getIndex() + { + return index; + } + + /** + * This method determines whether or not it was a parameter that was + * truncated. + * + * @return <code>true</code> if a parameter was truncated, <code>false</code> + * otherwise. + */ + public boolean getParameter() + { + return parameter; + } + + /** + * This method determines whether or not it was a column that was + * truncated. + * + * @return <code>true</code> if a column was truncated, <code>false</code> + * otherwise. + */ + public boolean getRead() + { + return read; + } + + /** + * This method returns the original size of the parameter or column that + * was truncated. + * + * @return The original size of the parameter or column that was truncated. + */ + public int getDataSize() + { + return dataSize; + } + + /** + * This method returns the size of the parameter or column after it was + * truncated. + * + * @return The size of the parameter or column after it was truncated. + */ + public int getTransferSize() + { + return transferSize; + } +} diff --git a/libjava/classpath/java/sql/DatabaseMetaData.java b/libjava/classpath/java/sql/DatabaseMetaData.java new file mode 100644 index 00000000000..d34c4e2f927 --- /dev/null +++ b/libjava/classpath/java/sql/DatabaseMetaData.java @@ -0,0 +1,2214 @@ +/* DatabaseMetaData.java -- Information about the database itself. + Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +public interface DatabaseMetaData +{ + /** + * It is unknown whether or not the procedure returns a result. + */ + int procedureResultUnknown = 0; + + /** + * The procedure does not return a result. + */ + int procedureNoResult = 1; + + /** + * The procedure returns a result. + */ + int procedureReturnsResult = 2; + + /** + * The column type is unknown. + */ + int procedureColumnUnknown = 0; + + /** + * The column type is input. + */ + int procedureColumnIn = 1; + + /** + * The column type is input/output. + */ + int procedureColumnInOut = 2; + + /** + * The column type is output + */ + int procedureColumnOut = 4; + + /** + * The column is used for return values. + */ + int procedureColumnReturn = 5; + + /** + * The column is used for storing results + */ + int procedureColumnResult = 3; + + /** + * NULL values are not allowed. + */ + int procedureNoNulls = 0; + + /** + * NULL values are allowed. + */ + int procedureNullable = 1; + + /** + * It is unknown whether or not NULL values are allowed. + */ + int procedureNullableUnknown = 2; + + /** + * The column does not allow NULL + */ + int columnNoNulls = 0; + + /** + * The column does allow NULL + */ + int columnNullable = 1; + + /** + * It is unknown whether or not the column allows NULL + */ + int columnNullableUnknown = 2; + + /** + * The best row's scope is only guaranteed to be valid so long as the + * row is actually being used. + */ + int bestRowTemporary = 0; + + /** + * The best row identifier is valid to the end of the transaction. + */ + int bestRowTransaction = 1; + + /** + * The best row identifier is valid to the end of the session. + */ + int bestRowSession = 2; + + /** + * The best row may or may not be a pseudo-column. + */ + int bestRowUnknown = 0; + + /** + * The best row identifier is not a pseudo-column. + */ + int bestRowNotPseudo = 1; + + /** + * The best row identifier is a pseudo-column. + */ + int bestRowPseudo = 2; + + /** + * It is unknown whether or not the version column is a pseudo-column. + */ + int versionColumnUnknown = 0; + + /** + * The version column is not a pseudo-column + */ + int versionColumnNotPseudo = 1; + + /** + * The version column is a pseudo-column + */ + int versionColumnPseudo = 2; + + /** + * Foreign key changes are cascaded in updates or deletes. + */ + int importedKeyCascade = 0; + + /** + * Column may not be updated or deleted in use as a foreign key. + */ + int importedKeyRestrict = 1; + + /** + * When primary key is updated or deleted, the foreign key is set to NULL. + */ + int importedKeySetNull = 2; + + /** + * If the primary key is a foreign key, it cannot be udpated or deleted. + */ + int importedKeyNoAction = 3; + + /** + * If the primary key is updated or deleted, the foreign key is set to + * a default value. + */ + int importedKeySetDefault = 4; + + /** + * Wish I knew what this meant. + */ + int importedKeyInitiallyDeferred = 5; + + /** + * Wish I knew what this meant. + */ + int importedKeyInitiallyImmediate = 6; + + /** + * Wish I knew what this meant. + */ + int importedKeyNotDeferrable = 7; + + /** + * A NULL value is not allowed for this data type. + */ + int typeNoNulls = 0; + + /** + * A NULL value is allowed for this data type. + */ + int typeNullable = 1; + + /** + * It is unknown whether or not NULL values are allowed for this data type. + */ + int typeNullableUnknown = 2; + + /** + * Where clauses are not supported for this type. + */ + int typePredNone = 0; + + /** + * Only "WHERE..LIKE" style WHERE clauses are allowed on this data type. + */ + int typePredChar = 1; + + /** + * All WHERE clauses except "WHERE..LIKE" style are allowed on this data type. + */ + int typePredBasic = 2; + + /** + * Any type of WHERE clause is allowed for this data type. + */ + int typeSearchable = 3; + + /** + * This column contains table statistics. + */ + short tableIndexStatistic = 0; + + /** + * This table index is clustered. + */ + short tableIndexClustered = 1; + + /** + * This table index is hashed. + */ + short tableIndexHashed = 2; + + /** + * This table index is of another type. + */ + short tableIndexOther = 3; + + short attributeNoNulls = 0; + + short attributeNullable = 1; + + short attributeNullableUnknown = 2; + + int sqlStateXOpen = 1; + + int sqlStateSQL99 = 2; + + /** + * This method tests whether or not all the procedures returned by + * the <code>getProcedures</code> method can be called by this user. + * + * @return <code>true</code> if all the procedures can be called, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean allProceduresAreCallable() throws SQLException; + + /** + * This method tests whether or not all the table returned by the + * <code>getTables</code> method can be selected by this user. + * + * @return <code>true</code> if all the procedures can be called, + * <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean allTablesAreSelectable() throws SQLException; + + /** + * This method returns the URL for this database. + * + * @return The URL string for this database, or <code>null</code> if it + * is not known. + * @exception SQLException If an error occurs. + */ + String getURL() throws SQLException; + + /** + * This method returns the database username for this connection. + * + * @return The database username. + * @exception SQLException If an error occurs. + */ + String getUserName() throws SQLException; + + /** + * This method tests whether or not the database is in read only mode. + * + * @return <code>true</code> if the database is in read only mode, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly() throws SQLException; + + /** + * This method tests whether or not NULL's sort as high values. + * + * @return <code>true</code> if NULL's sort as high values, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedHigh() throws SQLException; + + /** + * This method tests whether or not NULL's sort as low values. + * + * @return <code>true</code> if NULL's sort as low values, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedLow() throws SQLException; + + /** + * This method tests whether or not NULL's sort as high values. + * + * @return <code>true</code> if NULL's sort as high values, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedAtStart() throws SQLException; + + /** + * This method test whether or not NULL's are sorted to the end + * of the list regardless of ascending or descending sort order. + * + * @return <code>true</code> if NULL's always sort to the end, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedAtEnd() throws SQLException; + + /** + * This method returns the name of the database product. + * + * @return The database product. + * @exception SQLException If an error occurs. + */ + String getDatabaseProductName() throws SQLException; + + /** + * This method returns the version of the database product. + * + * @return The version of the database product. + * @exception SQLException If an error occurs. + */ + String getDatabaseProductVersion() throws SQLException; + + /** + * This method returns the name of the JDBC driver. + * + * @return The name of the JDBC driver. + * @exception SQLException If an error occurs. + */ + String getDriverName() throws SQLException; + + /** + * This method returns the version of the JDBC driver. + * + * @return The version of the JDBC driver. + * @exception SQLException If an error occurs. + */ + String getDriverVersion() throws SQLException; + + /** + * This method returns the major version number of the JDBC driver. + * + * @return The major version number of the JDBC driver. + */ + int getDriverMajorVersion(); + + /** + * This method returns the minor version number of the JDBC driver. + * + * @return The minor version number of the JDBC driver. + */ + int getDriverMinorVersion(); + + /** + * This method tests whether or not the database uses local files to + * store tables. + * + * @return <code>true</code> if the database uses local files, + * <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean usesLocalFiles() throws SQLException; + + /** + * This method tests whether or not the database uses a separate file for + * each table. + * + * @return <code>true</code> if the database uses a separate file for each + * table <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean usesLocalFilePerTable() throws SQLException; + + /** + * This method tests whether or not the database supports identifiers + * with mixed case. + * + * @return <code>true</code> if the database supports mixed case identifiers, + * <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean supportsMixedCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * identifiers as all upper case. + * + * @return <code>true</code> if the database treats all identifiers as + * upper case, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesUpperCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * identifiers as all lower case. + * + * @return <code>true</code> if the database treats all identifiers as + * lower case, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesLowerCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database stores mixed case + * identifers even if it treats them as case insensitive. + * + * @return <code>true</code> if the database stores mixed case identifiers, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesMixedCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database supports quoted identifiers + * with mixed case. + * + * @return <code>true</code> if the database supports mixed case quoted + * identifiers, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMixedCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * quoted identifiers as all upper case. + * + * @return <code>true</code> if the database treats all quoted identifiers + * as upper case, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesUpperCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * quoted identifiers as all lower case. + * + * @return <code>true</code> if the database treats all quoted identifiers + * as lower case, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesLowerCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database stores mixed case + * quoted identifers even if it treats them as case insensitive. + * + * @return <code>true</code> if the database stores mixed case quoted + * identifiers, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesMixedCaseQuotedIdentifiers() throws SQLException; + + /** + * This metohd returns the quote string for SQL identifiers. + * + * @return The quote string for SQL identifers, or a space if quoting + * is not supported. + * @exception SQLException If an error occurs. + */ + String getIdentifierQuoteString() throws SQLException; + + /** + * This method returns a comma separated list of all the SQL keywords in + * the database that are not in SQL92. + * + * @return The list of SQL keywords not in SQL92. + * @exception SQLException If an error occurs. + */ + String getSQLKeywords() throws SQLException; + + /** + * This method returns a comma separated list of math functions. + * + * @return The list of math functions. + * @exception SQLException If an error occurs. + */ + String getNumericFunctions() throws SQLException; + + /** + * This method returns a comma separated list of string functions. + * + * @return The list of string functions. + * @exception SQLException If an error occurs. + */ + String getStringFunctions() throws SQLException; + + /** + * This method returns a comma separated list of of system functions. + * + * @return A comma separated list of system functions. + * @exception SQLException If an error occurs. + */ + String getSystemFunctions() throws SQLException; + + /** + * This method returns comma separated list of time/date functions. + * + * @return The list of time/date functions. + * @exception SQLException If an error occurs. + */ + String getTimeDateFunctions() throws SQLException; + + /** + * This method returns the string used to escape wildcards in search strings. + * + * @return The string used to escape wildcards in search strings. + * @exception SQLException If an error occurs. + */ + String getSearchStringEscape() throws SQLException; + + /** + * This methods returns non-standard characters that can appear in + * unquoted identifiers. + * + * @return Non-standard characters that can appear in unquoted identifiers. + * @exception SQLException If an error occurs. + */ + String getExtraNameCharacters() throws SQLException; + + /** + * This method tests whether or not the database supports + * "ALTER TABLE ADD COLUMN" + * + * @return <code>true</code> if column add supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsAlterTableWithAddColumn() throws SQLException; + + /** + * This method tests whether or not the database supports + * "ALTER TABLE DROP COLUMN" + * + * @return <code>true</code> if column drop supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsAlterTableWithDropColumn() throws SQLException; + + /** + * This method tests whether or not column aliasing is supported. + * + * @return <code>true</code> if column aliasing is supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsColumnAliasing() throws SQLException; + + /** + * This method tests whether the concatenation of a NULL and non-NULL + * value results in a NULL. This will always be true in fully JDBC compliant + * drivers. + * + * @return <code>true</code> if concatenating NULL and a non-NULL value + * returns a NULL, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullPlusNonNullIsNull() throws SQLException; + + /** + * Tests whether or not CONVERT is supported. + * + * @return <code>true</code> if CONVERT is supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsConvert() throws SQLException; + + /** + * This method tests whether or not CONVERT can be performed between the + * specified types. The types are contants from <code>Types</code>. + * + * @param fromType The SQL type to convert from. + * @param toType The SQL type to convert to. + * @return <code>true</code> if the conversion can be performed, + * <code>false</code> otherwise. + * @see Types + */ + boolean supportsConvert(int fromType, int toType) throws + SQLException; + + /** + * This method tests whether or not table correlation names are + * supported. This will be always be <code>true</code> in a fully JDBC + * compliant driver. + * + * @return <code>true</code> if table correlation names are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTableCorrelationNames() throws SQLException; + + /** + * This method tests whether correlation names must be different from the + * name of the table. + * + * @return <code>true</code> if the correlation name must be different from + * the table name, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDifferentTableCorrelationNames() throws SQLException; + + /** + * This method tests whether or not expressions are allowed in an + * ORDER BY lists. + * + * @return <code>true</code> if expressions are allowed in ORDER BY + * lists, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsExpressionsInOrderBy() throws SQLException; + + /** + * This method tests whether or ORDER BY on a non-selected column is + * allowed. + * + * @return <code>true</code> if a non-selected column can be used in an + * ORDER BY, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOrderByUnrelated() throws SQLException; + + /** + * This method tests whether or not GROUP BY is supported. + * + * @return <code>true</code> if GROUP BY is supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupBy() throws SQLException; + + /** + * This method tests whether GROUP BY on a non-selected column is + * allowed. + * + * @return <code>true</code> if a non-selected column can be used in a + * GROUP BY, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupByUnrelated() throws SQLException; + + /** + * This method tests whether or not a GROUP BY can add columns not in the + * select if it includes all the columns in the select. + * + * @return <code>true</code> if GROUP BY an add columns provided it includes + * all columns in the select, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupByBeyondSelect() throws SQLException; + + /** + * This method tests whether or not the escape character is supported in + * LIKE expressions. A fully JDBC compliant driver will always return + * <code>true</code>. + * + * @return <code>true</code> if escapes are supported in LIKE expressions, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsLikeEscapeClause() throws SQLException; + + /** + * This method tests whether multiple result sets for a single statement are + * supported. + * + * @return <code>true</code> if multiple result sets are supported for a + * single statement, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMultipleResultSets() throws SQLException; + + /** + * This method test whether or not multiple transactions may be open + * at once, as long as they are on different connections. + * + * @return <code>true</code> if multiple transactions on different + * connections are supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMultipleTransactions() throws SQLException; + + /** + * This method tests whether or not columns can be defined as NOT NULL. A + * fully JDBC compliant driver always returns <code>true</code>. + * + * @return <code>true</code> if NOT NULL columns are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsNonNullableColumns() throws SQLException; + + /** + * This method tests whether or not the minimum grammer for ODBC is supported. + * A fully JDBC compliant driver will always return <code>true</code>. + * + * @return <code>true</code> if the ODBC minimum grammar is supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMinimumSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the core grammer for ODBC is supported. + * + * @return <code>true</code> if the ODBC core grammar is supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCoreSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the extended grammer for ODBC is supported. + * + * @return <code>true</code> if the ODBC extended grammar is supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsExtendedSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the ANSI92 entry level SQL + * grammar is supported. A fully JDBC compliant drivers must return + * <code>true</code>. + * + * @return <code>true</code> if the ANSI92 entry level SQL grammar is + * supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92EntryLevelSQL() throws SQLException; + + /** + * This method tests whether or not the ANSI92 intermediate SQL + * grammar is supported. + * + * @return <code>true</code> if the ANSI92 intermediate SQL grammar is + * supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92IntermediateSQL() throws SQLException; + + /** + * This method tests whether or not the ANSI92 full SQL + * grammar is supported. + * + * @return <code>true</code> if the ANSI92 full SQL grammar is + * supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92FullSQL() throws SQLException; + + /** + * This method tests whether or not the SQL integrity enhancement + * facility is supported. + * + * @return <code>true</code> if the integrity enhancement facility is + * supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsIntegrityEnhancementFacility() throws SQLException; + + /** + * This method tests whether or not the database supports outer joins. + * + * @return <code>true</code> if outer joins are supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOuterJoins() throws SQLException; + + /** + * This method tests whether or not the database supports full outer joins. + * + * @return <code>true</code> if full outer joins are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsFullOuterJoins() throws SQLException; + + /** + * This method tests whether or not the database supports limited outer joins. + * + * @return <code>true</code> if limited outer joins are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsLimitedOuterJoins() throws SQLException; + + /** + * This method returns the vendor's term for "schema". + * + * @return The vendor's term for schema. + * @exception SQLException if an error occurs. + */ + String getSchemaTerm() throws SQLException; + + /** + * This method returns the vendor's term for "procedure". + * + * @return The vendor's term for procedure. + * @exception SQLException if an error occurs. + */ + String getProcedureTerm() throws SQLException; + + /** + * This method returns the vendor's term for "catalog". + * + * @return The vendor's term for catalog. + * @exception SQLException if an error occurs. + */ + String getCatalogTerm() throws SQLException; + + /** + * This method tests whether a catalog name appears at the beginning of + * a fully qualified table name. + * + * @return <code>true</code> if the catalog name appears at the beginning, + * <code>false</code> if it appears at the end. + * @exception SQLException If an error occurs. + */ + boolean isCatalogAtStart() throws SQLException; + + /** + * This method returns the separator between the catalog name and the + * table name. + * + * @return The separator between the catalog name and the table name. + * @exception SQLException If an error occurs. + */ + String getCatalogSeparator() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a data + * manipulation statement. + * + * @return <code>true</code> if a catalog name can appear in a data + * manipulation statement, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInDataManipulation() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a procedure + * call + * + * @return <code>true</code> if a catalog name can appear in a procedure + * call, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInProcedureCalls() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a table definition. + * + * @return <code>true</code> if a catalog name can appear in a table + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInTableDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in an index definition. + * + * @return <code>true</code> if a catalog name can appear in an index + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInIndexDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in privilege definitions. + * + * @return <code>true</code> if a catalog name can appear in privilege + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInPrivilegeDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a data + * manipulation statement. + * + * @return <code>true</code> if a catalog name can appear in a data + * manipulation statement, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInDataManipulation() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a procedure + * call + * + * @return <code>true</code> if a catalog name can appear in a procedure + * call, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInProcedureCalls() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a table definition. + * + * @return <code>true</code> if a catalog name can appear in a table + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInTableDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in an index definition. + * + * @return <code>true</code> if a catalog name can appear in an index + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInIndexDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in privilege definitions. + * + * @return <code>true</code> if a catalog name can appear in privilege + * definition, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException; + + /** + * This method tests whether or not that database supports positioned + * deletes. + * + * @return <code>true</code> if positioned deletes are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsPositionedDelete() throws SQLException; + + /** + * This method tests whether or not that database supports positioned + * updates. + * + * @return <code>true</code> if positioned updates are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsPositionedUpdate() throws SQLException; + + /** + * This method tests whether or not SELECT FOR UPDATE is supported by the + * database. + * + * @return <code>true</code> if SELECT FOR UPDATE is supported + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSelectForUpdate() throws SQLException; + + /** + * This method tests whether or not stored procedures are supported on + * this database. + * + * @return <code>true</code> if stored procedures are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsStoredProcedures() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in comparisons. + * A fully JDBC compliant driver will always return <code>true</code>. + * + * @return <code>true</code> if subqueries are allowed in comparisons, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInComparisons() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in exists + * expressions. A fully JDBC compliant driver will always return + * <code>true</code>. + * + * @return <code>true</code> if subqueries are allowed in exists + * expressions, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInExists() throws SQLException; + + /** + * This method tests whether subqueries are allowed in IN statements. + * A fully JDBC compliant driver will always return <code>true</code>. + * + * @return <code>true</code> if the driver supports subqueries in IN + * statements, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInIns() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in quantified + * expressions. A fully JDBC compliant driver will always return + * <code>true</code>. + * + * @return <code>true</code> if subqueries are allowed in quantified + * expressions, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInQuantifieds() throws SQLException; + + /** + * This method test whether or not correlated subqueries are allowed. A + * fully JDBC compliant driver will always return <code>true</code>. + * + * @return <code>true</code> if correlated subqueries are allowed, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCorrelatedSubqueries() throws SQLException; + + /** + * This method tests whether or not the UNION statement is supported. + * + * @return <code>true</code> if UNION is supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsUnion() throws SQLException; + + /** + * This method tests whether or not the UNION ALL statement is supported. + * + * @return <code>true</code> if UNION ALL is supported, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsUnionAll() throws SQLException; + + /** + * This method tests whether or not the database supports cursors + * remaining open across commits. + * + * @return <code>true</code> if cursors can remain open across commits, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenCursorsAcrossCommit() throws SQLException; + + /** + * This method tests whether or not the database supports cursors + * remaining open across rollbacks. + * + * @return <code>true</code> if cursors can remain open across rollbacks, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenCursorsAcrossRollback() throws SQLException; + + /** + * This method tests whether or not the database supports statements + * remaining open across commits. + * + * @return <code>true</code> if statements can remain open across commits, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenStatementsAcrossCommit() throws SQLException; + + /** + * This method tests whether or not the database supports statements + * remaining open across rollbacks. + * + * @return <code>true</code> if statements can remain open across rollbacks, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenStatementsAcrossRollback() throws SQLException; + + /** + * This method returns the number of hex characters allowed in an inline + * binary literal. + * + * @return The number of hex characters allowed in a binary literal, 0 meaning + * either an unknown or unlimited number. + * @exception SQLException If an error occurs. + */ + int getMaxBinaryLiteralLength() throws SQLException; + + /** + * This method returns the maximum length of a character literal. + * + * @return The maximum length of a character literal. + * @exception SQLException If an error occurs. + */ + int getMaxCharLiteralLength() throws SQLException; + + /** + * This method returns the maximum length of a column name. + * + * @return The maximum length of a column name. + * @exception SQLException If an error occurs. + */ + int getMaxColumnNameLength() throws SQLException; + + /** + * This method returns the maximum number of columns in a GROUP BY statement. + * + * @return The maximum number of columns in a GROUP BY statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInGroupBy() throws SQLException; + + /** + * This method returns the maximum number of columns in an index. + * + * @return The maximum number of columns in an index. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInIndex() throws SQLException; + + /** + * This method returns the maximum number of columns in an ORDER BY statement. + * + * @return The maximum number of columns in an ORDER BY statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInOrderBy() throws SQLException; + + /** + * This method returns the maximum number of columns in a SELECT statement. + * + * @return The maximum number of columns in a SELECT statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInSelect() throws SQLException; + + /** + * This method returns the maximum number of columns in a table. + * + * @return The maximum number of columns in a table. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInTable() throws SQLException; + + /** + * This method returns the maximum number of connections this client + * can have to the database. + * + * @return The maximum number of database connections. + * @SQLException If an error occurs. + */ + int getMaxConnections() throws SQLException; + + /** + * This method returns the maximum length of a cursor name. + * + * @return The maximum length of a cursor name. + * @exception SQLException If an error occurs. + */ + int getMaxCursorNameLength() throws SQLException; + + /** + * This method returns the maximum length of an index. + * + * @return The maximum length of an index. + * @exception SQLException If an error occurs. + */ + int getMaxIndexLength() throws SQLException; + + /** + * This method returns the maximum length of a schema name. + * + * @return The maximum length of a schema name. + * @exception SQLException If an error occurs. + */ + int getMaxSchemaNameLength() throws SQLException; + + /** + * This method returns the maximum length of a procedure name. + * + * @return The maximum length of a procedure name. + * @exception SQLException If an error occurs. + */ + int getMaxProcedureNameLength() throws SQLException; + + /** + * This method returns the maximum length of a catalog name. + * + * @return The maximum length of a catalog name. + * @exception SQLException If an error occurs. + */ + int getMaxCatalogNameLength() throws SQLException; + + /** + * This method returns the maximum size of a row in bytes. + * + * @return The maximum size of a row. + * @exception SQLException If an error occurs. + */ + int getMaxRowSize() throws SQLException; + + /** + * This method tests whether or not the maximum row size includes BLOB's + * + * @return <code>true</code> if the maximum row size includes BLOB's, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean doesMaxRowSizeIncludeBlobs() throws SQLException; + + /** + * This method includes the maximum length of a SQL statement. + * + * @return The maximum length of a SQL statement. + * @exception SQLException If an error occurs. + */ + int getMaxStatementLength() throws SQLException; + + /** + * This method returns the maximum number of statements that can be + * active at any time. + * + * @return The maximum number of statements that can be active at any time. + * @exception SQLException If an error occurs. + */ + int getMaxStatements() throws SQLException; + + /** + * This method returns the maximum length of a table name. + * + * @return The maximum length of a table name. + * @exception SQLException If an error occurs. + */ + int getMaxTableNameLength() throws SQLException; + + /** + * This method returns the maximum number of tables that may be referenced + * in a SELECT statement. + * + * @return The maximum number of tables allowed in a SELECT statement. + * @exception SQLException If an error occurs. + */ + int getMaxTablesInSelect() throws SQLException; + + /** + * This method returns the maximum length of a user name. + * + * @return The maximum length of a user name. + * @exception SQLException If an error occurs. + */ + int getMaxUserNameLength() throws SQLException; + + /** + * This method returns the default transaction isolation level of the + * database. + * + * @return The default transaction isolation level of the database. + * @exception SQLException If an error occurs. + * @see Connection + */ + int getDefaultTransactionIsolation() throws SQLException; + + /** + * This method tests whether or not the database supports transactions. + * + * @return <code>true</code> if the database supports transactions, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTransactions() throws SQLException; + + /** + * This method tests whether or not the database supports the specified + * transaction isolation level. + * + * @param level The transaction isolation level. + * + * @return <code>true</code> if the specified transaction isolation level + * is supported, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTransactionIsolationLevel(int level) throws + SQLException; + + /** + * This method tests whether or not DDL and DML statements allowed within + * the same transaction. + * + * @return <code>true</code> if DDL and DML statements are allowed in the + * same transaction, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDataDefinitionAndDataManipulationTransactions() + throws SQLException; + + /** + * This method tests whether or not only DML statement are allowed + * inside a transaction. + * + * @return <code>true</code> if only DML statements are allowed in + * transactions, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDataManipulationTransactionsOnly() throws + SQLException; + + /** + * This method tests whether or not a DDL statement will cause the + * current transaction to be automatically committed. + * + * @return <code>true</code> if DDL causes an immediate transaction commit, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean dataDefinitionCausesTransactionCommit() throws SQLException; + + /** + * This method tests whether or not DDL statements are ignored in + * transactions. + * + * @return <code>true</code> if DDL statements are ignored in transactions, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean dataDefinitionIgnoredInTransactions() throws SQLException; + + /** + * This method returns a list of all the stored procedures matching the + * specified pattern in the given schema and catalog. This is returned + * a <code>ResultSet</code> with the following columns: + * <p> + * <ol> + * <li>PROCEDURE_CAT - The catalog the procedure is in, which may be + * <code>null</code>.</li> + * <li>PROCEDURE_SCHEM - The schema the procedures is in, which may be + * <code>null</code>.</li> + * <li>PROCEDURE_NAME - The name of the procedure.</li> + * <li>Unused</li> + * <li>Unused</li> + * <li>Unused</li> + * <li>REMARKS - A description of the procedure</li> + * <li>PROCEDURE_TYPE - Indicates the return type of the procedure, which + * is one of the contstants defined in this class + * (<code>procedureResultUnknown</code>, <code>procedureNoResult</code>, or + * <code>procedureReturnsResult</code>).</li> + * </ol> + * + * @param catalog The name of the catalog to return stored procedured from, + * or "" to return procedures from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return stored + * procedures from, or "" to return procedures from all schemas. + * @param namePattern The pattern of procedures names to return. + * @returns A <code>ResultSet</code> with all the requested procedures. + * @exception SQLException If an error occurs. + */ + ResultSet getProcedures(String catalog, String schemaPattern, String + procedureNamePattern) throws SQLException; + + /** + * This method returns a list of the parameter and result columns for + * the requested stored procedures. This is returned in the form of a + * <code>ResultSet</code> with the following columns: + * <p> + * <ol> + * <li>PROCEDURE_CAT - The catalog the procedure is in, which may be + * <code>null</code>.</li> + * <li>PROCEDURE_SCHEM - The schema the procedures is in, which may be + * <code>null</code>.</li> + * <li>PROCEDURE_NAME - The name of the procedure.</li> + * <li>COLUMN_NAME - The name of the column</li> + * <li>COLUMN_TYPE - The type of the column, which will be one of the + * contants defined in this class (<code>procedureColumnUnknown</code>, + * <code>procedureColumnIn</code>, <code>procedureColumnInOut</code>, + * <code>procedureColumnOut</code>, <code>procedureColumnReturn</code>, + * or <code>procedureColumnResult</code>).</li> + * <li>DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in <code>Types</code>.</li> + * <li>TYPE_NAME - The string name of the data type for this column.</li> + * <li>PRECISION - The precision of the column.</li> + * <li>LENGTH - The length of the column in bytes</li> + * <li>SCALE - The scale of the column.</li> + * <li>RADIX - The radix of the column.</li> + * <li>NULLABLE - Whether or not the column is NULLABLE. This is one of + * the constants defined in this class (<code>procedureNoNulls</code>, + * <code>procedureNullable</code>, or <code>procedureNullableUnknown</code>)</li> + * <li>REMARKS - A description of the column.</li> + * </ol> + * + * @param catalog The name of the catalog to return stored procedured from, + * or "" to return procedures from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return stored + * procedures from, or "" to return procedures from all schemas. + * @param namePattern The pattern of procedures names to return. + * @param columnPattern The pattern of column names to return. + * @returns A <code>ResultSet</code> with all the requested procedures. + * @exception SQLException If an error occurs. + */ + ResultSet getProcedureColumns(String catalog, String schemaPattern, + String procedureNamePattern, String columnNamePattern) throws + SQLException; + + /** + * This method returns a list of the requested table as a + * <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>TABLE_CAT - The catalog the table is in, which may be <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema the table is in, which may be <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>TABLE_TYPE - A string describing the table type. This will be one + * of the values returned by the <code>getTableTypes()</code> method.</li> + * <li>REMARKS - Comments about the table.</li> + * </ol> + * + * @param catalog The name of the catalog to return tables from, + * or "" to return tables from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return tables + * from, or "" to return tables from all schemas. + * @param namePattern The pattern of table names to return. + * @param types The list of table types to include; null returns all types. + * @returns A <code>ResultSet</code> with all the requested tables. + * @exception SQLException If an error occurs. + */ + ResultSet getTables(String catalog, String schemaPattern, String + tableNamePattern, String[] types) throws SQLException; + + /** + * This method returns the list of database schemas as a + * <code>ResultSet</code>, with one column - TABLE_SCHEM - that is the + * name of the schema. + * + * @return A <code>ResultSet</code> with all the requested schemas. + * @exception SQLException If an error occurs. + */ + ResultSet getSchemas() throws SQLException; + + /** + * This method returns the list of database catalogs as a + * <code>ResultSet</code> with one column - TABLE_CAT - that is the + * name of the catalog. + * + * @return A <code>ResultSet</code> with all the requested catalogs. + * @exception SQLException If an error occurs. + */ + ResultSet getCatalogs() throws SQLException; + + /** + * This method returns the list of database table types as a + * <code>ResultSet</code> with one column - TABLE_TYPE - that is the + * name of the table type. + * + * @return A <code>ResultSet</code> with all the requested table types. + * @exception SQLException If an error occurs. + */ + ResultSet getTableTypes() throws SQLException; + + /** + * This method returns a list of the tables columns for + * the requested tables. This is returned in the form of a + * <code>ResultSet</code> with the following columns: + * <p> + * <ol> + * <li>TABLE_CAT - The catalog the table is in, which may be + * <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema the tables is in, which may be + * <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>COLUMN_NAME - The name of the column</li> + * <li>DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in <code>Types</code>.</li> + * <li>TYPE_NAME - The string name of the data type for this column.</li> + * <li>COLUMN_SIZE - The size of the column.</li> + * <li>Unused</li> + * <li>NUM_PREC_RADIX - The radix of the column.</li> + * <li>NULLABLE - Whether or not the column is NULLABLE. This is one of + * the constants defined in this class (<code>tableNoNulls</code>, + * <code>tableNullable</code>, or <code>tableNullableUnknown</code>)</li> + * <li>REMARKS - A description of the column.</li> + * <li>COLUMN_DEF - The default value for the column, may be <code>null</code>.</li> + * <li>SQL_DATA_TYPE - Unused</li> + * <li>SQL_DATETIME_SUB - Unused</li> + * <li>CHAR_OCTET_LENGTH - For character columns, the maximum number of bytes + * in the column.</li> + * <li>ORDINAL_POSITION - The index of the column in the table.</li> + * <li>IS_NULLABLE - "NO" means no, "YES" means maybe, and an empty string + * means unknown.</li> + * </ol> + * + * @param catalog The name of the catalog to return table from, + * or "" to return tables from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return + * tables from, or "" to return tables from all schemas. + * @param namePattern The pattern of tables names to return. + * @param columnPattern The pattern of column names to return. + * @returns A <code>ResultSet</code> with all the requested tables. + * @exception SQLException If an error occurs. + */ + ResultSet getColumns(String catalog, String schemaPattern, String + tableNamePattern, String columnNamePattern) throws SQLException; + + /** + * This method returns the access rights that have been granted to the + * requested columns. This information is returned as a <code>ResultSet</code> + * with the following columns: + * + * <ol> + * <li>TABLE_CAT - The catalog the table is in, which may be + * <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema the tables is in, which may be + * <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>COLUMN_NAME - The name of the column.</li> + * <li>GRANTOR - The entity that granted the access.</li> + * <li>GRANTEE - The entity granted the access.</li> + * <li>PRIVILEGE - The name of the privilege granted.</li> + * <li>IS_GRANTABLE - "YES" if the grantee can grant the privilege to + * others, "NO" if not, and <code>null</code> if unknown.</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @param columnPattern A pattern of column names to return information for. + * @return A <code>ResultSet</code> with all the requested privileges. + * @exception SQLException If an error occurs. + */ + ResultSet getColumnPrivileges(String catalog, String schema, String + table, String columnNamePattern) throws SQLException; + + /** + * This method returns the access rights that have been granted to the + * requested tables. This information is returned as a <code>ResultSet</code> + * with the following columns: + * + * <ol> + * <li>TABLE_CAT - The catalog the table is in, which may be + * <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema the tables is in, which may be + * <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>GRANTOR - The entity that granted the access.</li> + * <li>GRANTEE - The entity granted the access.</li> + * <li>PRIVILEGE - The name of the privilege granted.</li> + * <li>IS_GRANTABLE - "YES" if the grantee can grant the privilege to + * others, "NO" if not, and <code>null</code> if unknown.</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tablePattern The table name pattern of tables to return + * information for. + * @return A <code>ResultSet</code> with all the requested privileges. + * @exception SQLException If an error occurs. + */ + ResultSet getTablePrivileges(String catalog, String schemaPattern, + String tableNamePattern) throws SQLException; + + /** + * This method returns the best set of columns for uniquely identifying + * a row. It returns this information as a <code>ResultSet</code> with + * the following columns: + * + * <ol> + * <li>SCOPE - The scope of the results returned. This is one of the + * constants defined in this class (<code>bestRowTemporary</code>, + * <code>bestRowTransaction</code>, or <code>bestRowSession</code>).</li> + * <li>COLUMN_NAME - The name of the column.</li> + * <li>DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in <code>Types</code>.</li> + * <li>TYPE_NAME - The string name of the data type for this column.</li> + * <li>COLUMN_SIZE - The precision of the columns</li> + * <li>BUFFER_LENGTH - Unused</li> + * <li>DECIMAL_DIGITS - The scale of the column.</li> + * <li>PSEUDO_COLUMN - Whether or not the best row identifier is a + * pseudo_column. This is one of the constants defined in this class + * (<code>bestRowUnknown</code>, <code>bestRowNotPseudo</code>, or + * <code>bestRowPseudo</code>).</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @param columnPattern A pattern of column names to return information for. + * @param scope One of the best row id scope constants from this class. + * @param nullable <code>true</code> to include columns that are nullable, + * <code>false</code> otherwise. + * @return A <code>ResultSet</code> with the best row identifier. + * @exception SQLException If an error occurs. + */ + ResultSet getBestRowIdentifier(String catalog, String schema, + String table, int scope, boolean nullable) throws SQLException; + + /** + * This method returns the set of columns that are automatically updated + * when the row is update. It returns this information as a + * <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>SCOPE - Unused</li> + * <li>COLUMN_NAME - The name of the column.</li> + * <li>DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in <code>Types</code>.</li> + * <li>TYPE_NAME - The string name of the data type for this column.</li> + * <li>COLUMN_SIZE - The precision of the columns</li> + * <li>BUFFER_LENGTH - Unused</li> + * <li>DECIMAL_DIGITS - The scale of the column.</li> + * <li>PSEUDO_COLUMN - Whether or not the best row identifier is a + * pseudo_column. This is one of the constants defined in this class + * (<code>versionRowUnknown</code>, <code>versionRowNotPseudo</code>, or + * <code>versionRowPseudo</code>).</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @param columnPattern A pattern of column names to return information for. + * @return A <code>ResultSet</code> with the version columns. + * @exception SQLException If an error occurs. + */ + ResultSet getVersionColumns(String catalog, String schema, + String table) throws SQLException; + + /** + * This method returns a list of a table's primary key columns. These + * are returned as a <code>ResultSet</code> with the following columns. + * + * <ol> + * <li>TABLE_CAT - The catalog of the table, which may be <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema of the table, which may be <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>COLUMN_NAME - The name of the column.</li> + * <li>KEY_SEQ - The sequence number of the column within the primary key.</li> + * <li>PK_NAME - The name of the primary key, which may be <code>null</code>.</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @param columnPattern A pattern of column names to return information for. + * @return A <code>ResultSet</code> with the primary key columns. + * @exception SQLException If an error occurs. + */ + ResultSet getPrimaryKeys(String catalog, String schema, String table) + throws SQLException; + + /** + * This method returns a list of the table's foreign keys. These are + * returned as a <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>PKTABLE_CAT - The catalog of the table the key was imported from.</li> + * <li>PKTABLE_SCHEM - The schema of the table the key was imported from.</li> + * <li>PKTABLE_NAME - The name of the table the key was imported from.</li> + * <li>PKCOLUMN_NAME - The name of the column that was imported.</li> + * <li>FKTABLE_CAT - The foreign key catalog name.</li> + * <li>FKTABLE_SCHEM - The foreign key schema name.</li> + * <li>FKTABLE_NAME - The foreign key table name.</li> + * <li>FKCOLUMN_NAME - The foreign key column name.</li> + * <li>KEY_SEQ - The sequence number of the column within the foreign key.</li> + * <li>UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, <code>importedKeySetDefault</code>, or + * <code>importedKeyRestrict</code>).</li> + * <li>DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, or <code>importedKeySetDefault</code>)</li> + * <li>FK_NAME - The name of the foreign key.</li> + * <li>PK_NAME - The name of the primary key.</li> + * <li>DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (<code>importedKeyInitiallyDeferred</code>, + * <code>importedKeyInitiallyImmediate</code>, or + * <code>importedKeyNotDeferrable</code>).</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * + * @return A <code>ResultSet</code> with the foreign key columns. + * + * @exception SQLException If an error occurs. + */ + ResultSet getImportedKeys(String catalog, String schema, + String table) throws SQLException; + + /** + * This method returns a list of the table's which use this table's + * primary key as a foreign key. The information is + * returned as a <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>PKTABLE_CAT - The catalog of the table the key was imported from.</li> + * <li>PKTABLE_SCHEM - The schema of the table the key was imported from.</li> + * <li>PKTABLE_NAME - The name of the table the key was imported from.</li> + * <li>PKCOLUMN_NAME - The name of the column that was imported.</li> + * <li>FKTABLE_CAT - The foreign key catalog name.</li> + * <li>FKTABLE_SCHEM - The foreign key schema name.</li> + * <li>FKTABLE_NAME - The foreign key table name.</li> + * <li>FKCOLUMN_NAME - The foreign key column name.</li> + * <li>KEY_SEQ - The sequence number of the column within the foreign key.</li> + * <li>UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, <code>importedKeySetDefault</code>, or + * <code>importedKeyRestrict</code>).</li> + * <li>DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, or <code>importedKeySetDefault</code>)</li> + * <li>FK_NAME - The name of the foreign key.</li> + * <li>PK_NAME - The name of the primary key.</li> + * <li>DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (<code>importedKeyInitiallyDeferred</code>, + * <code>importedKeyInitiallyImmediate</code>, or + * <code>importedKeyNotDeferrable</code>).</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @return A <code>ResultSet</code> with the requested information + * @exception SQLException If an error occurs. + */ + ResultSet getExportedKeys(String catalog, String schema, + String table) throws SQLException; + + /** + * This method returns a description of how one table imports another + * table's primary key as a foreign key. The information is + * returned as a <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>PKTABLE_CAT - The catalog of the table the key was imported from.</li> + * <li>PKTABLE_SCHEM - The schema of the table the key was imported from.</li> + * <li>PKTABLE_NAME - The name of the table the key was imported from.</li> + * <li>PKCOLUMN_NAME - The name of the column that was imported.</li> + * <li>FKTABLE_CAT - The foreign key catalog name.</li> + * <li>FKTABLE_SCHEM - The foreign key schema name.</li> + * <li>FKTABLE_NAME - The foreign key table name.</li> + * <li>FKCOLUMN_NAME - The foreign key column name.</li> + * <li>KEY_SEQ - The sequence number of the column within the foreign key.</li> + * <li>UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, <code>importedKeySetDefault</code>, or + * <code>importedKeyRestrict</code>).</li> + * <li>DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (<code>importedNoAction</code>, <code>importedKeyCascade</code>, + * <code>importedKeySetNull</code>, or <code>importedKeySetDefault</code>)</li> + * <li>FK_NAME - The name of the foreign key.</li> + * <li>PK_NAME - The name of the primary key.</li> + * <li>DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (<code>importedKeyInitiallyDeferred</code>, + * <code>importedKeyInitiallyImmediate</code>, or + * <code>importedKeyNotDeferrable</code>).</li> + * </ol> + * + * @param primCatalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs, on the exporting side. + * @param primSchema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema, on the exporting side. + * @param primTable The table name to return information for, on the exporting + * side. + * @param forCatalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs, on the importing side. + * @param forSchema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema on the importing side. + * @param forTable The table name to return information for on the importing + * side. + * @return A <code>ResultSet</code> with the requested information + * @exception SQLException If an error occurs. + */ + ResultSet getCrossReference(String primaryCatalog, String + primarySchema, String primaryTable, String foreignCatalog, String + foreignSchema, String foreignTable) throws SQLException; + + /** + * This method returns a list of the SQL types supported by this + * database. The information is returned as a <code>ResultSet</code> + * with the following columns: + * + * <ol> + * <li>TYPE_NAME - The name of the data type.</li> + * <li>DATA_TYPE - A data type constant from <code>Types</code> for this + * type.</li> + * <li>PRECISION - The maximum precision of this type.</li> + * <li>LITERAL_PREFIX - Prefix value used to quote a literal, which may be + * <code>null</code>.</li> + * <li>LITERAL_SUFFIX - Suffix value used to quote a literal, which may be + * <code>null</code>.</li> + * <li>CREATE_PARAMS - The parameters used to create the type, which may be + * <code>null</code>.</li> + * <li>NULLABLE - Whether or not this type supports NULL values. This will + * be one of the constants defined in this interface + * (<code>typeNoNulls</code>, <code>typeNullable</code>, or + * <code>typeNullableUnknown</code>).</li> + * <li>CASE_SENSITIVE - Whether or not the value is case sensitive.</li> + * <li>SEARCHABLE - Whether or not "LIKE" expressions are supported in + * WHERE clauses for this type. This will be one of the constants defined + * in this interface (<code>typePredNone</code>, <code>typePredChar</code>, + * <code>typePredBasic</code>, or <code>typeSearchable</code>).</li> + * <li>UNSIGNED_ATTRIBUTE - Is the value of this type unsigned.</li> + * <li>FIXED_PREC_SCALE - Whether or not this type can be used for money.</li> + * <li>AUTO_INCREMENT - Whether or not this type supports auto-incrementing.</li> + * <li>LOCAL_TYPE_NAME - A localized name for this data type.</li> + * <li>MINIMUM_SCALE - The minimum scale supported by this type.</li> + * <li>MAXIMUM_SCALE - The maximum scale supported by this type.</li> + * <li>SQL_DATA_TYPE - Unused.</li> + * <li>SQL_DATETIME_SUB - Unused.</li> + * <li>NUM_PREC_RADIX - The radix of this data type.</li> + * </ol> + * + * @return A <code>ResultSet</code> with the list of available data types. + * @exception SQLException If an error occurs. + */ + ResultSet getTypeInfo() throws SQLException; + + /** + * This method returns information about a tables indices and statistics. + * It is returned as a <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>TABLE_CAT - The catalog of the table, which may be <code>null</code>.</li> + * <li>TABLE_SCHEM - The schema of the table, which may be <code>null</code>.</li> + * <li>TABLE_NAME - The name of the table.</li> + * <li>NON_UNIQUE - Are index values non-unique?</li> + * <li>INDEX_QUALIFIER The index catalog, which may be <code>null</code></li> + * <li>INDEX_NAME - The name of the index.</li> + * <li>TYPE - The type of index, which will be one of the constants defined + * in this interface (<code>tableIndexStatistic</code>, + * <code>tableIndexClustered</code>, <code>tableIndexHashed</code>, or + * <code>tableIndexOther</code>).</li> + * <li>ORDINAL_POSITION - The sequence number of this column in the index. + * This will be 0 when the index type is <code>tableIndexStatistic</code>.</li> + * <li>COLUMN_NAME - The name of this column in the index.</li> + * <li>ASC_OR_DESC - "A" for an ascending sort sequence, "D" for a + * descending sort sequence or <code>null</code> if a sort sequence is not + * supported.</li> + * <li>CARDINALITY - The number of unique rows in the index, or the number + * of rows in the table if the index type is <code>tableIndexStatistic</code>.</li> + * <li>PAGES - The number of pages used for the index, or the number of pages + * in the table if the index type is <code>tableIndexStatistic</code>.</li> + * <li>FILTER_CONDITION - The filter condition for this index, which may be + * <code>null</code>.</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or + * <code>null</code> to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param table The table name to return information for. + * @param unique <code>true</code> to return only unique indexes, + * <code>false</code> otherwise. + * @param approx <code>true</code> if data values can be approximations, + * <code>false</code> otherwise. + * @return A <code>ResultSet</code> with the requested index information + * @exception SQLException If an error occurs. + */ + ResultSet getIndexInfo(String catalog, String schema, String table, + boolean unique, boolean approximate) throws SQLException; + + /** + * This method tests whether or not the datbase supports the specified + * result type. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * + * @return <code>true</code> if the result set type is supported, + * <code>false</code> otherwise. + * + * @exception SQLException If an error occurs. + * + * @see ResultSet + */ + boolean supportsResultSetType(int type) throws SQLException; + + /** + * This method tests whether the specified result set type and result set + * concurrency type are supported by the database. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @param concur The desired concurrency type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type is supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean supportsResultSetConcurrency(int type, int concurrency) + throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own updates. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees its own updates, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownUpdatesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own deletes. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees its own deletes, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownDeletesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own inserts. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees its own inserts, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownInsertsAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * updates committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees other updates, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersUpdatesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * deletes committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees other deletes, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersDeletesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * inserts committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type sees other inserts, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersInsertsAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible update by calling the <code>rowUpdated</code> method. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type can detect visible updates + * using <code>rowUpdated</code>, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean updatesAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible delete by calling the <code>rowUpdated</code> method. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type can detect visible deletes + * using <code>rowUpdated</code>, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean deletesAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible insert by calling the <code>rowUpdated</code> method. + * + * @param type The desired result type, which is one of the constants + * defined in <code>ResultSet</code>. + * @return <code>true</code> if the result set type can detect visible inserts + * using <code>rowUpdated</code>, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean insertsAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the database supports batch updates. + * + * @return <code>true</code> if batch updates are supported, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsBatchUpdates() throws SQLException; + + /** + * This method returns the list of user defined data types in use. These + * are returned as a <code>ResultSet</code> with the following columns: + * + * <ol> + * <li>TYPE_CAT - The catalog name, which may be <code>null</code>.</li> + * <li>TYPE_SCEHM - The schema name, which may be <code>null</code>.</li> + * <li>TYPE_NAME - The user defined data type name.</li> + * <li>CLASS_NAME - The Java class name this type maps to.</li> + * <li>DATA_TYPE - A type identifier from <code>Types</code> for this type. + * This will be one of <code>JAVA_OBJECT</code>, <code>STRUCT</code>, or + * <code>DISTINCT</code>.</li> + * <li>REMARKS - Comments about this data type.</li> + * </ol> + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or <code>null</code> + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param typePattern The type name pattern to match. + * @param types The type identifier patterns (from <code>Types</code>) to + * match. + * @return A <code>ResultSet</code> with the requested type information + * @exception SQLException If an error occurs. + */ + ResultSet getUDTs(String catalog, String schemaPattern, String + typeNamePattern, int[] types) throws SQLException; + + /** + * This method returns the <code>Connection</code> object that was used + * to generate the metadata in this object. + * + * @return The connection for this object. + * @exception SQLException If an error occurs. + */ + Connection getConnection() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsSavepoints() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsNamedParameters() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsMultipleOpenResults() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsGetGeneratedKeys() throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getSuperTypes(String catalog, String schemaPattern, + String typeNamePattern) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getSuperTables(String catalog, String schemaPattern, + String tableNamePattern) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getAttributes(String catalog, String schemaPattern, String + typeNamePattern, String attributeNamePattern) throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsResultSetHoldability(int holdability) + throws SQLException; + + /** + * @since 1.4 + */ + int getResultSetHoldability() throws SQLException; + + /** + * @since 1.4 + */ + int getDatabaseMajorVersion() throws SQLException; + + /** + * @since 1.4 + */ + int getDatabaseMinorVersion() throws SQLException; + + /** + * @since 1.4 + */ + int getJDBCMajorVersion() throws SQLException; + + /** + * @since 1.4 + */ + int getJDBCMinorVersion() throws SQLException; + + /** + * @since 1.4 + */ + int getSQLStateType() throws SQLException; + + /** + * @since 1.4 + */ + boolean locatorsUpdateCopy() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsStatementPooling() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Date.java b/libjava/classpath/java/sql/Date.java new file mode 100644 index 00000000000..48a274f331e --- /dev/null +++ b/libjava/classpath/java/sql/Date.java @@ -0,0 +1,188 @@ +/* Date.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Date. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Date extends java.util.Date +{ + static final long serialVersionUID = 1511598038487230103L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param year The year of this date minue 1900. + * @param month The month of this date (0-11). + * @param day The day of this date (1-31). + * + * @deprecated + */ + public Date(int year, int month, int day) + { + super(year, month, day); + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of seconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param time The time value to intialize this date to. + */ + public Date(long date) + { + super(date); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getHours() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getMinutes() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getSeconds() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setHours(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setMinutes(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setSeconds(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting <code>java.sql.Date</code> value. + * + * @deprecated + */ + public static Date valueOf (String str) + { + try + { + java.util.Date d = (java.util.Date) sdf.parseObject(str); + + if (d == null) + throw new IllegalArgumentException(str); + else + return new Date(d.getTime()); + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + * + * @deprecated + */ + public String toString() + { + return sdf.format(this); + } +} diff --git a/libjava/classpath/java/sql/Driver.java b/libjava/classpath/java/sql/Driver.java new file mode 100644 index 00000000000..10f83ef2f8b --- /dev/null +++ b/libjava/classpath/java/sql/Driver.java @@ -0,0 +1,123 @@ +/* Driver.java -- A JDBC driver + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Properties; + +/** + * This interface specifies a mechanism for accessing a JDBC database + * driver. When the class implementing this method is loaded, it should + * register an instance of itself with the <code>DriverManager</code> in + * a static initializer. + * <p> + * Because the <code>DriverManager</code> might attempt to use several + * drivers to find one that can connect to the requested database, + * this driver should not cause large numbers of classes and code to + * be loaded. If another driver is the one that ends up performing the + * request, any loading done by this driver would be wasted. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Driver +{ + /** + * This method connects to the specified database using the connection + * properties supplied. If the driver does not understand the database + * URL, it should return <code>null</code> instead of throwing an + * exception since the <code>DriverManager</code> will probe a driver + * in this manner. + * + * @param url The URL string for this connection. + * @param properties The list of database connection properties. + * @return A <code>Connection</code> object for the newly established + * connection, or <code>null</code> if the URL is not understood. + * @exception SQLException If an error occurs. + */ + Connection connect(String url, Properties info) throws SQLException; + + /** + * This method tests whether or not the driver believes it can connect to + * the specified database. The driver should only test whether it + * understands and accepts the URL. It should not necessarily attempt to + * probe the database for a connection. + * + * @param The database URL string. + * @return <code>true</code> if the drivers can connect to the database, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean acceptsURL(String url) throws SQLException; + + /** + * This method returns an array of possible properties that could be + * used to connect to the specified database. + * + * @param url The URL string of the database to connect to. + * @param properties The list of properties the caller is planning to use + * to connect to the database. + * @return A list of possible additional properties for a connection to this + * database. This list may be empty. + * @exception SQLException If an error occurs. + */ + DriverPropertyInfo[] getPropertyInfo(String url, Properties properties) + throws SQLException; + + /** + * This method returns the major version number of the driver. + * + * @return The major version number of the driver. + */ + int getMajorVersion(); + + /** + * This method returns the minor version number of the driver. + * + * @return The minor version number of the driver. + */ + int getMinorVersion(); + + /** + * This method tests whether or not the driver is JDBC compliant. This + * method should only return <code>true</code> if the driver has been + * certified as JDBC compliant. + * + * @return <code>true</code> if the driver has been certified JDBC compliant, + * <code>false</code> otherwise. + */ + boolean jdbcCompliant(); +} diff --git a/libjava/classpath/java/sql/DriverManager.java b/libjava/classpath/java/sql/DriverManager.java new file mode 100644 index 00000000000..9e252abcc60 --- /dev/null +++ b/libjava/classpath/java/sql/DriverManager.java @@ -0,0 +1,346 @@ +/* DriverManager.java -- Manage JDBC drivers + Copyright (C) 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * This class manages the JDBC drivers in the system. It maintains a + * registry of drivers and locates the appropriate driver to handle a + * JDBC database URL. + * <p> + * On startup, <code>DriverManager</code> loads all the managers specified + * by the system property <code>jdbc.drivers</code>. The value of this + * property should be a colon separated list of fully qualified driver + * class names. Additional drivers can be loaded at any time by + * simply loading the driver class with <code>class.forName(String)</code>. + * The driver should automatically register itself in a static + * initializer. + * <p> + * The methods in this class are all <code>static</code>. This class + * cannot be instantiated. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DriverManager +{ + /** + * This is the log stream for JDBC drivers. + */ + private static PrintStream log_stream; + + /** + * This is the log writer for JDBC drivers. + */ + private static PrintWriter log_writer; + + /** + * This is the login timeout used by JDBC drivers. + */ + private static int login_timeout; + + /** + * This is the list of JDBC drivers that are loaded. + */ + private static Vector drivers; + // Hmm, seems like we might want to do a Hashtable and lookup by something, + // but what would it be? + + // Load all drivers on startup + static + { + drivers = new Vector(); + + String driver_string = System.getProperty("jdbc.drivers"); + if (driver_string != null) + { + StringTokenizer st = new StringTokenizer(driver_string); + while (st.hasMoreTokens()) + { + String driver_classname = st.nextToken(); + + try + { + Class.forName(driver_classname); // The driver registers itself + } + catch (Exception e) + { + // Ignore not founds + } + } + } + + } + + /** Can't be instantiated. */ + private DriverManager() + { + } + + /** + * This method returns the log writer being used by all JDBC drivers. + * This method should be used in place of the deprecated + * <code>getLogStream</code> method. + * + * @return The log writer in use by JDBC drivers. + */ + public static PrintWriter getLogWriter() + { + return log_writer; + } + + /** + * This method sets the log writer being used by JDBC drivers. This is a + * system-wide parameter that affects all drivers. Note that since there + * is no way to retrieve a <code>PrintStream</code> from a + * <code>PrintWriter</code>, this method cannot set the log stream in + * use by JDBC. Thus any older drivers may not see this setting. + * + * @param out The new log writer for JDBC. + */ + public static void setLogWriter(PrintWriter out) + { + DriverManager.log_writer = out; + } + +/** + * This method attempts to return a connection to the specified + * JDBC URL string using the specified connection properties. + * + * @param url The JDBC URL string to connect to. + * @param properties The connection properties. + * + * @return A <code>Connection</code> to that URL. + * + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url, Properties properties) + throws SQLException + { + Driver d = getDriver(url); + if (d == null) + throw new SQLException("Driver not found for URL: " + url); + + return d.connect(url, properties); + } + + + /** + * This method attempts to return a connection to the specified + * JDBC URL string using the specified username and password. + * + * @param url The JDBC URL string to connect to. + * @param user The username to connect with. + * @param password The password to connect with. + * @return A <code>Connection</code> to that URL. + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url, String user, + String password) throws SQLException + { + Properties p = new Properties(); + + if (user != null) + p.setProperty("user", user); + if (password != null) + p.setProperty("password", password); + + return getConnection(url, p); + } + + /** + * This method attempts to return a connection to the specified + * JDBC URL string. + * + * @param url The JDBC URL string to connect to. + * + * @return A <code>Connection</code> to that URL. + * + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url) throws SQLException + { + return getConnection(url, new Properties()); + } + + /** + * This method returns a driver that can connect to the specified + * JDBC URL string. This will be selected from among drivers loaded + * at initialization time and those drivers manually loaded by the + * same class loader as the caller. + * + * @param url The JDBC URL string to find a driver for. + * + * @return A <code>Driver</code> that can connect to the specified + * URL. + * + * @exception SQLException If an error occurs, or no suitable driver can be found. + */ + public static Driver getDriver(String url) throws SQLException + { + // FIXME: Limit driver search to the appropriate subset of loaded drivers. + Enumeration e = drivers.elements(); + while(e.hasMoreElements()) + { + Driver d = (Driver)e.nextElement(); + if (d.acceptsURL(url)) + return d; + } + + throw new SQLException("No driver found for " + url); + } + + /** + * This method registers a new driver with the manager. This is normally + * called by the driver itself in a static initializer. + * + * @param driver The new <code>Driver</code> to add. + * + * @exception SQLException If an error occurs. + */ + public static void registerDriver(Driver driver) throws SQLException + { + if (! drivers.contains(driver)) + drivers.addElement(driver); + } + +/** + * This method de-registers a driver from the manager. + * + * @param driver The <code>Driver</code> to unregister. + * + * @exception SQLException If an error occurs. + */ + public static void deregisterDriver(Driver driver) throws SQLException + { + if (drivers.contains(driver)) + drivers.removeElement(driver); + } + + /** + * This method returns a list of all the currently registered JDBC drivers + * that were loaded by the current <code>ClassLoader</code>. + * + * @return An <code>Enumeration</code> of all currently loaded JDBC drivers. + */ + public static Enumeration getDrivers() + { + Vector v = new Vector(); + Enumeration e = drivers.elements(); + + // Is this right? + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + while(e.hasMoreElements()) + { + Object obj = e.nextElement(); + + ClassLoader loader = obj.getClass().getClassLoader(); + + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + if (! loader.equals(cl)) + continue; + + v.addElement(obj); + } + + return v.elements(); + } + + /** + * This method set the login timeout used by JDBC drivers. This is a + * system-wide parameter that applies to all drivers. + * + * @param login_timeout The new login timeout value. + */ + public static void setLoginTimeout(int seconds) + { + DriverManager.login_timeout = login_timeout; + } + + /** + * This method returns the login timeout in use by JDBC drivers systemwide. + * + * @return The login timeout. + */ + public static int getLoginTimeout() + { + return login_timeout; + } + + /** + * This method sets the log stream in use by JDBC. + * + * @param log_stream The log stream in use by JDBC. + * + * @deprecated Use <code>setLogWriter</code> instead. + */ + public static void setLogStream(PrintStream out) + { + DriverManager.log_stream = log_stream; + } + + /** + * This method returns the log stream in use by JDBC. + * + * @return The log stream in use by JDBC. + * + * @deprecated Use <code>getLogWriter()</code> instead. + */ + public static PrintStream getLogStream() + { + return log_stream; + } + + /** + * This method prints the specified line to the log stream. + * + * @param str The string to write to the log stream. + */ + public static void println(String message) + { + if (log_stream != null) // Watch for user not using logging + log_stream.println(message); + } +} diff --git a/libjava/classpath/java/sql/DriverPropertyInfo.java b/libjava/classpath/java/sql/DriverPropertyInfo.java new file mode 100644 index 00000000000..34e40fa0c3e --- /dev/null +++ b/libjava/classpath/java/sql/DriverPropertyInfo.java @@ -0,0 +1,88 @@ +/* DriverPropertyInfo.java -- Property information about drivers. + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This class holds a driver property that can be used for querying or + * setting driver configuration parameters. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DriverPropertyInfo +{ + /** + * The name of the property. + */ + public String name; + + /** + * A description of the property, possibly <code>null</code>. + */ + public String description; + + /** + * A flag indicating whether or not a value for this property is required + * in order to connect to the database. + */ + public boolean required; + + /** + * This is the value of the property. + */ + public String value; + + /** + * If values are restricted to certain choices, this is the list of valid + * ones. Otherwise it is <code>null</code>. + */ + public String[] choices; + + /** + * This method initializes a new instance of <code>DriverPropertyInfo</code> + * with the specified name and value. All other fields are defaulted. + * + * @param name The name of the property. + * @param value The value to assign to the property. + */ + public DriverPropertyInfo(String name, String value) + { + this.name = name; + this.value = value; + } +} diff --git a/libjava/classpath/java/sql/ParameterMetaData.java b/libjava/classpath/java/sql/ParameterMetaData.java new file mode 100644 index 00000000000..b3a75fd2058 --- /dev/null +++ b/libjava/classpath/java/sql/ParameterMetaData.java @@ -0,0 +1,103 @@ +/* ParameterMetaData.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * @since 1.4 + */ +public interface ParameterMetaData +{ + int parameterNoNulls = 0; + + int parameterNullable = 1; + + int parameterNullableUnknown = 2; + + int parameterModeUnknown = 0; + + int parameterModeIn = 1; + + int parameterModeInOut = 2; + + int parameterModeOut = 4; + + /** + * @since 1.4 + */ + int getParameterCount() throws SQLException; + + /** + * @since 1.4 + */ + int isNullable(int param) throws SQLException; + + /** + * @since 1.4 + */ + boolean isSigned(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getPrecision(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getScale(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getParameterType(int param) throws SQLException; + + /** + * @since 1.4 + */ + String getParameterTypeName(int param) throws SQLException; + + /** + * @since 1.4 + */ + String getParameterClassName(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getParameterMode(int param) throws SQLException; +} diff --git a/libjava/classpath/java/sql/PreparedStatement.java b/libjava/classpath/java/sql/PreparedStatement.java new file mode 100644 index 00000000000..3aedbc59300 --- /dev/null +++ b/libjava/classpath/java/sql/PreparedStatement.java @@ -0,0 +1,438 @@ +/* PreparedStatement.java -- Interface for pre-compiled statements. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; + +/** + * This interface provides a mechanism for executing pre-compiled + * statements. This provides greater efficiency when calling the same + * statement multiple times. Parameters are allowed in a statement, + * providings for maximum reusability. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface PreparedStatement extends Statement +{ + /** + * This method executes a prepared SQL query and returns its ResultSet. + * + * @return The ResultSet of the SQL statement. + * @exception SQLException If an error occurs. + */ + ResultSet executeQuery() throws SQLException; + + /** + * This method executes an SQL INSERT, UPDATE or DELETE statement. SQL + * statements that return nothing such as SQL DDL statements can be executed. + * + * @return The result is either the row count for INSERT, UPDATE or DELETE + * statements; or 0 for SQL statements that return nothing. + * @exception SQLException If an error occurs. + */ + int executeUpdate() throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param index The index of the parameter to set. + * @param type The SQL type identifier of the parameter from <code>Types</code> + * + * @exception SQLException If an error occurs. + */ + void setNull(int parameterIndex, int sqlType) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>boolean</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBoolean(int parameterIndex, boolean x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>byte</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setByte(int parameterIndex, byte x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>short</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setShort(int parameterIndex, short x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>int</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setInt(int parameterIndex, int x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>long</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setLong(int parameterIndex, long x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>float</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setFloat(int parameterIndex, float x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>double</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setDouble(int parameterIndex, double x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.math.BigDecimal</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBigDecimal(int parameterIndex, BigDecimal x) throws + SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>String</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setString(int parameterIndex, String x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>byte</code> array value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBytes(int parameterIndex, byte[] x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Date</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setDate(int parameterIndex, Date x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Time</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setTime(int parameterIndex, Time x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Timestamp</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setTimestamp(int parameterIndex, Timestamp x) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * ASCII <code>InputStream</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param length The number of bytes in the stream. + * @exception SQLException If an error occurs. + */ + void setAsciiStream(int parameterIndex, InputStream x, int length) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Unicode UTF-8 <code>InputStream</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param length The number of bytes in the stream. + * @exception SQLException If an error occurs. + * @deprecated + */ + void setUnicodeStream(int parameterIndex, InputStream x, int length) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * binary <code>InputStream</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param length The number of bytes in the stream. + * @exception SQLException If an error occurs. + */ + void setBinaryStream(int parameterIndex, InputStream x, int length) + throws SQLException; + + /** + * This method clears all of the input parameter that have been + * set on this statement. + * + * @exception SQLException If an error occurs. + */ + void clearParameters() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Object</code> value. The specified SQL object type will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param type The SQL type to use for the parameter, from <code>Types</code> + * @param scale The scale of the value, for numeric values only. + * @exception SQLException If an error occurs. + * @see Types + */ + void setObject(int parameterIndex, Object x, int targetSqlType, + int scale) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Object</code> value. The specified SQL object type will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param type The SQL type to use for the parameter, from <code>Types</code> + * @exception SQLException If an error occurs. + * @see Types + */ + void setObject(int parameterIndex, Object x, int targetSqlType) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Object</code> value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setObject(int parameterIndex, Object x) throws SQLException; + + /** + * This method executes a prepared SQL query. + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate. + * + * @return The result of the SQL statement. + * @exception SQLException If an error occurs. + */ + boolean execute() throws SQLException; + + /** + * This method adds a set of parameters to the batch for JDBC 2.0. + * @exception SQLException If an error occurs. + */ + void addBatch() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * character <code>Reader</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param length The number of bytes in the stream. + * @exception SQLException If an error occurs. + */ + void setCharacterStream(int parameterIndex, Reader reader, + int length) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Ref</code> value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setRef(int i, Ref x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Blob</code> value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBlob(int i, Blob x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Clob</code> value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setClob(int i, Clob x) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>Array</code> value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setArray(int i, Array x) throws SQLException; + + /** + * This method returns meta data for the result set from this statement. + * + * @return Meta data for the result set from this statement. + * @exception SQLException If an error occurs. + */ + ResultSetMetaData getMetaData() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Date</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param calendar The <code>Calendar</code> to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setDate(int parameterIndex, Date x, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Time</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param calendar The <code>Calendar</code> to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setTime(int parameterIndex, Time x, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * <code>java.sql.Timestamp</code> value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param calendar The <code>Calendar</code> to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) + throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param index The index of the parameter to set. + * @param type The SQL type identifier of the parameter from <code>Types</code> + * @param name The name of the data type, for user defined types. + * @exception SQLException If an error occurs. + */ + void setNull(int paramIndex, int sqlType, String typeName) + throws SQLException; + + /** + * @since 1.4 + */ + void setURL(int parameterIndex, URL x) throws SQLException; + + /** + * @since 1.4 + */ + ParameterMetaData getParameterMetaData() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Ref.java b/libjava/classpath/java/sql/Ref.java new file mode 100644 index 00000000000..4ebd5e64898 --- /dev/null +++ b/libjava/classpath/java/sql/Ref.java @@ -0,0 +1,75 @@ +/* Ref.java -- Reference to a SQL structured type. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Map; + +/** + * This interface provides a mechanism for obtaining information about + * a SQL structured type + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public interface Ref +{ + /** + * This method returns the fully qualified name of the SQL structured + * type of the referenced item. + * + * @return The fully qualified name of the SQL structured type. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + String getBaseTypeName() throws SQLException; + + /** + * @since 1.4 + */ + Object getObject(Map map) throws SQLException; + + /** + * @since 1.4 + */ + Object getObject() throws SQLException; + + /** + * @since 1.4 + */ + void setObject(Object value) throws SQLException; +} diff --git a/libjava/classpath/java/sql/ResultSet.java b/libjava/classpath/java/sql/ResultSet.java new file mode 100644 index 00000000000..97f2897d423 --- /dev/null +++ b/libjava/classpath/java/sql/ResultSet.java @@ -0,0 +1,1530 @@ +/* ResultSet.java -- A SQL statement result set. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; +import java.util.Map; + +/** + * This interface provides access to the data set returned by a SQL + * statement. An instance of this interface is returned by the various + * execution methods in the <code>Statement</code>. + * + * <p> This class models a cursor, which can be stepped through one row at a + * time. Methods are provided for accessing columns by column name or by + * index.</p> + * + * <p> Note that a result set is invalidated if the statement that returned + * it is closed.</p> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ResultSet +{ + /** + * The rows will be processed in order from first to last. + */ + int FETCH_FORWARD = 1000; + + /** + * The rows will be processed in order from last to first. + */ + int FETCH_REVERSE = 1001; + + /** + * The rows will be processed in an unknown order + */ + int FETCH_UNKNOWN = 1002; + + /** + * This type of result set may only step forward through the rows returned. + */ + int TYPE_FORWARD_ONLY = 1003; + + /** + * This type of result set is scrollable and is not sensitive to changes + * made by other statements. + */ + int TYPE_SCROLL_INSENSITIVE = 1004; + + /** + * This type of result set is scrollable and is also sensitive to changes + * made by other statements. + */ + int TYPE_SCROLL_SENSITIVE = 1005; + + /** + * The concurrency mode of for the result set may not be modified. + */ + int CONCUR_READ_ONLY = 1007; + + /** + * The concurrency mode of for the result set may be modified. + */ + int CONCUR_UPDATABLE = 1008; + + int HOLD_CURSORS_OVER_COMMIT = 1; + + int CLOSE_CURSORS_AT_COMMIT = 2; + + /** + * This method advances to the next row in the result set. Any streams + * open on the current row are closed automatically. + * + * @return <code>true</code> if the next row exists, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean next() throws SQLException; + + /** + * This method closes the result set and frees any associated resources. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method tests whether the value of the last column that was fetched + * was actually a SQL NULL value. + * + * @return <code>true</code> if the last column fetched was a NULL, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>String</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>String</code>. + * @exception SQLException If an error occurs. + */ + String getString(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>boolean</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>boolean</code>. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>byte</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>byte</code>. + * @exception SQLException If an error occurs. + */ + byte getByte(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>short</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>short</code>. + * @exception SQLException If an error occurs. + */ + short getShort(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>int</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>int</code>. + * @exception SQLException If an error occurs. + */ + int getInt(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>long</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>long</code>. + * @exception SQLException If an error occurs. + */ + long getLong(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>float</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>float</code>. + * @exception SQLException If an error occurs. + */ + float getFloat(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>double</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>double</code>. + * @exception SQLException If an error occurs. + */ + double getDouble(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>BigDecimal</code>. + * + * @param index The index of the column to return. + * @param scale The number of digits to the right of the decimal to return. + * @return The column value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + * @deprecated + */ + BigDecimal getBigDecimal(int columnIndex, int scale) + throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte array. + * + * @param index The index of the column to return. + * @return The column value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Date</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date getDate(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Time</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time getTime(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Timestamp</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as an ASCII + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param index The index of the column to return. + * @return The column value as an ASCII <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream getAsciiStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Unicode UTF-8 + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param index The index of the column to return. + * @return The column value as a Unicode UTF-8 <code>InputStream</code>. + * @exception SQLException If an error occurs. + * @deprecated Use getCharacterStream instead. + */ + InputStream getUnicodeStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a raw byte + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param index The index of the column to return. + * @return The column value as a raw byte <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>String</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>String</code>. + * @exception SQLException If an error occurs. + */ + String getString(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>boolean</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>boolean</code>. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>byte</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>byte</code>. + * @exception SQLException If an error occurs. + */ + byte getByte(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>short</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>short</code>. + * @exception SQLException If an error occurs. + */ + short getShort(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>int</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>int</code>. + * @exception SQLException If an error occurs. + */ + int getInt(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>long</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>long</code>. + * @exception SQLException If an error occurs. + */ + long getLong(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>float</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>float</code>. + * @exception SQLException If an error occurs. + */ + float getFloat(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>double</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>double</code>. + * @exception SQLException If an error occurs. + */ + double getDouble(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>BigDecimal</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + * @deprecated + */ + BigDecimal getBigDecimal(String columnName, int scale) + throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte array. + * + * @param column The name of the column to return. + * @return The column value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Date</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date getDate(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Time</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time getTime(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>java.sql.Timestamp</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as an ASCII + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param column The name of the column to return. + * @return The column value as an ASCII <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream getAsciiStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Unicode UTF-8 + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param column The name of the column to return. + * @return The column value as a Unicode UTF-8 <code>InputStream</code>. + * @exception SQLException If an error occurs. + * @deprecated Use getCharacterStream instead. + */ + InputStream getUnicodeStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a raw byte + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param column The name of the column to return. + * @return The column value as a raw byte <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream(String columnName) throws SQLException; + + /** + * This method returns the first SQL warning associated with this result + * set. Any additional warnings will be chained to this one. + * + * @return The first SQLWarning for this result set, or <code>null</code> if + * there are no warnings. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears all warnings associated with this result set. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method returns the name of the database cursor used by this + * result set. + * + * @return The name of the database cursor used by this result set. + * @exception SQLException If an error occurs. + */ + String getCursorName() throws SQLException; + + /** + * This method returns data about the columns returned as part of the + * result set as a <code>ResultSetMetaData</code> instance. + * + * @return The <code>ResultSetMetaData</code> instance for this result set. + * @exception SQLException If an error occurs. + */ + ResultSetMetaData getMetaData() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>Object</code>. + * + * @param index The index of the column to return. + * @return The column value as an <code>Object</code>. + * @exception SQLException If an error occurs. + */ + Object getObject(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>Object</code>. + * + * @param column The name of the column to return. + * @return The column value as an <code>Object</code>. + * @exception SQLException If an error occurs. + */ + Object getObject(String columnName) throws SQLException; + + /** + * This method returns the column index of the specified named column. + * + * @param column The name of the column. + * @return The index of the column. + * @exception SQLException If an error occurs. + */ + int findColumn(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a character + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param index The index of the column to return. + * @return The column value as an character <code>Reader</code>. + * @exception SQLException If an error occurs. + */ + Reader getCharacterStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a character + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling <code>next()</code> or <code>close()</code> on this result set + * will close this stream as well. + * + * @param column The name of the column to return. + * @return The column value as an character <code>Reader</code>. + * @exception SQLException If an error occurs. + */ + Reader getCharacterStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>BigDecimal</code>. + * + * @param index The index of the column to return. + * @return The column value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + */ + BigDecimal getBigDecimal(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>BigDecimal</code>. + * + * @param column The name of the column to return. + * @return The column value as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + */ + BigDecimal getBigDecimal(String columnName) throws SQLException; + + /** + * This method tests whether or not the cursor is before the first row + * in the result set. + * + * @return <code>true</code> if the cursor is positioned before the first + * row, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isBeforeFirst() throws SQLException; + + /** + * This method tests whether or not the cursor is after the last row + * in the result set. + * + * @return <code>true</code> if the cursor is positioned after the last + * row, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isAfterLast() throws SQLException; + + /** + * This method tests whether or not the cursor is positioned on the first + * row in the result set. + * + * @return <code>true</code> if the cursor is positioned on the first + * row, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isFirst() throws SQLException; + + /** + * This method tests whether or not the cursor is on the last row + * in the result set. + * + * @return <code>true</code> if the cursor is positioned on the last + * row, <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isLast() throws SQLException; + + /** + * This method repositions the cursor to before the first row in the + * result set. + * + * @exception SQLException If an error occurs. + */ + void beforeFirst() throws SQLException; + + /** + * This method repositions the cursor to after the last row in the result + * set. + * + * @exception SQLException If an error occurs. + */ + void afterLast() throws SQLException; + + /** + * This method repositions the cursor on the first row in the + * result set. + * + * @return <code>true</code> if the cursor is on a valid row; + * <code>false</code> if there are no rows in the result set. + * @exception SQLException If an error occurs. + */ + boolean first() throws SQLException; + + /** + * This method repositions the cursor on the last row in the result + * set. + * + * @return <code>true</code> if the cursor is on a valid row; + * <code>false</code> if there are no rows in the result set. + * @exception SQLException If an error occurs. + */ + boolean last() throws SQLException; + + /** + * This method returns the current row number in the cursor. Numbering + * begins at index 1. + * + * @return The current row number, or 0 if there is not current row. + * @exception SQLException If an error occurs. + */ + int getRow() throws SQLException; + + /** + * This method positions the result set to the specified absolute row. + * Positive numbers are row offsets from the beginning of the result + * set (numbering starts from row 1) and negative numbers are row offsets + * from the end of the result set (numbering starts from -1). + * + * @param row The row to position the result set to. + * + * @return <code>true</code> if the current position was changed, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean absolute(int row) throws SQLException; + + /** + * This method moves the result set position relative to the current row. + * The offset can be positive or negative. + * + * @param row The relative row position to move to. + * @return <code>true</code> if the current position was changed, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean relative(int rows) throws SQLException; + + /** + * This method moves the current position to the previous row in the + * result set. + * + * @return <code>true</code> if the previous row exists, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean previous() throws SQLException; + + /** + * This method provides a hint to the driver about which direction the + * result set will be processed in. + * + * @param direction The direction in which rows will be processed. (Values?) + * @exception SQLException If an error occurs. + */ + void setFetchDirection(int direction) throws SQLException; + + /** + * This method returns the current fetch direction for this result set. + * + * @return The fetch direction for this result set. + * @exception SQLException If an error occurs. + */ + int getFetchDirection() throws SQLException; + + /** + * This method provides a hint to the driver about how many rows at a + * time it should fetch from the database. + * + * @param rows The number of rows the driver should fetch per call. + * @exception SQLException If an error occurs. + */ + void setFetchSize(int rows) throws SQLException; + + /** + * This method returns the current number of rows that will be fetched + * from the database at a time. + * + * @return The current fetch size for this result set. + * @exception SQLException If an error occurs. + */ + int getFetchSize() throws SQLException; + + /** + * This method returns the result set type of this result set. This will + * be one of the TYPE_* constants defined in this interface. + * + * @return The result set type. + * @exception SQLException If an error occurs. + */ + int getType() throws SQLException; + + /** + * This method returns the concurrency type of this result set. This will + * be one of the CONCUR_* constants defined in this interface. + * + * @return The result set concurrency type. + * @exception SQLException If an error occurs. + */ + int getConcurrency() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been updated. Updates must be visible in order of this method to + * detect the update. + * + * @return <code>true</code> if the row has been updated, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowUpdated() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been inserted. Inserts must be visible in order of this method to + * detect the insert. + * + * @return <code>true</code> if the row has been inserted, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowInserted() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been deleted. Deletes must be visible in order of this method to + * detect the deletion. + * + * @return <code>true</code> if the row has been deleted, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowDeleted() throws SQLException; + + /** + * This method updates the specified column to have a NULL value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @return index The index of the column to update. + * @exception SQLException If an error occurs. + */ + void updateNull(int columnIndex) throws SQLException; + + /** + * This method updates the specified column to have a boolean value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBoolean(int columnIndex, boolean x) throws SQLException; + + /** + * This method updates the specified column to have a byte value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateByte(int columnIndex, byte x) throws SQLException; + + /** + * This method updates the specified column to have a short value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateShort(int columnIndex, short x) throws SQLException; + + /** + * This method updates the specified column to have an int value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateInt(int columnIndex, int x) throws SQLException; + + /** + * This method updates the specified column to have a long value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateLong(int columnIndex, long x) throws SQLException; + + /** + * This method updates the specified column to have a float value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateFloat(int columnIndex, float x) throws SQLException; + + /** + * This method updates the specified column to have a double value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDouble(int columnIndex, double x) throws SQLException; + + /** + * This method updates the specified column to have a BigDecimal value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBigDecimal(int columnIndex, BigDecimal x) + throws SQLException; + + /** + * This method updates the specified column to have a String value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateString(int columnIndex, String x) throws SQLException; + + /** + * This method updates the specified column to have a byte array value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBytes(int columnIndex, byte[] x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Date value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDate(int columnIndex, Date x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Time value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTime(int columnIndex, Time x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Timestamp value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTimestamp(int columnIndex, Timestamp x) + throws SQLException; + + /** + * This method updates the specified column from an ASCII text stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateAsciiStream(int columnIndex, InputStream x, int length) + throws SQLException; + + /** + * This method updates the specified column from a binary stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateBinaryStream(int columnIndex, InputStream x, int length) + throws SQLException; + + /** + * This method updates the specified column from a character stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateCharacterStream(int columnIndex, Reader x, int length) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * + * @exception SQLException If an error occurs. + */ + void updateObject(int columnIndex, Object x, int scale) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param index The index of the column to update. + * @param value The new value of the column. + * @param scale The scale of the object in question, which is used only + * for numeric type objects. + * @exception SQLException If an error occurs. + */ + void updateObject(int columnIndex, Object x) throws SQLException; + + /** + * This method updates the specified column to have a NULL value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @return name The name of the column to update. + * @exception SQLException If an error occurs. + */ + void updateNull(String columnName) throws SQLException; + + /** + * This method updates the specified column to have a boolean value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBoolean(String columnName, boolean x) throws SQLException; + + /** + * This method updates the specified column to have a byte value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateByte(String columnName, byte x) throws SQLException; + + /** + * This method updates the specified column to have a short value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateShort(String columnName, short x) throws SQLException; + + /** + * This method updates the specified column to have an int value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateInt(String columnName, int x) throws SQLException; + + /** + * This method updates the specified column to have a long value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateLong(String columnName, long x) throws SQLException; + + /** + * This method updates the specified column to have a float value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateFloat(String columnName, float x) throws SQLException; + + /** + * This method updates the specified column to have a double value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDouble(String columnName, double x) throws SQLException; + + /** + * This method updates the specified column to have a BigDecimal value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBigDecimal(String columnName, BigDecimal x) + throws SQLException; + + /** + * This method updates the specified column to have a String value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateString(String columnName, String x) throws SQLException; + + /** + * This method updates the specified column to have a byte array value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBytes(String columnName, byte[] x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Date value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDate(String columnName, Date x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Time value. This + * does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTime(String columnName, Time x) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Timestamp value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTimestamp(String columnName, Timestamp x) + throws SQLException; + + /** + * This method updates the specified column from an ASCII text stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateAsciiStream(String columnName, InputStream x, int length) + throws SQLException; + + /** + * This method updates the specified column from a binary stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateBinaryStream(String columnName, InputStream x, int length) + throws SQLException; + + /** + * This method updates the specified column from a character stream. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @param length The length of the stream. + * + * @exception SQLException If an error occurs. + */ + void updateCharacterStream(String columnName, Reader reader, + int length) throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateObject(String columnName, Object x, int scale) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. <code>updateRow</code> must be + * called in order to do that. + * + * @param name The name of the column to update. + * @param value The new value of the column. + * @param scale The scale of the object in question, which is used only + * for numeric type objects. + * @exception SQLException If an error occurs. + */ + void updateObject(String columnName, Object x) throws SQLException; + + /** + * This method inserts the current row into the database. The result set + * must be positioned on the insert row in order to call this method + * successfully. + * + * @exception SQLException If an error occurs. + */ + void insertRow() throws SQLException; + + /** + * This method updates the current row in the database. + * + * @exception SQLException If an error occurs. + */ + void updateRow() throws SQLException; + + /** + * This method deletes the current row in the database. + * + * @exception SQLException If an error occurs. + */ + void deleteRow() throws SQLException; + + /** + * This method refreshes the contents of the current row from the database. + * + * @exception SQLException If an error occurs. + */ + void refreshRow() throws SQLException; + + /** + * This method cancels any changes that have been made to a row. If + * the <code>rowUpdate</code> method has been called, then the changes + * cannot be undone. + * + * @exception SQLException If an error occurs. + */ + void cancelRowUpdates() throws SQLException; + + /** + * This method positions the result set to the "insert row", which allows + * a new row to be inserted into the database from the result set. + * + * @exception SQLException If an error occurs. + */ + void moveToInsertRow() throws SQLException; + + /** + * This method moves the result set position from the insert row back to + * the current row that was selected prior to moving to the insert row. + * + * @exception SQLException If an error occurs. + */ + void moveToCurrentRow() throws SQLException; + + /** + * This method returns a the <code>Statement</code> that was used to + * produce this result set. + * + * @return The <code>Statement</code> used to produce this result set. + * + * @exception SQLException If an error occurs. + */ + Statement getStatement() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>Object</code> using the specified SQL type to Java type map. + * + * @param index The index of the column to return. + * @param map The SQL type to Java type map to use. + * @return The value of the column as an <code>Object</code>. + * @exception SQLException If an error occurs. + */ + Object getObject(int i, Map map) throws SQLException; + + /** + * This method returns a <code>Ref</code> for the specified column which + * represents the structured type for the column. + * + * @param index The index of the column to return. + * @return A <code>Ref</code> object for the column + * @exception SQLException If an error occurs. + */ + Ref getRef(int i) throws SQLException; + + /** + * This method returns the specified column value as a BLOB. + * + * @param index The index of the column value to return. + * @return The value of the column as a BLOB. + * @exception SQLException If an error occurs. + */ + Blob getBlob(int i) throws SQLException; + + /** + * This method returns the specified column value as a CLOB. + * + * @param index The index of the column value to return. + * @return The value of the column as a CLOB. + * @exception SQLException If an error occurs. + */ + Clob getClob(int i) throws SQLException; + + /** + * This method returns the specified column value as an <code>Array</code>. + * + * @param index The index of the column value to return. + * @return The value of the column as an <code>Array</code>. + * @exception SQLException If an error occurs. + */ + Array getArray(int i) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * <code>Object</code> using the specified SQL type to Java type map. + * + * @param name The name of the column to return. + * @param map The SQL type to Java type map to use. + * @return The value of the column as an <code>Object</code>. + * @exception SQLException If an error occurs. + */ + Object getObject(String colName, Map map) throws SQLException; + + /** + * This method returns a <code>Ref</code> for the specified column which + * represents the structured type for the column. + * + * @param index The index of the column to return. + * @return A <code>Ref</code> object for the column + * @exception SQLException If an error occurs. + */ + Ref getRef(String colName) throws SQLException; + + /** + * This method returns the specified column value as a BLOB. + * + * @param name The name of the column value to return. + * @return The value of the column as a BLOB. + * @exception SQLException If an error occurs. + */ + Blob getBlob(String colName) throws SQLException; + + /** + * This method returns the specified column value as a CLOB. + * + * @param name The name of the column value to return. + * @return The value of the column as a CLOB. + * @exception SQLException If an error occurs. + */ + Clob getClob(String colName) throws SQLException; + + /** + * This method returns the specified column value as an <code>Array</code>. + * + * @param name The name of the column value to return. + * @return The value of the column as an <code>Array</code>. + * @exception SQLException If an error occurs. + */ + Array getArray(String colName) throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Date</code>. The specified <code>Calendar</code> is used + * to generate a value for the date if the database does not support + * timezones. + * + * @param index The index of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * @return The value of the column as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date getDate(int columnIndex, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Date</code>. The specified <code>Calendar</code> is used + * to generate a value for the date if the database does not support + * timezones. + * + * @param name The name of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * @return The value of the column as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date getDate(String columnName, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Time</code>. The specified <code>Calendar</code> is used + * to generate a value for the time if the database does not support + * timezones. + * + * @param index The index of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * @return The value of the column as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time getTime(int columnIndex, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Time</code>. The specified <code>Calendar</code> is used + * to generate a value for the time if the database does not support + * timezones. + * + * @param name The name of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * @return The value of the column as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time getTime(String columnName, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Timestamp</code>. The specified <code>Calendar</code> is used + * to generate a value for the timestamp if the database does not support + * timezones. + * + * @param index The index of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * @return The value of the column as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int columnIndex, Calendar cal) + throws SQLException; + + /** + * This method returns the specified column value as a + * <code>java.sql.Timestamp</code>. The specified <code>Calendar</code> is used + * to generate a value for the timestamp if the database does not support + * timezones. + * + * @param name The name of the column value to return. + * @param cal The <code>Calendar</code> to use for calculating timezones. + * + * @return The value of the column as a <code>java.sql.Timestamp</code>. + * + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(String columnName, Calendar cal) + throws SQLException; + + /** + * @since 1.4 + */ + URL getURL(int columnIndex) throws SQLException; + + /** + * @since 1.4 + */ + URL getURL(String columnName) throws SQLException; + + /** + * @since 1.4 + */ + void updateRef(int columnIndex, Ref x) throws SQLException; + + /** + * @since 1.4 + */ + void updateRef(String columnName, Ref x) throws SQLException; + + /** + * @since 1.4 + */ + void updateBlob(int columnIndex, Blob x) throws SQLException; + + /** + * @since 1.4 + */ + void updateBlob(String columnName, Blob x) throws SQLException; + + /** + * @since 1.4 + */ + void updateClob(int columnIndex, Clob x) throws SQLException; + + /** + * @since 1.4 + */ + void updateClob(String columnName, Clob x) throws SQLException; + + /** + * @since 1.4 + */ + void updateArray(int columnIndex, Array x) throws SQLException; + + /** + * @since 1.4 + */ + void updateArray(String columnName, Array x) throws SQLException; +} diff --git a/libjava/classpath/java/sql/ResultSetMetaData.java b/libjava/classpath/java/sql/ResultSetMetaData.java new file mode 100644 index 00000000000..0086677ee60 --- /dev/null +++ b/libjava/classpath/java/sql/ResultSetMetaData.java @@ -0,0 +1,281 @@ +/* ResultSetMetaData.java -- Returns information about the ResultSet + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface provides a mechanism for obtaining information about + * the columns that are present in a <code>ResultSet</code>. + * <p> + * Note that in this class column indexes start at 1, not 0. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ResultSetMetaData +{ + /** + * The column does not allow NULL's. + */ + int columnNoNulls = 0; + + /** + * The column allows NULL's. + */ + int columnNullable = 1; + + /** + * It is unknown whether or not the column allows NULL's. + */ + int columnNullableUnknown = 2; + + /** + * This method returns the number of columns in the result set. + * + * @return The number of columns in the result set. + * @exception SQLException If an error occurs. + */ + int getColumnCount() throws SQLException; + + /** + * This method test whether or not the column is an auto-increment column. + * Auto-increment columns are read-only. + * + * @param index The index of the column to test. + * @return <code>true</code> if the column is auto-increment, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isAutoIncrement(int column) throws SQLException; + + /** + * This method tests whether or not a column is case sensitive in its values. + * + * @param index The index of the column to test. + * @return <code>true</code> if the column value is case sensitive, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isCaseSensitive(int column) throws SQLException; + + /** + * This method tests whether not the specified column can be used in + * a WHERE clause. + * + * @param index The index of the column to test. + * @return <code>true</code> if the column may be used in a WHERE clause, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isSearchable(int column) throws SQLException; + + /** + * This method tests whether or not the column stores a monetary value. + * + * @param index The index of the column to test. + * @return <code>true</code> if the column contains a monetary value, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isCurrency(int column) throws SQLException; + + /** + * This method returns a value indicating whether or not the specified + * column may contain a NULL value. + * + * @param index The index of the column to test. + * @return A constant indicating whether or not the column can contain NULL, + * which will be one of <code>columnNoNulls</code>, + * <code>columnNullable</code>, or <code>columnNullableUnknown</code>. + * @exception SQLException If an error occurs. + */ + int isNullable(int column) throws SQLException; + + /** + * This method tests whether or not the value of the specified column + * is signed or unsigned. + * + * @param index The index of the column to test. + * @return <code>true</code> if the column value is signed, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isSigned(int column) throws SQLException; + + /** + * This method returns the maximum number of characters that can be used + * to display a value in this column. + * + * @param index The index of the column to check. + * @return The maximum number of characters that can be used to display a + * value for this column. + * @exception SQLException If an error occurs. + */ + int getColumnDisplaySize(int column) throws SQLException; + + /** + * This method returns a string that should be used as a caption for this + * column for user display purposes. + * + * @param index The index of the column to check. + * @return A display string for the column. + * @exception SQLException If an error occurs. + */ + String getColumnLabel(int column) throws SQLException; + + /** + * This method returns the name of the specified column. + * + * @param index The index of the column to return the name of. + * @return The name of the column. + * @exception SQLException If an error occurs. + */ + String getColumnName(int column) throws SQLException; + + /** + * This method returns the name of the schema that contains the specified + * column. + * + * @param index The index of the column to check the schema name for. + * @return The name of the schema that contains the column. + * @exception SQLException If an error occurs. + */ + String getSchemaName(int column) throws SQLException; + + /** + * This method returns the precision of the specified column, which is the + * number of decimal digits it contains. + * + * @param index The index of the column to check the precision on. + * @return The precision of the specified column. + * @exception SQLException If an error occurs. + */ + int getPrecision(int column) throws SQLException; + + /** + * This method returns the scale of the specified column, which is the + * number of digits to the right of the decimal point. + * + * @param index The index column to check the scale of. + * @return The scale of the column. + * @exception SQLException If an error occurs. + */ + int getScale(int column) throws SQLException; + + /** + * This method returns the name of the table containing the specified + * column. + * + * @param index The index of the column to check the table name for. + * @return The name of the table containing the column. + * @exception SQLException If an error occurs. + */ + String getTableName(int column) throws SQLException; + + /** + * This method returns the name of the catalog containing the specified + * column. + * + * @param index The index of the column to check the catalog name for. + * @return The name of the catalog containing the column. + * @exception SQLException If an error occurs. + */ + String getCatalogName(int column) throws SQLException; + + /** + * This method returns the SQL type of the specified column. This will + * be one of the constants from <code>Types</code>. + * + * @param index The index of the column to check the SQL type of. + * @return The SQL type for this column. + * @exception SQLException If an error occurs. + * @see Types + */ + int getColumnType(int column) throws SQLException; + + /** + * This method returns the name of the SQL type for this column. + * + * @param index The index of the column to check the SQL type name for. + * @return The name of the SQL type for this column. + * @exception SQLException If an error occurs. + */ + String getColumnTypeName(int column) throws SQLException; + + /** + * This method tests whether or not the specified column is read only. + * + * @param index The index of the column to check. + * @return <code>true</code> if the column is read only, <code>false</code> + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly(int column) throws SQLException; + + /** + * This method tests whether or not the column may be writable. This + * does not guarantee that a write will be successful. + * + * @param index The index of the column to check for writability. + * @return <code>true</code> if the column may be writable, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isWritable(int column) throws SQLException; + + /** + * This method tests whether or not the column is writable. This + * does guarantee that a write will be successful. + * + * @param index The index of the column to check for writability. + * @return <code>true</code> if the column is writable, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean isDefinitelyWritable(int column) throws SQLException; + + /** + * This method returns the name of the Java class which will be used to + * create objects representing the data in this column. + * + * @param index The index of the column to check. + * @return The name of the Java class that will be used for values in + * this column. + * @exception SQLException If an error occurs. + */ + String getColumnClassName(int column) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLData.java b/libjava/classpath/java/sql/SQLData.java new file mode 100644 index 00000000000..2ba1fb18d05 --- /dev/null +++ b/libjava/classpath/java/sql/SQLData.java @@ -0,0 +1,72 @@ +/* SQLData.java -- Custom mapping for a user defined datatype + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface is used for mapping SQL data to user defined datatypes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLData +{ + /** + * This method returns the user defined datatype name for this object. + * + * @return The user defined data type name for this object. + * @exception SQLException If an error occurs. + */ + String getSQLTypeName() throws SQLException; + + /** + * This method populates the data in the object from the specified stream. + * + * @param stream The stream to read the data from. + * @param name The data type name of the data on the stream. + * @exception SQLException If an error occurs. + */ + void readSQL(SQLInput stream, String typeName) throws SQLException; + + /** + * This method writes the data in this object to the specified stream. + * + * @param stream The stream to write the data to. + * @exception SQLException If an error occurs. + */ + void writeSQL(SQLOutput stream) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLException.java b/libjava/classpath/java/sql/SQLException.java new file mode 100644 index 00000000000..8c502f2df7c --- /dev/null +++ b/libjava/classpath/java/sql/SQLException.java @@ -0,0 +1,167 @@ +/* SQLException.java -- General SQL exception + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This exception is thrown when a database error occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class SQLException extends Exception +{ + static final long serialVersionUID = 2135244094396331484L; + + /** + * This is the next exception in the chain + */ + private SQLException next; + + /** + * This is the state of the SQL statement at the time of the error. + */ + private String SQLState; + + /** + * The vendor error code for this error + */ + private int vendorCode; + + /** + * This method initializes a nwe instance of <code>SQLException</code> + * with the specified descriptive error message, SQL state string, and + * vendor code. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + * @param vendorCode The vendor error code associated with this error. + */ + public SQLException(String message, String SQLState, int vendorCode) + { + super(message); + this.SQLState = SQLState; + this.vendorCode = vendorCode; + } + + /** + * This method initializes a new instance of <code>SQLException</code> + * with the specified descriptive error message and SQL state string. + * The vendor error code of this instance will be 0. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + */ + public SQLException(String message, String SQLState) + { + this(message, SQLState, 0); + } + + /** + * This method initializes a new instance of <code>SQLException</code> + * with the specified descriptive error message. The SQL state of this + * instance will be <code>null</code> and the vendor error code will be 0. + * + * @param message A string describing the nature of the error. + */ + public SQLException(String message) + { + this(message, null, 0); + } + + /** + * This method initializes a new instance of <code>SQLException</code> + * that does not have a descriptive messages and SQL state, and which + * has a vendor error code of 0. + */ + public SQLException() + { + this(null, null, 0); + } + + /** + * This method returns the SQLState information associated with this + * error. The value returned is a <code>String</code> which is formatted + * using the XOPEN SQL state conventions. + * + * @return The SQL state, which may be <code>null</code>. + */ + public String getSQLState() + { + return SQLState; + } + + /** + * This method returns the vendor specific error code associated with + * this error. + * + * @return The vendor specific error code associated with this error. + */ + public int getErrorCode() + { + return vendorCode; + } + + /** + * This method returns the exception that is chained to this object. + * + * @return The exception chained to this object, which may be + * <code>null</code>. + */ + public SQLException getNextException() + { + return next; + } + + /** + * This method adds a new exception to the end of the chain of exceptions + * that are chained to this object. + * + * @param e The exception to add to the end of the chain. + */ + public void setNextException(SQLException e) + { + if (e == null) + return; + + SQLException list_entry = this; + while (list_entry.getNextException() != null) + list_entry = list_entry.getNextException(); + + list_entry.next = e; + } +} diff --git a/libjava/classpath/java/sql/SQLInput.java b/libjava/classpath/java/sql/SQLInput.java new file mode 100644 index 00000000000..730627d9a9e --- /dev/null +++ b/libjava/classpath/java/sql/SQLInput.java @@ -0,0 +1,259 @@ +/* SQLInput.java -- Read SQL values from a stream + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; + +/** + * This interface provides methods for reading values from a stream + * that is connected to a SQL structured or distinct type. It is used + * for custom mapping of user defined data types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLInput +{ + /** + * This method reads the next item from the stream a Java + * <code>String</code>. + * + * @return The value read from the stream as a <code>String</code>. + * @exception SQLException If an error occurs. + */ + String readString() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>boolean</code>. + * + * @return The value read from the stream as a <code>boolean</code>. + * @exception SQLException If an error occurs. + */ + boolean readBoolean() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>byte</code>. + * + * @return The value read from the stream as a <code>byte</code>. + * @exception SQLException If an error occurs. + */ + byte readByte() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>short</code>. + * + * @return The value read from the stream as a <code>short</code>. + * @exception SQLException If an error occurs. + */ + short readShort() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>int</code>. + * + * @return The value read from the stream as an <code>int</code>. + * @exception SQLException If an error occurs. + */ + int readInt() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>long</code>. + * + * @return The value read from the stream as a <code>long</code>. + * @exception SQLException If an error occurs. + */ + long readLong() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>float</code>. + * + * @return The value read from the stream as a <code>float</code>. + * @exception SQLException If an error occurs. + */ + float readFloat() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>double</code>. + * + * @return The value read from the stream as a <code>double</code>. + * @exception SQLException If an error occurs. + */ + double readDouble() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>BigDecimal</code>. + * + * @return The value read from the stream as a <code>BigDecimal</code>. + * @exception SQLException If an error occurs. + */ + BigDecimal readBigDecimal() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * byte array + * + * @return The value read from the stream as a byte array. + * @exception SQLException If an error occurs. + */ + byte[] readBytes() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>java.sql.Date</code>. + * + * @return The value read from the stream as a <code>java.sql.Date</code>. + * @exception SQLException If an error occurs. + */ + Date readDate() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>java.sql.Time</code>. + * + * @return The value read from the stream as a <code>java.sql.Time</code>. + * @exception SQLException If an error occurs. + */ + Time readTime() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>java.sql.Timestamp</code>. + * + * @return The value read from the stream as a <code>java.sql.Timestamp</code>. + * @exception SQLException If an error occurs. + */ + Timestamp readTimestamp() throws SQLException; + + /** + * This method reads the next item from the stream a character + * <code>Reader</code>. + * + * @return The value read from the stream as a <code>Reader</code>. + * @exception SQLException If an error occurs. + */ + Reader readCharacterStream() throws SQLException; + + /** + * This method reads the next item from the stream a ASCII text + * <code>InputStream</code>. + * + * @return The value read from the stream as an <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream readAsciiStream() throws SQLException; + + /** + * This method reads the next item from the stream a binary + * <code>InputStream</code>. + * + * @return The value read from the stream as an <code>InputStream</code>. + * @exception SQLException If an error occurs. + */ + InputStream readBinaryStream() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * <code>Object</code>. + * + * @return The value read from the stream as an <code>Object</code>. + * @exception SQLException If an error occurs. + */ + Object readObject() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * <code>Ref</code>. + * + * @return The value read from the stream as an <code>Ref</code>. + * @exception SQLException If an error occurs. + */ + Ref readRef() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * <code>Blob</code>. + * + * @return The value read from the stream as a <code>Blob</code>. + * @exception SQLException If an error occurs. + */ + Blob readBlob() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * <code>Clob</code>. + * + * @return The value read from the stream as a <code>Clob</code>. + * @exception SQLException If an error occurs. + */ + Clob readClob() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * <code>Array</code>. + * + * @return The value read from the stream as an <code>Array</code>. + * @exception SQLException If an error occurs. + */ + Array readArray() throws SQLException; + + /** + * This method tests whether or not the last value read was a SQL + * NULL value. + * + * @return <code>true</code> if the last value read was a NULL, + * <code>false</code> otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * @since 1.4 + */ + URL readURL() throws SQLException; +} + diff --git a/libjava/classpath/java/sql/SQLOutput.java b/libjava/classpath/java/sql/SQLOutput.java new file mode 100644 index 00000000000..8e6c88f8cf6 --- /dev/null +++ b/libjava/classpath/java/sql/SQLOutput.java @@ -0,0 +1,257 @@ +/* SQLOutput.java -- Write SQL values to a stream + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; + +/** + * This interface provides methods for writing Java types to a SQL stream. + * It is used for implemented custom type mappings for user defined data + * types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLOutput +{ + /** + * This method writes the specified Java <code>String</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeString(String x) throws SQLException; + + /** + * This method writes the specified Java <code>boolean</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBoolean(boolean x) throws SQLException; + + /** + * This method writes the specified Java <code>byte</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeByte(byte x) throws SQLException; + + /** + * This method writes the specified Java <code>short</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeShort(short x) throws SQLException; + + /** + * This method writes the specified Java <code>int</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeInt(int x) throws SQLException; + + /** + * This method writes the specified Java <code>long</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeLong(long x) throws SQLException; + + /** + * This method writes the specified Java <code>float</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeFloat(float x) throws SQLException; + + /** + * This method writes the specified Java <code>double</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeDouble(double x) throws SQLException; + + /** + * This method writes the specified Java <code>BigDecimal</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBigDecimal(BigDecimal x) throws SQLException; + + /** + * This method writes the specified Java <code>byte</code> array + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBytes(byte[] x) throws SQLException; + + /** + * This method writes the specified Java <code>java.sql.Date</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeDate(Date x) throws SQLException; + + /** + * This method writes the specified Java <code>java.sql.Time</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeTime(Time x) throws SQLException; + + /** + * This method writes the specified Java <code>java.sql.Timestamp</code> + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeTimestamp(Timestamp x) throws SQLException; + + /** + * This method writes the specified Java character stream + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeCharacterStream(Reader x) throws SQLException; + + /** + * This method writes the specified ASCII text stream + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeAsciiStream(InputStream x) throws SQLException; + + /** + * This method writes the specified uninterpreted binary byte stream + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBinaryStream(InputStream x) throws SQLException; + + /** + * This method writes the specified Java <code>SQLData</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeObject(SQLData x) throws SQLException; + + /** + * This method writes the specified Java SQL <code>Ref</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeRef(Ref x) throws SQLException; + + /** + * This method writes the specified Java SQL <code>Blob</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBlob(Blob x) throws SQLException; + + /** + * This method writes the specified Java SQL <code>Clob</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeClob(Clob x) throws SQLException; + + /** + * This method writes the specified Java SQL <code>Struct</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeStruct(Struct x) throws SQLException; + + /** + * This method writes the specified Java SQL <code>Array</code> object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeArray(Array x) throws SQLException; + + /** + * @since 1.4 + */ + void writeURL(URL x) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLPermission.java b/libjava/classpath/java/sql/SQLPermission.java new file mode 100644 index 00000000000..101fa0161b4 --- /dev/null +++ b/libjava/classpath/java/sql/SQLPermission.java @@ -0,0 +1,57 @@ +/* SQLPermission.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.security.BasicPermission; + +/** + * @since 1.3 + */ +public final class SQLPermission extends BasicPermission +{ + public SQLPermission(String name) + { + super(name); + } + + public SQLPermission(String name, String actions) + { + super(name, actions); + } +} diff --git a/libjava/classpath/java/sql/SQLWarning.java b/libjava/classpath/java/sql/SQLWarning.java new file mode 100644 index 00000000000..841d137e13a --- /dev/null +++ b/libjava/classpath/java/sql/SQLWarning.java @@ -0,0 +1,120 @@ +/* SQLWarning.java -- Database access warnings. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This exception is thrown when a database warning occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class SQLWarning extends SQLException +{ + static final long serialVersionUID = 3917336774604784856L; + + /** + * This method initializes a nwe instance of <code>SQLWarning</code> + * with the specified descriptive error message, SQL state string, and + * vendor code. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + * @param vendorCode The vendor error code associated with this error. + */ + public SQLWarning(String reason, String SQLState, int vendorCode) + { + super(reason, SQLState, vendorCode); + } + + /** + * This method initializes a new instance of <code>SQLWarning</code> + * with the specified descriptive error message and SQL state string. + * The vendor error code of this instance will be 0. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + */ + public SQLWarning(String message, String SQLState) + { + super(message, SQLState); + } + + /** + * This method initializes a new instance of <code>SQLWarning</code> + * with the specified descriptive error message. The SQL state of this + * instance will be <code>null</code> and the vendor error code will be 0. + * + * @param message A string describing the nature of the error. + */ + public SQLWarning(String message) + { + super(message); + } + + /** + * This method initializes a new instance of <code>SQLWarning</code> + * that does not have a descriptive messages and SQL state, and which + * has a vendor error code of 0. + */ + public SQLWarning() + { + super(); + } + + /** + * This method returns the exception that is chained to this object. + * + * @return The exception chained to this object, which may be + * <code>null</code>. + */ + public SQLWarning getNextWarning() + { + return (SQLWarning) super.getNextException(); + } + + /** + * This method adds a new exception to the end of the chain of exceptions + * that are chained to this object. + * + * @param w The exception to add to the end of the chain. + */ + public void setNextWarning(SQLWarning w) + { + super.setNextException(w); + } +} diff --git a/libjava/classpath/java/sql/Savepoint.java b/libjava/classpath/java/sql/Savepoint.java new file mode 100644 index 00000000000..a4d89ae6e50 --- /dev/null +++ b/libjava/classpath/java/sql/Savepoint.java @@ -0,0 +1,55 @@ +/* SavePoint.java -- Returns information about the ResultSet + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * @since 1.4 + */ +public interface Savepoint +{ + /** + * @since 1.4 + */ + int getSavepointId() throws SQLException; + + /** + * @since 1.4 + */ + String getSavepointName() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Statement.java b/libjava/classpath/java/sql/Statement.java new file mode 100644 index 00000000000..42e8e8e8816 --- /dev/null +++ b/libjava/classpath/java/sql/Statement.java @@ -0,0 +1,366 @@ +/* Statement.java -- Interface for executing SQL statements. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface provides a mechanism for executing SQL statements. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Statement +{ + int CLOSE_CURRENT_RESULT = 1; + int KEEP_CURRENT_RESULT = 2; + int CLOSE_ALL_RESULTS = 3; + int SUCCESS_NO_INFO = -2; + int EXECUTE_FAILED = -3; + int RETURN_GENERATED_KEYS = 1; + int NO_GENERATED_KEYS = 2; + + /** + * This method executes the specified SQL SELECT statement and returns a + * (possibly empty) <code>ResultSet</code> with the results of the query. + * + * @param sql The SQL statement to execute. + * @return The result set of the SQL statement. + * @exception SQLException If an error occurs. + */ + ResultSet executeQuery(String sql) throws SQLException; + + /** + * This method executes the specified SQL INSERT, UPDATE, or DELETE statement + * and returns the number of rows affected, which may be 0. + * + * @param sql The SQL statement to execute. + * @return The number of rows affected by the SQL statement. + * @exception SQLException If an error occurs. + */ + int executeUpdate(String sql) throws SQLException; + + /** + * This method closes the statement and frees any associated resources. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method returns the maximum length of any column value in bytes. + * + * @return The maximum length of any column value in bytes. + * @exception SQLException If an error occurs. + */ + int getMaxFieldSize() throws SQLException; + + /** + * This method sets the limit for the maximum length of any column in bytes. + * + * @param maxsize The new maximum length of any column in bytes. + * @exception SQLException If an error occurs. + */ + void setMaxFieldSize(int max) throws SQLException; + + /** + * This method returns the maximum possible number of rows in a result set. + * + * @return The maximum possible number of rows in a result set. + * @exception SQLException If an error occurs. + */ + int getMaxRows() throws SQLException; + + /** + * This method sets the maximum number of rows that can be present in a + * result set. + * + * @param maxrows The maximum possible number of rows in a result set. + * @exception SQLException If an error occurs. + */ + void setMaxRows(int max) throws SQLException; + + /** + * This method sets the local escape processing mode on or off. The + * default value is on. + * + * @param escape <code>true</code> to enable local escape processing, + * <code>false</code> to disable it. + * @exception SQLException If an error occurs. + */ + void setEscapeProcessing(boolean enable) throws SQLException; + + /** + * The method returns the number of seconds a statement may be in process + * before timing out. A value of 0 means there is no timeout. + * + * @return The SQL statement timeout in seconds. + * @exception SQLException If an error occurs. + */ + int getQueryTimeout() throws SQLException; + + /** + * This method sets the number of seconds a statement may be in process + * before timing out. A value of 0 means there is no timeout. + * + * @param timeout The new SQL statement timeout value. + * @exception SQLException If an error occurs. + */ + void setQueryTimeout(int seconds) throws SQLException; + + /** + * This method cancels an outstanding statement, if the database supports + * that operation. + * + * @exception SQLException If an error occurs. + */ + void cancel() throws SQLException; + + /** + * This method returns the first SQL warning attached to this statement. + * Subsequent warnings will be chained to this one. + * + * @return The first SQL warning for this statement. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears any SQL warnings that have been attached to this + * statement. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method sets the cursor name that will be used by the result set. + * + * @param name The cursor name to use for this statement. + * @exception SQLException If an error occurs. + */ + void setCursorName(String name) throws SQLException; + + /** + * This method executes an arbitrary SQL statement of any time. The + * methods <code>getResultSet</code>, <code>getMoreResults</code> and + * <code>getUpdateCount</code> retrieve the results. + * + * @return <code>true</code> if a result set was returned, <code>false</code> + * if an update count was returned. + * @exception SQLException If an error occurs. + */ + boolean execute(String sql) throws SQLException; + + /** + * This method returns the result set of the SQL statement that was + * executed. This should be called only once per result set returned. + * + * @return The result set of the query, or <code>null</code> if there was + * no result set (for example, if the statement was an UPDATE). + * @exception SQLException If an error occurs. + * @see execute + */ + ResultSet getResultSet() throws SQLException; + + /** + * This method returns the update count of the SQL statement that was + * executed. This should be called only once per executed SQL statement. + * + * @return The update count of the query, or -1 if there was no update + * count (for example, if the statement was a SELECT). + * @exception SQLException If an error occurs. + * @see execute + */ + int getUpdateCount() throws SQLException; + + /** + * This method advances the result set pointer to the next result set, + * which can then be retrieved using <code>getResultSet</code> + * + * @return <code>true</code> if there is another result set, + * <code>false</code> otherwise (for example, the next result is an + * update count). + * @exception SQLException If an error occurs. + * @see execute + */ + boolean getMoreResults() throws SQLException; + + /** + * This method informs the driver which direction the result set will + * be accessed in. + * + * @param direction The direction the result set will be accessed in (?????) + * @exception SQLException If an error occurs. + */ + void setFetchDirection(int direction) throws SQLException; + + /** + * This method returns the current direction that the driver thinks the + * result set will be accessed int. + * + * @return The direction the result set will be accessed in (????) + * @exception SQLException If an error occurs. + */ + int getFetchDirection() throws SQLException; + + /** + * This method informs the driver how many rows it should fetch from the + * database at a time. + * + * @param numrows The number of rows the driver should fetch at a time + * to populate the result set. + * @exception SQLException If an error occurs. + */ + void setFetchSize(int rows) throws SQLException; + + /** + * This method returns the number of rows the driver believes should be + * fetched from the database at a time. + * + * @return The number of rows that will be fetched from the database at a time. + * @exception SQLException If an error occurs. + */ + int getFetchSize() throws SQLException; + + /** + * This method returns the concurrency type of the result set for this + * statement. This will be one of the concurrency types defined in + * <code>ResultSet</code>. + * + * @return The concurrency type of the result set for this statement. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + int getResultSetConcurrency() throws SQLException; + + /** + * This method returns the result set type for this statement. This will + * be one of the result set types defined in <code>ResultSet</code>. + * + * @return The result set type for this statement. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + int getResultSetType() throws SQLException; + + /** + * This method adds a SQL statement to a SQL batch. A driver is not + * required to implement this method. + * + * @param sql The sql statement to add to the batch. + * @exception SQLException If an error occurs. + */ + void addBatch(String sql) throws SQLException; + + /** + * This method clears out any SQL statements that have been populated in + * the current batch. A driver is not required to implement this method. + * + * @exception SQLException If an error occurs. + */ + void clearBatch() throws SQLException; + + /** + * This method executes the SQL batch and returns an array of update + * counts - one for each SQL statement in the batch - ordered in the same + * order the statements were added to the batch. A driver is not required + * to implement this method. + * + * @return An array of update counts for this batch. + * @exception SQLException If an error occurs. + */ + int[] executeBatch() throws SQLException; + + /** + * This method returns the <code>Connection</code> instance that was + * used to create this object. + * + * @return The connection used to create this object. + * @exception SQLException If an error occurs. + */ + Connection getConnection() throws SQLException; + + /** + * @since 1.4 + */ + boolean getMoreResults(int current) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getGeneratedKeys() throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, int[] columnIndexes) + throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, String[] columnNames) + throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, int[] columnIndexes) throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, String[] columnNames) + throws SQLException; + + /** + * @since 1.4 + */ + int getResultSetHoldability() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Struct.java b/libjava/classpath/java/sql/Struct.java new file mode 100644 index 00000000000..5cbc88e133a --- /dev/null +++ b/libjava/classpath/java/sql/Struct.java @@ -0,0 +1,77 @@ +/* Struct.java -- Mapping for a SQL structured type. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.util.Map; + +/** + * This interface implements the standard type mapping for a SQL + * structured type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Struct +{ + /** + * This method returns the name of the SQL structured type for this + * object. + * + * @return The SQL structured type name. + * @exception SQLException If an error occurs. + */ + String getSQLTypeName() throws SQLException; + + /** + * This method returns the attributes of this SQL structured type. + * + * @return The attributes of this structure type. + * @exception SQLException If an error occurs. + */ + Object[] getAttributes() throws SQLException; + + /** + * This method returns the attributes of this SQL structured type. + * The specified map of type mappings overrides the default mappings. + * + * @param map The map of SQL type mappings. + * @return The attributes of this structure type. + * @exception SQLException If a error occurs. + */ + Object[] getAttributes(Map map) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Time.java b/libjava/classpath/java/sql/Time.java new file mode 100644 index 00000000000..eb6ef2d651e --- /dev/null +++ b/libjava/classpath/java/sql/Time.java @@ -0,0 +1,205 @@ +/* Time.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Time. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Time extends java.util.Date +{ + static final long serialVersionUID = 8397324403548013681L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getDate() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getDay() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getMonth() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getYear() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setDate(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setMonth(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setYear(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting <code>java.sql.Time</code> value. + * + * @deprecated + */ + public static Time valueOf (String str) + { + try + { + java.util.Date d = (java.util.Date) sdf.parseObject(str); + + if (d == null) + throw new IllegalArgumentException(str); + else + return new Time(d.getTime()); + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param hour The hour for this Time (0-23) + * @param minute The minute for this time (0-59) + * @param second The second for this time (0-59) + * @deprecated + */ + public Time(int hour, int minute, int second) + { + super(System.currentTimeMillis()); + + setHours(hour); + setMinutes(minute); + setSeconds(second); + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of seconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param time The time value to intialize this <code>Time</code> to. + */ + public Time(long date) + { + super(date); + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + * + * @deprecated + */ + public String toString () + { + return sdf.format (this); + } + +} + diff --git a/libjava/classpath/java/sql/Timestamp.java b/libjava/classpath/java/sql/Timestamp.java new file mode 100644 index 00000000000..f3459b22f3e --- /dev/null +++ b/libjava/classpath/java/sql/Timestamp.java @@ -0,0 +1,315 @@ +/* Time.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Timestamp. Note that this + * class also adds an additional field for nano-seconds, and so + * is not completely identical to <code>java.util.Date</code> as + * the <code>java.sql.Date</code> and <code>java.sql.Time</code> + * classes are. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Timestamp extends java.util.Date +{ + static final long serialVersionUID = 2745179027874758501L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static DecimalFormat decimalFormat = new DecimalFormat("000000000"); + private static StringBuffer sbuf = new StringBuffer(29); + + /** + * The nanosecond value for this object + */ + private int nanos; + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting <code>java.sql.Timestamp</code> value. + */ + public static Timestamp valueOf(String str) + { + int nanos = 0; + int dot = str.indexOf('.'); + if (dot != -1) + { + if (str.lastIndexOf('.') != dot) + throw new IllegalArgumentException(str); + + int len = str.length() - dot - 1; + if (len < 1 || len > 9) + throw new IllegalArgumentException(str); + + nanos = Integer.parseInt(str.substring(dot + 1)); + for (int i = len; i < 9; i++) + nanos *= 10; + + str = str.substring(0, dot); + + } + + try + { + java.util.Date d; + synchronized (dateFormat) + { + d = (java.util.Date) dateFormat.parseObject(str); + } + + if (d == null) + throw new IllegalArgumentException(str); + + Timestamp ts = new Timestamp(d.getTime() + nanos / 1000000); + ts.nanos = nanos; + return ts; + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param year The year for this Timestamp (year - 1900) + * @param month The month for this Timestamp (0-11) + * @param day The day for this Timestamp (1-31) + * @param hour The hour for this Timestamp (0-23) + * @param minute The minute for this Timestamp (0-59) + * @param second The second for this Timestamp (0-59) + * @param nanos The nanosecond value for this Timestamp (0 to 999,999,9999) + * @deprecated + */ + public Timestamp(int year, int month, int day, int hour, int minute, + int second, int nanos) + { + super(year, month, day, hour, minute, second); + this.nanos = nanos; + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of milliseconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param time The time value to intialize this <code>Time</code> to. + */ + public Timestamp(long date) + { + super(date - (date % 1000)); + nanos = (int) (date % 1000) * 1000000; + } + + /** + * Return the value of this Timestamp as the number of milliseconds + * since Jan 1, 1970 at 12:00 midnight GMT. + */ + public long getTime() + { + return super.getTime() + (nanos / 1000000); + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + */ + public String toString() + { + synchronized (dateFormat) + { + sbuf.setLength(0); + dateFormat.format(this, sbuf, null); + sbuf.append('.'); + decimalFormat.format(nanos, sbuf, null); + int end = sbuf.length() - 1; + while (end > 20 && sbuf.charAt(end) == '0') + end--; + return sbuf.substring(0, end + 1); + } + } + + /** + * This method returns the nanosecond value for this object. + * @return The nanosecond value for this object. + */ + public int getNanos() + { + return nanos; + } + + /** + * This method sets the nanosecond value for this object. + * + * @param nanos The nanosecond value for this object. + */ + public void setNanos(int nanos) + { + this.nanos = nanos; + } + + /** + * This methods tests whether this object is earlier than the specified + * object. + * + * @param ts The other <code>Timestamp</code> to test against. + * @return <code>true</code> if this object is earlier than the other object, + * <code>false</code> otherwise. + */ + public boolean before(Timestamp ts) + { + long time1 = getTime(); + long time2 = ts.getTime(); + if (time1 < time2 || (time1 == time2 && getNanos() < ts.getNanos())) + return true; + return false; + } + + /** + * This methods tests whether this object is later than the specified + * object. + * + * @param ts The other <code>Timestamp</code> to test against. + * + * @return <code>true</code> if this object is later than the other object, + * <code>false</code> otherwise. + */ + public boolean after(Timestamp ts) + { + long time1 = getTime(); + long time2 = ts.getTime(); + if (time1 > time2 || (time1 == time2 && getNanos() > ts.getNanos())) + return true; + return false; + } + + /** + * This method these the specified <code>Object</code> for equality + * against this object. This will be true if an only if the specified + * object is an instance of <code>Timestamp</code> and has the same + * time value fields. + * + * @param obj The object to test against for equality. + * + * @return <code>true</code> if the specified object is equal to this + * object, <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof Timestamp)) + return false; + + return equals((Timestamp) obj); + } + + /** + * This method tests the specified timestamp for equality against this + * object. This will be true if and only if the specified object is + * not <code>null</code> and contains all the same time value fields + * as this object. + * + * @param ts The <code>Timestamp</code> to test against for equality. + * + * @return <code>true</code> if the specified object is equal to this + * object, <code>false</code> otherwise. + */ + public boolean equals(Timestamp ts) + { + if (ts == null) + return false; + + if (ts.getTime() != getTime()) + return false; + + if (ts.getNanos() != getNanos()) + return false; + + return true; + } + + /** + * Compare two Timestamp + * @param when the other Timestamp. + * @return 0, if the date represented + * by obj is exactly the same as the time represented by this + * object, a negative if this Timestamp is before the other Timestamp, and + * a positive value otherwise. + * @since 1.2 + */ + public int compareTo(Timestamp ts) + { + int s = super.compareTo((java.util.Date) ts); + if (s != 0) + return s; + // If Date components were equal, then we check the nanoseconds. + return nanos - ts.nanos; + } + + /** + * Compares this Timestamp to another. This behaves like + * <code>compareTo(Timestamp)</code>, but it may throw a + * <code>ClassCastException</code> + * @param obj the other Timestamp. + * @return 0, if the Timestamp represented + * by obj is exactly the same as the time represented by this + * object, a negative if this Timestamp is before the other Timestamp, and + * a positive value otherwise. + * @exception ClassCastException if obj is not of type Timestamp. + * @since 1.2 + */ + public int compareTo(Object obj) + { + return compareTo((Timestamp) obj); + } +} diff --git a/libjava/classpath/java/sql/Types.java b/libjava/classpath/java/sql/Types.java new file mode 100644 index 00000000000..7dd41411ada --- /dev/null +++ b/libjava/classpath/java/sql/Types.java @@ -0,0 +1,85 @@ +/* Types.java -- SQL type constants + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This class contains constants that are used to identify SQL data types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Types +{ + // These should be self explanatory. People need a SQL book, not + // Javadoc comments for these. + public static final int BIT = -7; + public static final int TINYINT = -6; + public static final int SMALLINT = 5; + public static final int INTEGER = 4; + public static final int BIGINT = -5; + public static final int FLOAT = 6; + public static final int REAL = 7; + public static final int DOUBLE = 8; + public static final int NUMERIC = 2; + public static final int DECIMAL = 3; + public static final int CHAR = 1; + public static final int VARCHAR = 12; + public static final int LONGVARCHAR = -1; + public static final int DATE = 91; + public static final int TIME = 92; + public static final int TIMESTAMP = 93; + public static final int BINARY = -2; + public static final int VARBINARY = -3; + public static final int LONGVARBINARY = -4; + public static final int NULL = 0; + public static final int OTHER = 1111; + public static final int JAVA_OBJECT = 2000; + public static final int DISTINCT = 2001; + public static final int STRUCT = 2002; + public static final int ARRAY = 2003; + public static final int BLOB = 2004; + public static final int CLOB = 2005; + public static final int REF = 2006; + public static final int DATALINK = 70; + public static final int BOOLEAN = 16; + + // This class can't be instantiated. + private Types() + { + } +} diff --git a/libjava/classpath/java/sql/package.html b/libjava/classpath/java/sql/package.html new file mode 100644 index 00000000000..e8982f529b4 --- /dev/null +++ b/libjava/classpath/java/sql/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.sql package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.sql</title></head> + +<body> +<p>Interfaces and classes to connect to a database and wrappers for data +in the database and result queries.</p> + +</body> +</html> diff --git a/libjava/classpath/java/text/Annotation.java b/libjava/classpath/java/text/Annotation.java new file mode 100644 index 00000000000..cecb44aaf0e --- /dev/null +++ b/libjava/classpath/java/text/Annotation.java @@ -0,0 +1,113 @@ +/* Annotation.java -- Wrapper for a text attribute object + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used as a wrapper for a text attribute object. Annotation + * objects are associated with a specific range of text. Changing either + * the text range or the underlying text invalidates the object. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Annotation +{ + +/* + * Instance Variables + */ + +/** + * This is the attribute object being wrappered + */ +private Object attrib; + +/*************************************************************************/ + +/** + * Constructors + */ + +/** + * This method initializes a new instance of <code>Annotation</code> to + * wrapper the specified text attribute object. + * + * @param attrib The text attribute <code>Object</code> to wrapper. + */ +public +Annotation(Object attrib) +{ + this.attrib = attrib; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This method returns the text attribute object this <code>Annotation</code> + * instance is wrappering. + * + * @return The text attribute object for this <code>Annotation</code>. + */ +public Object +getValue() +{ + return(attrib); +} + +/*************************************************************************/ + +/** + * This method returns a <code>String</code> representation of this + * object. + * + * @return This object as a <code>String</code>. + */ +public String +toString() +{ + return(getClass().getName() + "[value=" + attrib.toString() + "]"); +} + +} // class Annotation + diff --git a/libjava/classpath/java/text/AttributedCharacterIterator.java b/libjava/classpath/java/text/AttributedCharacterIterator.java new file mode 100644 index 00000000000..e5686ba346d --- /dev/null +++ b/libjava/classpath/java/text/AttributedCharacterIterator.java @@ -0,0 +1,268 @@ +/* AttributedCharacterIterator.java -- Iterate over attributes + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * This interface extends the <code>CharacterIterator</code> interface + * in order to support iteration over character attributes as well as + * over the characters themselves. + * <p> + * In addition to attributes of specific characters, this interface + * supports the concept of the "attribute run", which is an attribute + * that is defined for a particular value across an entire range of + * characters or which is undefined over a range of characters. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface AttributedCharacterIterator extends CharacterIterator +{ + /** + * This class defines attribute keys that are used as text attributes. + */ + public static class Attribute implements Serializable + { + private static final long serialVersionUID = -9142742483513960612L; + + /** + * This is the attribute for the language of the text. The value of + * attributes of this key type are instances of <code>Locale</code>. + */ + public static final Attribute LANGUAGE = new Attribute ("LANGUAGE"); + + /** + * This is the attribute for the reading form of text. This is used + * for storing pronunciation along with the written text for languages + * which need it. The value of attributes of this key type are + * instances of <code>Annotation</code> which wrappers a <code>String</code>. + */ + public static final Attribute READING = new Attribute ("READING"); + + /** + * This is the attribute for input method segments. The value of attributes + * of this key type are instances of <code>Annotation</code> which wrapper + * a <code>String</code>. + */ + public static final Attribute INPUT_METHOD_SEGMENT = + new Attribute ("INPUT_METHOD_SEGMENT"); + + /** + * This is the name of the attribute key + * @serial + */ + private String name; + + /** + * This method initializes a new instance of this class with the specified + * name. + * + * @param name The name of this attribute key. + */ + protected Attribute (String name) + { + this.name = name; + } + + /** + * This method returns the name of this attribute. + * + * @return The attribute name + */ + protected String getName() + { + return name; + } + + /** + * This method resolves an instance of <code>AttributedCharacterIterator.Attribute</code> + * that is being deserialized to one of the three pre-defined attribute + * constants. It does this by comparing the names of the attributes. The + * constant that the deserialized object resolves to is returned. + * + * @return The resolved contant value + * + * @exception InvalidObjectException If the object being deserialized cannot be resolved. + */ + protected Object readResolve() throws InvalidObjectException + { + if (this.equals (READING)) + return READING; + + if (this.equals (LANGUAGE)) + return LANGUAGE; + + if (this.equals (INPUT_METHOD_SEGMENT)) + return INPUT_METHOD_SEGMENT; + + throw new InvalidObjectException ("Can't resolve Attribute: " + getName()); + } + + /** + * This method tests this object for equality against the specified object. + * The two objects will be considered equal if and only if: + * <ul> + * <li>The specified object is not <code>null</code>. + * <li>The specified object is an instance of <code>AttributedCharacterIterator.Attribute</code>. + * <li>The specified object has the same attribute name as this object. + * </ul> + * + * @param The <code>Object</code> to test for equality against this object. + * + * @return <code>true</code> if the specified object is equal to this one, <code>false</code> otherwise. + */ + public final boolean equals (Object obj) + { + if (obj == this) + return true; + else + return false; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public final int hashCode() + { + return super.hashCode(); + } + + /** + * This method returns a <code>String</code> representation of this object. + * + * @return A <code>String</code> representation of this object. + */ + public String toString() + { + return getClass().getName() + "(" + getName() + ")"; + } + + } // Inner class Attribute + + /** + * This method returns a list of all keys that are defined for the + * text range. This can be an empty list if no attributes are defined. + * + * @return A list of keys + */ + Set getAllAttributeKeys(); + + /** + * This method returns a <code>Map</code> of the attributed defined for + * the current character. + * + * @return A <code>Map</code> of the attributes for the current character. + */ + Map getAttributes(); + + /** + * This method returns the value of the specified attribute for the + * current character. If the attribute is not defined for the current + * character, <code>null</code> is returned. + * + * @param attrib The attribute to retrieve the value of. + * + * @return The value of the specified attribute + */ + Object getAttribute (AttributedCharacterIterator.Attribute attrib); + + /** + * This method returns the index of the first character in the run that + * contains all attributes defined for the current character. + * + * @return The start index of the run + */ + int getRunStart(); + + /** + * This method returns the index of the first character in the run that + * contains all attributes in the specified <code>Set</code> defined for + * the current character. + * + * @param attribs The <code>Set</code> of attributes. + * + * @return The start index of the run. + */ + int getRunStart (Set attribs); + + /** + * This method returns the index of the first character in the run that + * contains the specified attribute defined for the current character. + * + * @param attrib The attribute. + * + * @return The start index of the run. + */ + int getRunStart (AttributedCharacterIterator.Attribute attrib); + + /** + * This method returns the index of the character after the end of the run + * that contains all attributed defined for the current character. + * + * @return The end index of the run. + */ + int getRunLimit(); + + /** + * This method returns the index of the character after the end of the run + * that contains all attributes in the specified <code>Set</code> defined + * for the current character. + * + * @param attribs The <code>Set</code> of attributes. + * + * @return The end index of the run. + */ + int getRunLimit (Set attribs); + + /** + * This methods returns the index of the character after the end of the run + * that contains the specified attribute defined for the current character. + * + * @param attrib The attribute. + * + * @return The end index of the run. + */ + int getRunLimit (AttributedCharacterIterator.Attribute attrib); + +} // interface AttributedCharacterIterator diff --git a/libjava/classpath/java/text/AttributedString.java b/libjava/classpath/java/text/AttributedString.java new file mode 100644 index 00000000000..b9ced8f5ac2 --- /dev/null +++ b/libjava/classpath/java/text/AttributedString.java @@ -0,0 +1,425 @@ +/* AttributedString.java -- Models text with attributes + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class models a <code>String</code> with attributes over various + * subranges of the string. It allows applications to access this + * information via the <code>AttributedCharcterIterator</code> interface. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class AttributedString +{ + +/*************************************************************************/ + +/* + * Inner Classes + */ + +/** + * This class contains the attributes and ranges of text over which + * that attributes apply. + */ +final class AttributeRange +{ + +/* + * Instance Variables + */ + +/** + * A Map of the attributes + */ +Map attribs; + +/** + * The beginning index of the attributes + */ +int begin_index; + +/** + * The ending index of the attributes + */ +int end_index; + +/*************************************************************************/ + +/* + * Constructors + */ + +AttributeRange(Map attribs, int begin_index, int end_index) +{ + this.attribs = attribs; + this.begin_index = begin_index; + this.end_index = end_index; +} + +} // Inner class AttributeRange + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This object holds the string we are representing. + */ +private StringCharacterIterator sci; + +/** + * This is the attribute information + */ +private AttributeRange[] attribs; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method initializes a new instance of <code>AttributedString</code> + * that represents the specified <code>String</code> with no attributes. + * + * @param str The <code>String</code> to be attributed. + */ +public +AttributedString(String str) +{ + sci = new StringCharacterIterator(str); + attribs = new AttributeRange[0]; +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>AttributedString</code> + * that represents that specified <code>String</code> with the specified + * attributes over the entire length of the <code>String</code>. + * + * @param str The <code>String</code> to be attributed. + * @param attributes The attribute list. + */ +public +AttributedString(String str, Map attributes) +{ + this(str); + + attribs = new AttributeRange[1]; + attribs[0] = new AttributeRange(attributes, 0, str.length()); +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>AttributedString</code> + * that will use the text and attribute information from the specified + * <code>AttributedCharacterIterator</code>. + * + * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information. + */ +public +AttributedString(AttributedCharacterIterator aci) +{ + this(aci, aci.getBeginIndex(), aci.getEndIndex(), null); +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>AttributedString</code> + * that will use the text and attribute information from the specified + * subrange of the specified <code>AttributedCharacterIterator</code>. + * + * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information. + * @param begin_index The beginning index of the text subrange. + * @param end_index The ending index of the text subrange. + */ +public +AttributedString(AttributedCharacterIterator aci, int begin_index, + int end_index) +{ + this(aci, begin_index, end_index, null); +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>AttributedString</code> + * that will use the text and attribute information from the specified + * subrange of the specified <code>AttributedCharacterIterator</code>. + * Only attributes from the source iterator that are present in the + * specified array of attributes will be included in the attribute list + * for this object. + * + * @param aci The <code>AttributedCharacterIterator</code> containing the text and attribute information. + * @param begin_index The beginning index of the text subrange. + * @param end_index The ending index of the text subrange. + * @param attributes A list of attributes to include from the iterator, or <code>null</code> to include all attributes. + */ +public +AttributedString(AttributedCharacterIterator aci, int begin_index, + int end_index, AttributedCharacterIterator.Attribute[] attributes) +{ + // Validate some arguments + if ((begin_index < 0) || (end_index < begin_index)) + throw new IllegalArgumentException("Bad index values"); + + StringBuffer sb = new StringBuffer(""); + + // Get the valid attribute list + Set all_attribs = aci.getAllAttributeKeys(); + if (attributes != null) + all_attribs.retainAll(Arrays.asList(attributes)); + + // Loop through and extract the attributes + char c = aci.setIndex(begin_index); + + ArrayList accum = new ArrayList(); + do + { + sb.append(c); + + Iterator iter = all_attribs.iterator(); + while(iter.hasNext()) + { + Object obj = iter.next(); + + // What should we do if this is not true? + if (!(obj instanceof AttributedCharacterIterator.Attribute)) + continue; + + AttributedCharacterIterator.Attribute attrib = + (AttributedCharacterIterator.Attribute)obj; + + // Make sure the attribute is defined. + int rl = aci.getRunLimit(attrib); + if (rl == -1) + continue; + if (rl > end_index) + rl = end_index; + rl -= begin_index; + + // Check to see if we already processed this one + int rs = aci.getRunStart(attrib); + if ((rs < aci.getIndex()) && (aci.getIndex() != begin_index)) + continue; + + // If the attribute run starts before the beginning index, we + // need to junk it if it is an Annotation. + Object attrib_obj = aci.getAttribute(attrib); + if (rs < begin_index) + { + if (attrib_obj instanceof Annotation) + continue; + + rs = begin_index; + } + else + { + rs -= begin_index; + } + + // Create a map object. Yes this will only contain one attribute + Map new_map = new Hashtable(); + new_map.put(attrib, attrib_obj); + + // Add it to the attribute list. + accum.add(new AttributeRange(new_map, rs, rl)); + } + + c = aci.next(); + } + while(c != CharacterIterator.DONE); + + attribs = new AttributeRange[accum.size()]; + attribs = (AttributeRange[]) accum.toArray(attribs); + + sci = new StringCharacterIterator(sb.toString()); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method adds a new attribute that will cover the entire string. + * + * @param attrib The attribute to add. + * @param value The value of the attribute. + */ +public void +addAttribute(AttributedCharacterIterator.Attribute attrib, Object value) +{ + addAttribute(attrib, value, 0, sci.getEndIndex()); +} + +/*************************************************************************/ + +/** + * This method adds a new attribute that will cover the specified subrange + * of the string. + * + * @param attrib The attribute to add. + * @param value The value of the attribute, which may be null. + * @param begin_index The beginning index of the subrange. + * @param end_index The ending index of the subrange. + * + * @exception IllegalArgumentException If attribute is <code>null</code> or the subrange is not valid. + */ +public void +addAttribute(AttributedCharacterIterator.Attribute attrib, Object value, + int begin_index, int end_index) +{ + if (attrib == null) + throw new IllegalArgumentException("null attribute"); + + HashMap hm = new HashMap(); + hm.put(attrib, value); + + addAttributes(hm, begin_index, end_index); +} + +/*************************************************************************/ + +/** + * This method adds all of the attributes in the specified list to the + * specified subrange of the string. + * + * @param attributes The list of attributes. + * @param begin_index The beginning index. + * @param end_index The ending index + * + * @param IllegalArgumentException If the list is <code>null</code> or the subrange is not valid. + */ +public void +addAttributes(Map attributes, int begin_index, int end_index) +{ + if (attributes == null) + throw new IllegalArgumentException("null attribute"); + + if ((begin_index < 0) || (end_index > sci.getEndIndex()) || + (end_index < begin_index)) + throw new IllegalArgumentException("bad range"); + + AttributeRange[] new_list = new AttributeRange[attribs.length + 1]; + System.arraycopy(attribs, 0, new_list, 0, attribs.length); + attribs = new_list; + attribs[attribs.length - 1] = new AttributeRange(attributes, begin_index, + end_index); +} + +/*************************************************************************/ + +/** + * This method returns an <code>AttributedCharacterIterator</code> that + * will iterate over the entire string. + * + * @return An <code>AttributedCharacterIterator</code> for the entire string. + */ +public AttributedCharacterIterator +getIterator() +{ + return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(), null)); +} + +/*************************************************************************/ + +/** + * This method returns an <code>AttributedCharacterIterator</code> that + * will iterate over the entire string. This iterator will return information + * about the list of attributes in the specified array. Attributes not in + * the array may or may not be returned by the iterator. If the specified + * array is <code>null</code>, all attributes will be returned. + * + * @param attributes A list of attributes to include in the returned iterator. + * + * @return An <code>AttributedCharacterIterator</code> for this string. + */ +public AttributedCharacterIterator +getIterator(AttributedCharacterIterator.Attribute[] attributes) +{ + return(getIterator(attributes, 0, sci.getEndIndex())); +} + +/*************************************************************************/ + +/** + * This method returns an <code>AttributedCharacterIterator</code> that + * will iterate over the specified subrange. This iterator will return information + * about the list of attributes in the specified array. Attributes not in + * the array may or may not be returned by the iterator. If the specified + * array is <code>null</code>, all attributes will be returned. + * + * @param attributes A list of attributes to include in the returned iterator. + * @param begin_index The beginning index of the subrange. + * @param end_index The ending index of the subrange. + * + * @return An <code>AttributedCharacterIterator</code> for this string. + */ +public AttributedCharacterIterator +getIterator(AttributedCharacterIterator.Attribute[] attributes, + int begin_index, int end_index) +{ + if ((begin_index < 0) || (end_index > sci.getEndIndex()) || + (end_index < begin_index)) + throw new IllegalArgumentException("bad range"); + + return(new AttributedStringIterator(sci, attribs, begin_index, end_index, + attributes)); +} + +} // class AttributedString + diff --git a/libjava/classpath/java/text/AttributedStringIterator.java b/libjava/classpath/java/text/AttributedStringIterator.java new file mode 100644 index 00000000000..7d7bf270cef --- /dev/null +++ b/libjava/classpath/java/text/AttributedStringIterator.java @@ -0,0 +1,348 @@ +/* AttributedStringIterator.java -- Class to iterate over AttributedString + Copyright (C) 1998, 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class implements the AttributedCharacterIterator interface. It + * is used by AttributedString.getIterator(). + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class AttributedStringIterator implements AttributedCharacterIterator +{ + +/*************************************************************************/ + +/** + * Instance Variables + */ + +/** + * The character iterator containing the text + */ +private CharacterIterator ci; + +/** + * The list of attributes and ranges + */ +private AttributedString.AttributeRange[] attribs; + +/** + * The list of attributes that the user is interested in. We may, + * at our option, not return any other attributes. + */ +private AttributedCharacterIterator.Attribute[] restricts; + +/*************************************************************************/ + +/* + * Constructors + */ + +AttributedStringIterator(StringCharacterIterator sci, + AttributedString.AttributeRange[] attribs, + int begin_index, int end_index, + AttributedCharacterIterator.Attribute[] restricts) +{ + this.ci = new StringCharacterIterator(sci, begin_index, end_index); + this.attribs = attribs; + this.restricts = restricts; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +// First we have a bunch of stupid redirects. If StringCharacterIterator +// weren't final, I just would have extended that for this class. Alas, no. + +public Object +clone() +{ + return(ci.clone()); +} + +public char +current() +{ + return(ci.current()); +} + +public char +next() +{ + return(ci.next()); +} + +public char +previous() +{ + return(ci.previous()); +} + +public char +first() +{ + return(ci.first()); +} + +public char +last() +{ + return(ci.last()); +} + +public int +getIndex() +{ + return(ci.getIndex()); +} + +public char +setIndex(int index) +{ + return(ci.setIndex(index)); +} + +public int +getBeginIndex() +{ + return(ci.getBeginIndex()); +} + +public int +getEndIndex() +{ + return(ci.getEndIndex()); +} + +/* + * Here is where the AttributedCharacterIterator methods start. + */ + +/*************************************************************************/ + +/** + * Returns a list of all the attribute keys that are defined anywhere + * on this string. + */ +public Set +getAllAttributeKeys() +{ + HashSet s = new HashSet(); + if (attribs == null) + return(s); + + for (int i = 0; i < attribs.length; i++) + { + if (attribs[i].begin_index > getEndIndex() + || attribs[i].end_index <= getBeginIndex()) + continue; + + Set key_set = attribs[i].attribs.keySet(); + Iterator iter = key_set.iterator(); + while (iter.hasNext()) + { + s.add(iter.next()); + } + } + + return(s); +} + +/*************************************************************************/ + +/** + * Various methods that determine how far the run extends for various + * attribute combinations. + */ + +public int +getRunLimit() +{ + return(getRunLimit(getAttributes().keySet())); +} + +public int +getRunLimit(AttributedCharacterIterator.Attribute attrib) +{ + HashSet s = new HashSet(); + s.add(attrib); + + return(getRunLimit(s)); +} + +public synchronized int +getRunLimit(Set attribute_set) +{ + boolean hit = false; + int runLimit = ci.getEndIndex (); + int pos = ci.getIndex (); + + for (int i = 0; i < attribs.length; ++i) + { + if (pos >= attribs[i].begin_index && + pos < attribs[i].end_index) + { + Iterator iter = attribute_set.iterator(); + while(iter.hasNext()) + if (attribs[i].attribs.containsKey(iter.next())) + { + hit = true; + runLimit = Math.min(runLimit, attribs[i].end_index); + } + } + } + if (hit) + return runLimit; + else + return ci.getEndIndex(); +} + +/*************************************************************************/ + +/** + * Various methods that determine where the run begins for various + * attribute combinations. + */ + +public int +getRunStart() +{ + return(getRunStart(getAttributes().keySet())); +} + +public int +getRunStart(AttributedCharacterIterator.Attribute attrib) +{ + HashSet s = new HashSet(); + s.add(attrib); + + return(getRunStart(s)); +} + +public int +getRunStart(Set attribute_set) +{ + boolean hit = false; + int runBegin = 0; + int pos = ci.getIndex (); + + for (int i = 0; i < attribs.length; ++i) + { + if (pos >= attribs[i].begin_index && + pos <= attribs[i].end_index) + { + Iterator iter = attribute_set.iterator(); + while(iter.hasNext()) + if (attribs[i].attribs.containsKey(iter.next())) + { + hit = true; + runBegin = Math.max(runBegin, attribs[i].begin_index); + } + } + } + if (hit) + return runBegin; + else + return -1; +} + +/*************************************************************************/ + +public Object +getAttribute(AttributedCharacterIterator.Attribute attrib) +{ + if (attribs == null) + return(null); + + for (int i = 0; i < attribs.length; i++) + { + Set key_set = attribs[i].attribs.keySet(); + Iterator iter = key_set.iterator(); + while (iter.hasNext()) + { + Object obj = iter.next(); + + // Check for attribute match and range match + if (obj.equals(attrib)) + if ((ci.getIndex() >= attribs[i].begin_index) && + (ci.getIndex() < attribs[i].end_index)) + return(attribs[i].attribs.get(obj)); + } + } + + return(null); +} + +/*************************************************************************/ + +/** + * Return a list of all the attributes and values defined for this + * character + */ +public Map +getAttributes() +{ + HashMap m = new HashMap(); + if (attribs == null) + return(m); + + for (int i = 0; i < attribs.length; i++) + { + if ((ci.getIndex() >= attribs[i].begin_index) && + (ci.getIndex() < attribs[i].end_index)) + m.putAll(attribs[i].attribs); + } + + return(m); +} + +} // class AttributedStringIterator + diff --git a/libjava/classpath/java/text/BreakIterator.java b/libjava/classpath/java/text/BreakIterator.java new file mode 100644 index 00000000000..d021dceba73 --- /dev/null +++ b/libjava/classpath/java/text/BreakIterator.java @@ -0,0 +1,374 @@ +/* BreakIterator.java -- Breaks text into elements + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This class iterates over text elements such as words, lines, sentences, + * and characters. It can only iterate over one of these text elements at + * a time. An instance of this class configured for the desired iteration + * type is created by calling one of the static factory methods, not + * by directly calling a constructor. + * + * The standard iterators created by the factory methods in this + * class will be valid upon creation. That is, their methods will + * not cause exceptions if called before you call setText(). + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 19, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct to 1.1. + */ +public abstract class BreakIterator implements Cloneable +{ + /** + * This value is returned by the <code>next()</code> and + * <code>previous</code> in order to indicate that the end of the + * text has been reached. + */ + // The value was discovered by writing a test program. + public static final int DONE = -1; + + /** + * This method initializes a new instance of <code>BreakIterator</code>. + * This protected constructor is available to subclasses as a default + * no-arg superclass constructor. + */ + protected BreakIterator () + { + } + + /** + * Create a clone of this object. + */ + public Object clone () + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method returns the index of the current text element boundary. + * + * @return The current text boundary. + */ + public abstract int current (); + + /** + * This method returns the first text element boundary in the text being + * iterated over. + * + * @return The first text boundary. + */ + public abstract int first (); + + /** + * This methdod returns the offset of the text element boundary following + * the specified offset. + * + * @param offset The text index from which to find the next text boundary. + * + * @param The next text boundary following the specified index. + */ + public abstract int following (int pos); + + /** + * This method returns a list of locales for which instances of + * <code>BreakIterator</code> are available. + * + * @return A list of available locales + */ + public static synchronized Locale[] getAvailableLocales () + { + Locale[] l = new Locale[1]; + l[0] = Locale.US; + return l; + } + + private static BreakIterator getInstance (String type, Locale loc) + { + String className; + try + { + ResourceBundle res + = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + className = res.getString(type); + } + catch (MissingResourceException x) + { + return null; + } + try + { + Class k = Class.forName(className); + return (BreakIterator) k.newInstance(); + } + catch (ClassNotFoundException x1) + { + return null; + } + catch (InstantiationException x2) + { + return null; + } + catch (IllegalAccessException x3) + { + return null; + } + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over characters as defined in the default locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getCharacterInstance () + { + return getCharacterInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over characters as defined in the specified locale. If the + * desired locale is not available, the default locale is used. + * + * @param locale The desired locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getCharacterInstance (Locale loc) + { + BreakIterator r = getInstance ("CharacterIterator", loc); + if (r == null) + r = new gnu.java.text.CharacterBreakIterator (); + return r; + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over line breaks as defined in the default locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getLineInstance () + { + return getLineInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over line breaks as defined in the specified locale. If the + * desired locale is not available, the default locale is used. + * + * @param locale The desired locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getLineInstance (Locale loc) + { + BreakIterator r = getInstance ("LineIterator", loc); + if (r == null) + r = new gnu.java.text.LineBreakIterator (); + return r; + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over sentences as defined in the default locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getSentenceInstance () + { + return getSentenceInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over sentences as defined in the specified locale. If the + * desired locale is not available, the default locale is used. + * + * @param locale The desired locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getSentenceInstance (Locale loc) + { + BreakIterator r = getInstance ("SentenceIterator", loc); + if (r == null) + r = new gnu.java.text.SentenceBreakIterator (); + return r; + } + + /** + * This method returns the text this object is iterating over as a + * <code>CharacterIterator</code>. + * + * @param The text being iterated over. + */ + public abstract CharacterIterator getText (); + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over words as defined in the default locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getWordInstance () + { + return getWordInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>BreakIterator</code> that will + * iterate over words as defined in the specified locale. If the + * desired locale is not available, the default locale is used. + * + * @param locale The desired locale. + * + * @return A <code>BreakIterator</code> instance for the default locale. + */ + public static BreakIterator getWordInstance (Locale loc) + { + BreakIterator r = getInstance ("WordIterator", loc); + if (r == null) + r = new gnu.java.text.WordBreakIterator (); + return r; + } + + /** + * This method tests whether or not the specified position is a text + * element boundary. + * + * @param offset The text position to test. + * + * @return <code>true</code> if the position is a boundary, + * <code>false</code> otherwise. + */ + public boolean isBoundary (int pos) + { + if (pos == 0) + return true; + return following (pos - 1) == pos; + } + + /** + * This method returns the last text element boundary in the text being + * iterated over. + * + * @return The last text boundary. + */ + public abstract int last (); + + /** + * This method returns the text element boundary following the current + * text position. + * + * @return The next text boundary. + */ + public abstract int next (); + + /** + * This method returns the n'th text element boundary following the current + * text position. + * + * @param n The number of text element boundaries to skip. + * + * @return The next text boundary. + */ + public abstract int next (int n); + + /** + * This methdod returns the offset of the text element boundary preceding + * the specified offset. + * + * @param offset The text index from which to find the preceding + * text boundary. + * + * @returns The next text boundary preceding the specified index. + */ + public int preceding (int pos) + { + if (following (pos) == DONE) + last (); + while (previous () >= pos) + ; + return current (); + } + + /** + * This method returns the text element boundary preceding the current + * text position. + * + * @return The previous text boundary. + */ + public abstract int previous (); + + /** + * This method sets the text string to iterate over. + * + * @param str The <code>String</code> to iterate over. + */ + public void setText (String newText) + { + setText (new StringCharacterIterator (newText)); + } + + /** + * This method sets the text to iterate over from the specified + * <code>CharacterIterator</code>. + * + * @param ci The desired <code>CharacterIterator</code>. + */ + public abstract void setText (CharacterIterator newText); +} diff --git a/libjava/classpath/java/text/CharacterIterator.java b/libjava/classpath/java/text/CharacterIterator.java new file mode 100644 index 00000000000..6b3f951d1fb --- /dev/null +++ b/libjava/classpath/java/text/CharacterIterator.java @@ -0,0 +1,144 @@ +/* CharacterIterator.java -- Iterate over a character range + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This interface defines a mechanism for iterating over a range of + * characters. For a given range of text, a beginning and ending index, + * as well as a current index are defined. These values can be queried + * by the methods in this interface. Additionally, various methods allow + * the index to be set. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface CharacterIterator extends Cloneable +{ + /** + * This is a special constant value that is returned when the beginning or + * end of the character range has been reached. + */ + char DONE = '\uFFFF'; + + /** + * This method returns the character at the current index position + * + * @return The character at the current index position. + */ + char current(); + + /** + * This method increments the current index and then returns the character + * at the new index value. If the index is already at <code>getEndIndex() - 1</code>, + * it will not be incremented. + * + * @return The character at the position of the incremented index value, + * or <code>DONE</code> if the index has reached getEndIndex() - 1 + */ + char next(); + + /** + * This method decrements the current index and then returns the character + * at the new index value. If the index value is already at the beginning + * index, it will not be decremented. + * + * @return The character at the position of the decremented index value, + * or <code>DONE</code> if index was already equal to the beginning index value. + */ + char previous(); + + /** + * This method sets the index value to the beginning of the range and returns + * the character there. + * + * @return The character at the beginning of the range, or <code>DONE</code> if the range is empty. + */ + char first(); + + /** + * This method sets the index value to <code>getEndIndex() - 1</code> and + * returns the character there. If the range is empty, then the index value + * will be set equal to the beginning index. + * + * @return The character at the end of the range, or <code>DONE</code> if the range is empty. + */ + char last(); + + /** + * This method returns the current value of the index. + * + * @return The current index value + */ + int getIndex(); + + /** + * This method sets the value of the index to the specified value, then + * returns the character at that position. + * + * @param index The new index value. + * + * @return The character at the new index value or <code>DONE</code> if the index value is equal to <code>getEndIndex</code>. + */ + char setIndex (int index) throws IllegalArgumentException; + + /** + * This method returns the character position of the first character in the + * range. + * + * @return The index of the first character in the range. + */ + int getBeginIndex(); + + /** + * This method returns the character position of the end of the text range. + * This will actually be the index of the first character following the + * end of the range. In the event the text range is empty, this will be + * equal to the first character in the range. + * + * @return The index of the end of the range. + */ + int getEndIndex(); + + /** + * This method creates a copy of this <code>CharacterIterator</code>. + * + * @return A copy of this <code>CharacterIterator</code>. + */ + Object clone(); + +} // interface CharacterIterator diff --git a/libjava/classpath/java/text/ChoiceFormat.java b/libjava/classpath/java/text/ChoiceFormat.java new file mode 100644 index 00000000000..23c8a8c3af0 --- /dev/null +++ b/libjava/classpath/java/text/ChoiceFormat.java @@ -0,0 +1,503 @@ +/* ChoiceFormat.java -- Format over a range of numbers + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.Vector; + +/** + * This class allows a format to be specified based on a range of numbers. + * To use this class, first specify two lists of formats and range terminators. + * These lists must be arrays of equal length. The format of index + * <code>i</code> will be selected for value <code>X</code> if + * <code>terminator[i] <= X < limit[i + 1]</code>. If the value X is not + * included in any range, then either the first or last format will be + * used depending on whether the value X falls outside the range. + * <p> + * This sounds complicated, but that is because I did a poor job of + * explaining it. Consider the following example: + * <p> + * +<pre>terminators = { 1, ChoiceFormat.nextDouble(1) } +formats = { "file", "files" }</pre> + * + * <p> + * In this case if the actual number tested is one or less, then the word + * "file" is used as the format value. If the number tested is greater than + * one, then "files" is used. This allows plurals to be handled + * gracefully. Note the use of the method <code>nextDouble</code>. This + * method selects the next highest double number than its argument. This + * effectively makes any double greater than 1.0 cause the "files" string + * to be selected. (Note that all terminator values are specified as + * doubles. + * <p> + * Note that in order for this class to work properly, the range terminator + * array must be sorted in ascending order and the format string array + * must be the same length as the terminator array. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 9, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.1. + */ +public class ChoiceFormat extends NumberFormat +{ + /** + * This method sets new range terminators and format strings for this + * object based on the specified pattern. This pattern is of the form + * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday". + * + * @param pattern The pattern of terminators and format strings. + * + * @exception IllegalArgumentException If the pattern is not valid + */ + public void applyPattern (String newPattern) + { + // Note: we assume the same kind of quoting rules apply here. + // This isn't explicitly documented. But for instance we accept + // '#' as a literal hash in a format string. + int index = 0, max = newPattern.length(); + Vector stringVec = new Vector (); + Vector limitVec = new Vector (); + StringBuffer buf = new StringBuffer (); + + while (true) + { + // Find end of double. + int dstart = index; + while (index < max) + { + char c = newPattern.charAt(index); + if (c == '#' || c == '\u2064' || c == '<') + break; + ++index; + } + + if (index == max) + throw new IllegalArgumentException ("unexpected end of text"); + Double d = new Double (newPattern.substring(dstart, index)); + + if (newPattern.charAt(index) == '<') + d = new Double (nextDouble (d.doubleValue())); + + limitVec.addElement(d); + + // Scan text. + ++index; + buf.setLength(0); + while (index < max) + { + char c = newPattern.charAt(index); + if (c == '\'' && index < max + 1 + && newPattern.charAt(index + 1) == '\'') + { + buf.append(c); + ++index; + } + else if (c == '\'' && index < max + 2) + { + buf.append(newPattern.charAt(index + 1)); + index += 2; + } + else if (c == '|') + break; + else + buf.append(c); + ++index; + } + + stringVec.addElement(buf.toString()); + if (index == max) + break; + ++index; + } + + choiceFormats = new String[stringVec.size()]; + stringVec.copyInto(choiceFormats); + + choiceLimits = new double[limitVec.size()]; + for (int i = 0; i < choiceLimits.length; ++i) + { + Double d = (Double) limitVec.elementAt(i); + choiceLimits[i] = d.doubleValue(); + } + } + + /** + * This method initializes a new instance of <code>ChoiceFormat</code> that + * generates its range terminator and format string arrays from the + * specified pattern. This pattern is of the form + * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday". + * This is the same pattern type used by the <code>applyPattern</code> + * method. + * + * @param pattern The pattern of terminators and format strings. + * + * @exception IllegalArgumentException If the pattern is not valid + */ + public ChoiceFormat (String newPattern) + { + super (); + applyPattern (newPattern); + } + + /** + * This method initializes a new instance of <code>ChoiceFormat</code> that + * will use the specified range terminators and format strings. + * + * @param choiceLimits The array of range terminators + * @param choiceFormats The array of format strings + */ + public ChoiceFormat (double[] choiceLimits, String[] choiceFormats) + { + super (); + setChoices (choiceLimits, choiceFormats); + } + + /** + * This method tests this object for equality with the specified + * object. This will be true if and only if: + * <ul> + * <li>The specified object is not <code>null</code>.</li> + * <li>The specified object is an instance of <code>ChoiceFormat</code>.</li> + * <li>The termination ranges and format strings are identical to + * this object's. </li> + * </ul> + * + * @param obj The object to test for equality against. + * + * @return <code>true</code> if the specified object is equal to + * this one, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof ChoiceFormat)) + return false; + ChoiceFormat cf = (ChoiceFormat) obj; + if (choiceLimits.length != cf.choiceLimits.length) + return false; + for (int i = choiceLimits.length - 1; i >= 0; --i) + { + if (choiceLimits[i] != cf.choiceLimits[i] + || !choiceFormats[i].equals(cf.choiceFormats[i])) + return false; + } + return true; + } + + /** + * This method appends the appropriate format string to the specified + * <code>StringBuffer</code> based on the supplied <code>long</code> + * argument. + * + * @param number The number used for determine (based on the range + * terminators) which format string to append. + * @param sb The <code>StringBuffer</code> to append the format string to. + * @param status Unused. + * + * @return The <code>StringBuffer</code> with the format string appended. + */ + public StringBuffer format (long num, StringBuffer appendBuf, + FieldPosition pos) + { + return format ((double) num, appendBuf, pos); + } + + /** + * This method appends the appropriate format string to the specified + * <code>StringBuffer</code> based on the supplied <code>double</code> + * argument. + * + * @param number The number used for determine (based on the range + * terminators) which format string to append. + * @param sb The <code>StringBuffer</code> to append the format string to. + * @param status Unused. + * + * @return The <code>StringBuffer</code> with the format string appended. + */ + public StringBuffer format (double num, StringBuffer appendBuf, + FieldPosition pos) + { + if (choiceLimits.length == 0) + return appendBuf; + + int index = 0; + if (! Double.isNaN(num) && num >= choiceLimits[0]) + { + for (; index < choiceLimits.length - 1; ++index) + { + if (choiceLimits[index] <= num && num < choiceLimits[index + 1]) + break; + } + } + + return appendBuf.append(choiceFormats[index]); + } + + /** + * This method returns the list of format strings in use. + * + * @return The list of format objects. + */ + public Object[] getFormats () + { + return (Object[]) choiceFormats.clone(); + } + + /** + * This method returns the list of range terminators in use. + * + * @return The list of range terminators. + */ + public double[] getLimits () + { + return (double[]) choiceLimits.clone(); + } + + /** + * This method returns a hash value for this object + * + * @return A hash value for this object. + */ + public int hashCode () + { + int hash = 0; + for (int i = 0; i < choiceLimits.length; ++i) + { + long v = Double.doubleToLongBits(choiceLimits[i]); + hash ^= (v ^ (v >>> 32)); + hash ^= choiceFormats[i].hashCode(); + } + return hash; + } + + /** + * This method returns the lowest possible double greater than the + * specified double. If the specified double value is equal to + * <code>Double.NaN</code> then that is the value returned. + * + * @param d The specified double + * + * @return The lowest double value greater than the specified double. + */ + public static final double nextDouble (double d) + { + return nextDouble (d, true); + } + + /** + * This method returns a double that is either the next highest double + * or next lowest double compared to the specified double depending on the + * value of the passed boolean parameter. If the boolean parameter is + * <code>true</code>, then the lowest possible double greater than the + * specified double will be returned. Otherwise the highest possible + * double less than the specified double will be returned. + * + * @param d The specified double + * @param positive <code>true</code> to return the next highest + * double, <code>false</code> otherwise. + * + * @return The next highest or lowest double value. + */ + public static double nextDouble (double d, boolean next) + { + if (Double.isInfinite(d) || Double.isNaN(d)) + return d; + + long bits = Double.doubleToLongBits(d); + + long mantMask = (1L << mantissaBits) - 1; + long mantissa = bits & mantMask; + + long expMask = (1L << exponentBits) - 1; + long exponent = (bits >>> mantissaBits) & expMask; + + if (next ^ (bits < 0)) // Increment magnitude + { + if (mantissa == (1L << mantissaBits) - 1) + { + mantissa = 0L; + exponent++; + + // Check for absolute overflow. + if (exponent >= (1L << mantissaBits)) + return (bits > 0) ? Double.POSITIVE_INFINITY + : Double.NEGATIVE_INFINITY; + } + else + mantissa++; + } + else // Decrement magnitude + { + if (exponent == 0L && mantissa == 0L) + { + // The only case where there is a change of sign + return next ? Double.MIN_VALUE : -Double.MIN_VALUE; + } + else + { + if (mantissa == 0L) + { + mantissa = (1L << mantissaBits) - 1; + exponent--; + } + else + mantissa--; + } + } + + long result = bits < 0 ? 1 : 0; + result = (result << exponentBits) | exponent; + result = (result << mantissaBits) | mantissa; + return Double.longBitsToDouble(result); + } + + /** + * I'm not sure what this method is really supposed to do, as it is + * not documented. + */ + public Number parse (String sourceStr, ParsePosition pos) + { + int index = pos.getIndex(); + for (int i = 0; i < choiceLimits.length; ++i) + { + if (sourceStr.startsWith(choiceFormats[i], index)) + { + pos.setIndex(index + choiceFormats[i].length()); + return new Double (choiceLimits[i]); + } + } + pos.setErrorIndex(index); + return new Double (Double.NaN); + } + + /** + * This method returns the highest possible double less than the + * specified double. If the specified double value is equal to + * <code>Double.NaN</code> then that is the value returned. + * + * @param d The specified double + * + * @return The highest double value less than the specified double. + */ + public static final double previousDouble (double d) + { + return nextDouble (d, false); + } + + /** + * This method sets new range terminators and format strings for this + * object. + * + * @param choiceLimits The new range terminators + * @param choiceFormats The new choice formats + */ + public void setChoices (double[] choiceLimits, String[] choiceFormats) + { + if (choiceLimits == null || choiceFormats == null) + throw new NullPointerException (); + if (choiceLimits.length != choiceFormats.length) + throw new IllegalArgumentException (); + this.choiceFormats = (String[]) choiceFormats.clone(); + this.choiceLimits = (double[]) choiceLimits.clone(); + } + + private void quoteString (StringBuffer dest, String text) + { + int max = text.length(); + for (int i = 0; i < max; ++i) + { + char c = text.charAt(i); + if (c == '\'') + { + dest.append(c); + dest.append(c); + } + else if (c == '#' || c == '|' || c == '\u2064' || c == '<') + { + dest.append('\''); + dest.append(c); + dest.append('\''); + } + else + dest.append(c); + } + } + + /** + * This method returns the range terminator list and format string list + * as a <code>String</code> suitable for using with the + * <code>applyPattern</code> method. + * + * @return A pattern string for this object + */ + public String toPattern () + { + StringBuffer result = new StringBuffer (); + for (int i = 0; i < choiceLimits.length; ++i) + { + result.append(choiceLimits[i]); + result.append('#'); + quoteString (result, choiceFormats[i]); + } + return result.toString(); + } + + /** + * This is the list of format strings. Note that this variable is + * specified by the serialization spec of this class. + */ + private String[] choiceFormats; + + /** + * This is the list of range terminator values. Note that this variable is + * specified by the serialization spec of this class. + */ + private double[] choiceLimits; + + // Number of mantissa bits in double. + private static final int mantissaBits = 52; + // Number of exponent bits in a double. + private static final int exponentBits = 11; + + private static final long serialVersionUID = 1795184449645032964L; +} diff --git a/libjava/classpath/java/text/CollationElementIterator.java b/libjava/classpath/java/text/CollationElementIterator.java new file mode 100644 index 00000000000..60b148ef7c1 --- /dev/null +++ b/libjava/classpath/java/text/CollationElementIterator.java @@ -0,0 +1,467 @@ +/* CollationElementIterator.java -- Walks through collation elements + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.ArrayList; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to JDK 1.1. + */ + +/** + * This class walks through the character collation elements of a + * <code>String</code> as defined by the collation rules in an instance of + * <code>RuleBasedCollator</code>. There is no public constructor for + * this class. An instance is created by calling the + * <code>getCollationElementIterator</code> method on + * <code>RuleBasedCollator</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Guilhem Lavaux (guilhem.lavaux@free.fr) + */ +public final class CollationElementIterator +{ + /** + * This is a constant value that is returned to indicate that the end of + * the string was encountered. + */ + public static final int NULLORDER = -1; + + /** + * This is the RuleBasedCollator this object was created from. + */ + RuleBasedCollator collator; + + /** + * This is the String that is being iterated over. + */ + String text; + + /** + * This is the index into the collation decomposition where we are currently scanning. + */ + int index; + + /** + * This is the index into the String where we are currently scanning. + */ + int textIndex; + + /** + * Array containing the collation decomposition of the + * text given to the constructor. + */ + private RuleBasedCollator.CollationElement[] text_decomposition; + + /** + * Array containing the index of the specified block. + */ + private int[] text_indexes; + + /** + * This method initializes a new instance of <code>CollationElementIterator</code> + * to iterate over the specified <code>String</code> using the rules in the + * specified <code>RuleBasedCollator</code>. + * + * @param collator The <code>RuleBasedCollation</code> used for calculating collation values + * @param text The <code>String</code> to iterate over. + */ + CollationElementIterator(RuleBasedCollator collator, String text) + { + this.collator = collator; + + setText (text); + } + + RuleBasedCollator.CollationElement nextBlock() + { + if (index >= text_decomposition.length) + return null; + + RuleBasedCollator.CollationElement e = text_decomposition[index]; + + textIndex = text_indexes[index+1]; + + index++; + + return e; + } + + RuleBasedCollator.CollationElement previousBlock() + { + if (index == 0) + return null; + + index--; + RuleBasedCollator.CollationElement e = text_decomposition[index]; + + textIndex = text_indexes[index+1]; + + return e; + } + + /** + * This method returns the collation ordering value of the next character sequence + * in the string (it may be an extended character following collation rules). + * This method will return <code>NULLORDER</code> if the + * end of the string was reached. + * + * @return The collation ordering value. + */ + public int next() + { + RuleBasedCollator.CollationElement e = nextBlock(); + + if (e == null) + return NULLORDER; + + return e.getValue(); + } + + /** + * This method returns the collation ordering value of the previous character + * in the string. This method will return <code>NULLORDER</code> if the + * beginning of the string was reached. + * + * @return The collation ordering value. + */ + public int previous() + { + RuleBasedCollator.CollationElement e = previousBlock(); + + if (e == null) + return NULLORDER; + + return e.getValue(); + } + + /** + * This method returns the primary order value for the given collation + * value. + * + * @param value The collation value returned from <code>next()</code> or <code>previous()</code>. + * + * @return The primary order value of the specified collation value. This is the high 16 bits. + */ + public static int primaryOrder(int order) + { + // From the JDK 1.2 spec. + return order >>> 16; + } + + /** + * This method resets the internal position pointer to read from the + * beginning of the <code>String</code> again. + */ + public void reset() + { + index = 0; + textIndex = 0; + } + + /** + * This method returns the secondary order value for the given collation + * value. + * + * @param value The collation value returned from <code>next()</code> or <code>previous()</code>. + * + * @return The secondary order value of the specified collation value. This is the bits 8-15. + */ + public static short secondaryOrder(int order) + { + // From the JDK 1.2 spec. + return (short) ((order >>> 8) & 255); + } + + /** + * This method returns the tertiary order value for the given collation + * value. + * + * @param value The collation value returned from <code>next()</code> or <code>previous()</code>. + * + * @return The tertiary order value of the specified collation value. This is the low eight bits. + */ + public static short tertiaryOrder(int order) + { + // From the JDK 1.2 spec. + return (short) (order & 255); + } + + /** + * This method sets the <code>String</code> that it is iterating over + * to the specified <code>String</code>. + * + * @param text The new <code>String</code> to iterate over. + * + * @since 1.2 + */ + public void setText(String text) + { + int idx = 0; + int idx_idx = 0; + int alreadyExpanded = 0; + int idxToMove = 0; + + this.text = text; + this.index = 0; + + String work_text = text.intern(); + + ArrayList a_element = new ArrayList(); + ArrayList a_idx = new ArrayList(); + + // Build element collection ordered as they come in "text". + while (idx < work_text.length()) + { + String key, key_old; + + Object object = null; + int p = 1; + + // IMPROVE: use a TreeMap with a prefix-ordering rule. + key_old = key = null; + do + { + if (object != null) + key_old = key; + key = work_text.substring (idx, idx+p); + object = collator.prefix_tree.get (key); + if (object != null && idx < alreadyExpanded) + { + RuleBasedCollator.CollationElement prefix = (RuleBasedCollator.CollationElement)object; + if (prefix.expansion != null && + prefix.expansion.startsWith(work_text.substring(0, idx))) + { + object = null; + key = key_old; + } + } + p++; + } + while (idx+p <= work_text.length()); + + if (object == null) + key = key_old; + + RuleBasedCollator.CollationElement prefix = + (RuleBasedCollator.CollationElement) collator.prefix_tree.get (key); + + /* + * First case: There is no such sequence in the database. + * We will have to build one from the context. + */ + if (prefix == null) + { + /* + * We are dealing with sequences in an expansion. They + * are treated as accented characters (tertiary order). + */ + if (alreadyExpanded > 0) + { + RuleBasedCollator.CollationElement e = + collator.getDefaultAccentedElement (work_text.charAt (idx)); + + a_element.add (e); + a_idx.add (new Integer(idx_idx)); + idx++; + alreadyExpanded--; + if (alreadyExpanded == 0) + { + /* There is not any characters left in the expansion set. + * We can increase the pointer in the source string. + */ + idx_idx += idxToMove; + idxToMove = 0; + } + else + idx_idx++; + } + else + { + /* This is a normal character. */ + RuleBasedCollator.CollationElement e = + collator.getDefaultElement (work_text.charAt (idx)); + Integer i_ref = new Integer(idx_idx); + + /* Don't forget to mark it as a special sequence so the + * string can be ordered. + */ + a_element.add (RuleBasedCollator.SPECIAL_UNKNOWN_SEQ); + a_idx.add (i_ref); + a_element.add (e); + a_idx.add (i_ref); + idx_idx++; + idx++; + } + continue; + } + + /* + * Second case: Here we have found a matching sequence. + * Here we have an expansion string prepend it to the "work text" and + * add the corresponding sorting element. We must also mark + */ + if (prefix.expansion != null) + { + work_text = prefix.expansion + + work_text.substring (idx+prefix.key.length()); + idx = 0; + a_element.add (prefix); + a_idx.add (new Integer(idx_idx)); + if (alreadyExpanded == 0) + idxToMove = prefix.key.length(); + alreadyExpanded += prefix.expansion.length()-prefix.key.length(); + } + else + { + /* Third case: the simplest. We have got the prefix and it + * has not to be expanded. + */ + a_element.add (prefix); + a_idx.add (new Integer(idx_idx)); + idx += prefix.key.length(); + /* If the sequence is in an expansion, we must decrease the + * counter. + */ + if (alreadyExpanded > 0) + { + alreadyExpanded -= prefix.key.length(); + if (alreadyExpanded == 0) + { + idx_idx += idxToMove; + idxToMove = 0; + } + } + else + idx_idx += prefix.key.length(); + } + } + + text_decomposition = (RuleBasedCollator.CollationElement[]) + a_element.toArray(new RuleBasedCollator.CollationElement[a_element.size()]); + text_indexes = new int[a_idx.size()+1]; + for (int i = 0; i < a_idx.size(); i++) + { + text_indexes[i] = ((Integer)a_idx.get(i)).intValue(); + } + text_indexes[a_idx.size()] = text.length(); + } + + /** + * This method sets the <code>String</code> that it is iterating over + * to the <code>String</code> represented by the specified + * <code>CharacterIterator</code>. + * + * @param source The <code>CharacterIterator</code> containing the new + * <code>String</code> to iterate over. + */ + public void setText(CharacterIterator source) + { + StringBuffer expand = new StringBuffer(); + + // For now assume we read from the beginning of the string. + for (char c = source.first(); + c != CharacterIterator.DONE; + c = source.next()) + expand.append(c); + + setText(expand.toString()); + } + + /** + * This method returns the current offset into the <code>String</code> + * that is being iterated over. + * + * @return The iteration index position. + * + * @since 1.2 + */ + public int getOffset() + { + return textIndex; + } + + /** + * This method sets the iteration index position into the current + * <code>String</code> to the specified value. This value must not + * be negative and must not be greater than the last index position + * in the <code>String</code>. + * + * @param offset The new iteration index position. + * + * @exception IllegalArgumentException If the new offset is not valid. + */ + public void setOffset(int offset) + { + if (offset < 0) + throw new IllegalArgumentException("Negative offset: " + offset); + + if (offset > (text.length() - 1)) + throw new IllegalArgumentException("Offset too large: " + offset); + + for (index = 0; index < text_decomposition.length; index++) + { + if (offset <= text_indexes[index]) + break; + } + /* + * As text_indexes[0] == 0, we should not have to take care whether index is + * greater than 0. It is always. + */ + if (text_indexes[index] == offset) + textIndex = offset; + else + textIndex = text_indexes[index-1]; + } + + /** + * This method returns the maximum length of any expansion sequence that + * ends with the specified collation order value. (Whatever that means). + * + * @param value The collation order value + * + * @param The maximum length of an expansion sequence. + */ + public int getMaxExpansion(int value) + { + return 1; + } +} diff --git a/libjava/classpath/java/text/CollationKey.java b/libjava/classpath/java/text/CollationKey.java new file mode 100644 index 00000000000..f7e3a2476de --- /dev/null +++ b/libjava/classpath/java/text/CollationKey.java @@ -0,0 +1,199 @@ +/* CollationKey.java -- Precomputed collation value + Copyright (C) 1998, 1999, 2000, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class represents a pre-computed series of bits representing a + * <code>String</code> for under a particular <code>Collator</code>. This + * value may be compared bitwise against another <code>CollationKey</code> + * representing a different <code>String</code> under the same + * <code>Collator</code> in a manner than is usually more efficient than + * using the raw <code>Collator</code> compare methods. There is overhead + * associated with calculating this value, so it is generally not + * advisable to compute <code>CollationKey</code>'s unless multiple + * comparisons against a <code>String</code> will be done. (For example, + * in a sort routine). + * <p> + * This class cannot be instantiated directly. Instead, a + * <code>CollationKey</code> is created by calling the + * <code>getCollationKey</code> method on an instance of <code>Collator</code>. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date March 25, 1999 + */ +public final class CollationKey implements Comparable +{ + /** + * This is the <code>Collator</code> this object was created from. + */ + private Collator collator; + + /** + * This is the <code>String</code> this object represents. + */ + private String originalText; + + /** + * This is the bit value for this key. + */ + private byte[] key; + + CollationKey (Collator collator, String originalText, byte[] key) + { + this.collator = collator; + this.originalText = originalText; + this.key = key; + } + + /** + * This method compares the specified object to this one. An integer is + * returned which indicates whether the specified object is less than, + * greater than, or equal to this object. + * + * @param ck The <code>CollationKey</code> to compare against this one. + * + * @return A negative integer if this object is less than the specified object, 0 if it is equal or a positive integer if it is greater than the specified object. + */ + public int compareTo (CollationKey ck) + { + int max = Math.min (key.length, ck.key.length); + + for (int i = 0; i < max; ++i) + { + if (key[i] != ck.key[i]) + return key[i] - ck.key[i]; + } + + return key.length - ck.key.length; + } + + /** + * This method compares the specified object to this one. The specified + * object must be an instance of <code>CollationKey</code> or an exception + * will be thrown. An integer is returned which indicates whether the + * specified object is less than, greater than, or equal to this object. + * + * @param obj The <code>Object</code> to compare against this one. + * + * @return A negative integer if this object is less than the specified object, 0 if it is equal or a positive integer if it is greater than the specified object. + */ + public int compareTo (Object obj) + { + return compareTo ((CollationKey) obj); + } + + /** + * This method tests the specified <code>Object</code> for equality with + * this object. This will be true if and only if: + * <p> + * <ul> + * <li>The specified object must not be <code>null</code></li> + * <li>The specified object is an instance of <code>CollationKey</code>.</li> + * <li>The specified object was created from the same <code>Collator</code> + * as this object.</li> + * <li>The specified object has the same source string and bit key as + * this object.</li> + * </ul> + * + * @param obj The <code>Object</code> to test for equality. + * + * @return <code>true</code> if the specified object is equal to this one, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof CollationKey)) + return false; + + CollationKey ck = (CollationKey) obj; + + if (ck.collator != collator) + return false; + + if (!ck.getSourceString ().equals (getSourceString ())) + return false; + + if (!ck.toByteArray ().equals (toByteArray ())) + return false; + + return true; + } + + /** + * This method returns the <code>String</code> that this object was created + * from. + * + * @return The source <code>String</code> for this object. + */ + public String getSourceString() + { + return originalText; + } + + /** + * This method returns a hash value for this object. The hash value + * returned will be the hash code of the bit key so that identical bit + * keys will return the same value. + * + * @return A hash value for this object. + */ + public int hashCode() + { + // We just follow BitSet instead of thinking up something new. + long h = originalText.hashCode(); + for (int i = key.length - 1; i >= 0; --i) + h ^= key[i] * (i + 1); + return (int) ((h >> 32) ^ h); + } + + /** + * This method returns the collation bit sequence as a byte array. + * + * @param A byte array containing the collation bit sequence. + */ + public byte[] toByteArray() + { + return key; + } +} diff --git a/libjava/classpath/java/text/Collator.java b/libjava/classpath/java/text/Collator.java new file mode 100644 index 00000000000..633bc672527 --- /dev/null +++ b/libjava/classpath/java/text/Collator.java @@ -0,0 +1,400 @@ +/* Collator.java -- Perform locale dependent String comparisons. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.Comparator; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This class is the abstract superclass of classes which perform + * locale dependent <code>String</code> comparisons. A caller requests + * an instance of <code>Collator</code> for a particular locale using + * the <code>getInstance()</code> static method in this class. That method + * will return a locale specific subclass of <code>Collator</code> which + * can be used to perform <code>String</code> comparisons for that locale. + * If a subclass of <code>Collator</code> cannot be located for a particular + * locale, a default instance for the current locale will be returned. + * + * In addition to setting the correct locale, there are two additional + * settings that can be adjusted to affect <code>String</code> comparisons: + * strength and decomposition. The strength value determines the level + * of signficance of character differences required for them to sort + * differently. (For example, whether or not capital letters are considered + * different from lower case letters). The decomposition value affects how + * variants of the same character are treated for sorting purposes. (For + * example, whether or not an accent is signficant or not). These settings + * are described in detail in the documentation for the methods and values + * that are related to them. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 18, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Mostly complete, but parts stubbed out. Look for FIXME. + */ +public abstract class Collator implements Comparator, Cloneable +{ + /** + * This constant is a strength value which indicates that only primary + * differences between characters will be considered signficant. As an + * example, two completely different English letters such as 'a' and 'b' + * are considered to have a primary difference. + */ + public static final int PRIMARY = 0; + + /** + * This constant is a strength value which indicates that only secondary + * or primary differences between characters will be considered + * significant. An example of a secondary difference between characters + * are instances of the same letter with different accented forms. + */ + public static final int SECONDARY = 1; + + /** + * This constant is a strength value which indicates that tertiary, + * secondary, and primary differences will be considered during sorting. + * An example of a tertiary difference is capitalization of a given letter. + * This is the default value for the strength setting. + */ + public static final int TERTIARY = 2; + + /** + * This constant is a strength value which indicates that any difference + * at all between character values are considered significant. + */ + public static final int IDENTICAL = 3; + + /** + * This constant indicates that accented characters won't be decomposed + * when performing comparisons. This will yield the fastest results, but + * will only work correctly in call cases for languages which do not + * use accents such as English. + */ + public static final int NO_DECOMPOSITION = 0; + + /** + * This constant indicates that only characters which are canonical variants + * in Unicode 2.0 will be decomposed prior to performing comparisons. This + * will cause accented languages to be sorted correctly. This is the + * default decomposition value. + */ + public static final int CANONICAL_DECOMPOSITION = 1; + + /** + * This constant indicates that both canonical variants and compatibility + * variants in Unicode 2.0 will be decomposed prior to performing + * comparisons. This is the slowest mode, but is required to get the + * correct sorting for certain languages with certain special formats. + */ + public static final int FULL_DECOMPOSITION = 2; + + /** + * This method initializes a new instance of <code>Collator</code> to have + * the default strength (TERTIARY) and decomposition + * (CANONICAL_DECOMPOSITION) settings. This constructor is protected and + * is for use by subclasses only. Non-subclass callers should use the + * static <code>getInstance()</code> methods of this class to instantiate + * <code>Collation</code> objects for the desired locale. + */ + protected Collator () + { + strength = TERTIARY; + decmp = CANONICAL_DECOMPOSITION; + } + + /** + * This method compares the two <code>String</code>'s and returns an + * integer indicating whether or not the first argument is less than, + * equal to, or greater than the second argument. The comparison is + * performed according to the rules of the locale for this + * <code>Collator</code> and the strength and decomposition rules in + * effect. + * + * @param str1 The first object to compare + * @param str2 The second object to compare + * + * @return A negative integer if str1 < str2, 0 if str1 == str2, or + * a positive integer if str1 > str2. + */ + public abstract int compare (String source, String target); + + /** + * This method compares the two <code>Object</code>'s and returns an + * integer indicating whether or not the first argument is less than, + * equal to, or greater than the second argument. These two objects + * must be <code>String</code>'s or an exception will be thrown. + * + * @param obj1 The first object to compare + * @param obj2 The second object to compare + * + * @return A negative integer if obj1 < obj2, 0 if obj1 == obj2, or + * a positive integer if obj1 > obj2. + * + * @exception ClassCastException If the arguments are not instances + * of <code>String</code>. + */ + public int compare (Object o1, Object o2) + { + return compare ((String) o1, (String) o2); + } + + /** + * This method tests the specified object for equality against this + * object. This will be true if and only if the following conditions are + * met: + * <ul> + * <li>The specified object is not <code>null</code>.</li> + * <li>The specified object is an instance of <code>Collator</code>.</li> + * <li>The specified object has the same strength and decomposition + * settings as this object.</li> + * </ul> + * + * @param obj The <code>Object</code> to test for equality against + * this object. + * + * @return <code>true</code> if the specified object is equal to + * this one, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof Collator)) + return false; + Collator c = (Collator) obj; + return decmp == c.decmp && strength == c.strength; + } + + /** + * This method tests whether the specified <code>String</code>'s are equal + * according to the collation rules for the locale of this object and + * the current strength and decomposition settings. + * + * @param str1 The first <code>String</code> to compare + * @param str2 The second <code>String</code> to compare + * + * @return <code>true</code> if the two strings are equal, + * <code>false</code> otherwise. + */ + public boolean equals (String source, String target) + { + return compare (source, target) == 0; + } + + /** + * This method returns a copy of this <code>Collator</code> object. + * + * @return A duplicate of this object. + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException _) + { + return null; + } + } + + /** + * This method returns an array of <code>Locale</code> objects which is + * the list of locales for which <code>Collator</code> objects exist. + * + * @return The list of locales for which <code>Collator</code>'s exist. + */ + public static synchronized Locale[] getAvailableLocales () + { + // FIXME + Locale[] l = new Locale[1]; + l[0] = Locale.US; + return l; + } + + /** + * This method transforms the specified <code>String</code> into a + * <code>CollationKey</code> for faster comparisons. This is useful when + * comparisons against a string might be performed multiple times, such + * as during a sort operation. + * + * @param str The <code>String</code> to convert. + * + * @return A <code>CollationKey</code> for the specified <code>String</code>. + */ + public abstract CollationKey getCollationKey (String source); + + /** + * This method returns the current decomposition setting for this + * object. This * will be one of NO_DECOMPOSITION, + * CANONICAL_DECOMPOSITION, or * FULL_DECOMPOSITION. See the + * documentation for those constants for an * explanation of this + * setting. + * + * @return The current decomposition setting. + */ + public synchronized int getDecomposition () + { + return decmp; + } + + /** + * This method returns an instance of <code>Collator</code> for the + * default locale. + * + * @return A <code>Collator</code> for the default locale. + */ + public static Collator getInstance () + { + return getInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>Collator</code> for the + * specified locale. If no <code>Collator</code> exists for the desired + * locale, a <code>Collator</code> for the default locale will be returned. + * + * @param locale The desired localed to load a <code>Collator</code> for. + * + * @return A <code>Collator</code> for the requested locale + */ + public static Collator getInstance (Locale loc) + { + ResourceBundle res; + String pattern; + try + { + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + pattern = res.getString("collation_rules"); + } + catch (MissingResourceException x) + { + pattern = "<0<1<2<3<4<5<6<7<8<9<A,a<b,B<c,C<d,D<e,E<f,F<g,G<h,H<i,I<j,J<k,K" + + "<l,L<m,M<n,N<o,O<p,P<q,Q<r,R<s,S<t,T<u,U<v,V<w,W<x,X<y,Y<z,Z"; + } + try + { + return new RuleBasedCollator (pattern); + } + catch (ParseException x) + { + throw (InternalError)new InternalError().initCause(x); + } + } + + /** + * This method returns the current strength setting for this object. This + * will be one of PRIMARY, SECONDARY, TERTIARY, or IDENTICAL. See the + * documentation for those constants for an explanation of this setting. + * + * @return The current strength setting. + */ + public synchronized int getStrength () + { + return strength; + } + + /** + * This method returns a hash code value for this object. + * + * @return A hash value for this object. + */ + public abstract int hashCode (); + + /** + * This method sets the decomposition setting for this object to the + * specified value. This must be one of NO_DECOMPOSITION, + * CANONICAL_DECOMPOSITION, or FULL_DECOMPOSITION. Otherwise an + * exception will be thrown. See the documentation for those + * contants for an explanation of this setting. + * + * @param decmp The new decomposition setting. + * + * @exception IllegalArgumentException If the requested + * decomposition setting is not valid. + */ + public synchronized void setDecomposition (int mode) + { + if (mode != NO_DECOMPOSITION + && mode != CANONICAL_DECOMPOSITION + && mode != FULL_DECOMPOSITION) + throw new IllegalArgumentException (); + decmp = mode; + } + + /** + * This method sets the strength setting for this object to the specified + * value. This must be one of PRIMARY, SECONDARY, TERTIARY, or IDENTICAL. + * Otherwise an exception is thrown. See the documentation for these + * constants for an explanation of this setting. + * + * @param strength The new strength setting. + * + * @exception IllegalArgumentException If the requested strength + * setting value is not valid. + */ + public synchronized void setStrength (int strength) + { + if (strength != PRIMARY && strength != SECONDARY + && strength != TERTIARY && strength != IDENTICAL) + throw new IllegalArgumentException (); + this.strength = strength; + } + + // Decompose a single character and append results to the buffer. + // FIXME: for libgcj this is a native method which handles + // decomposition. For Classpath, for now, it does nothing. + final void decomposeCharacter (char c, StringBuffer buf) + { + buf.append (c); + } + + /** + * This is the current collation decomposition setting. + */ + int decmp; + + /** + * This is the current collation strength setting. + */ + int strength; +} diff --git a/libjava/classpath/java/text/DateFormat.java b/libjava/classpath/java/text/DateFormat.java new file mode 100644 index 00000000000..f6dfceb14e8 --- /dev/null +++ b/libjava/classpath/java/text/DateFormat.java @@ -0,0 +1,892 @@ +/* DateFormat.java -- Class for formatting/parsing date/times + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.io.InvalidObjectException; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.TimeZone; + +/** + * @author Per Bothner (bothner@cygnus.com) + * @date October 25, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Mostly complete; search for FIXME to see omissions. + */ + +public abstract class DateFormat extends Format implements Cloneable +{ + protected Calendar calendar; + protected NumberFormat numberFormat; + + // (Values determined using a test program.) + public static final int FULL = 0; + public static final int LONG = 1; + public static final int MEDIUM = 2; + public static final int SHORT = 3; + public static final int DEFAULT = MEDIUM; + + /* These constants need to have these exact values. They + * correspond to index positions within the localPatternChars + * string for a given locale. Each locale may specify its + * own character for a particular field, but the position + * of these characters must correspond to an appropriate field + * number (as listed below), in order for their meaning to + * be determined. For example, the US locale uses + * the string "GyMdkHmsSEDFwWahKzYeugAZ", where 'G' is the character + * for era, 'y' for year, and so on down to 'Z' for time zone. + */ + /** + * Represents the position of the era + * pattern character in the array of + * localized pattern characters. + * For example, 'AD' is an era used + * in the Gregorian calendar system. + * In the U.S. locale, this is 'G'. + */ + public static final int ERA_FIELD = 0; + /** + * Represents the position of the year + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'y'. + */ + public static final int YEAR_FIELD = 1; + /** + * Represents the position of the month + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'M'. + */ + public static final int MONTH_FIELD = 2; + /** + * Represents the position of the date + * or day of the month pattern character + * in the array of localized pattern + * characters. In the U.S. locale, + * this is 'd'. + */ + public static final int DATE_FIELD = 3; + /** + * Represents the position of the 24 + * hour pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'k'. + * This field numbers hours from 1 to 24. + */ + public static final int HOUR_OF_DAY1_FIELD = 4; + /** + * Represents the position of the 24 + * hour pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'H'. + * This field numbers hours from 0 to 23. + */ + public static final int HOUR_OF_DAY0_FIELD = 5; + /** + * Represents the position of the minute + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'm'. + */ + public static final int MINUTE_FIELD = 6; + /** + * Represents the position of the second + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 's'. + */ + public static final int SECOND_FIELD = 7; + /** + * Represents the position of the millisecond + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'S'. + */ + public static final int MILLISECOND_FIELD = 8; + /** + * Represents the position of the day of the + * week pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'E'. + */ + public static final int DAY_OF_WEEK_FIELD = 9; + /** + * Represents the position of the day of the + * year pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'D'. + */ + public static final int DAY_OF_YEAR_FIELD = 10; + /** + * Represents the position of the day of the + * week in the month pattern character in the + * array of localized pattern characters. + * In the U.S. locale, this is 'F'. + */ + public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11; + /** + * Represents the position of the week of the + * year pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'w'. + */ + public static final int WEEK_OF_YEAR_FIELD = 12; + /** + * Represents the position of the week of the + * month pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'W'. + */ + public static final int WEEK_OF_MONTH_FIELD = 13; + /** + * Represents the position of the am/pm + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'a'. + */ + public static final int AM_PM_FIELD = 14; + /** + * Represents the position of the 12 + * hour pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'h'. + * This field numbers hours from 1 to 12. + */ + public static final int HOUR1_FIELD = 15; + /** + * Represents the position of the 12 + * hour pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'K'. + * This field numbers hours from 0 to 11. + */ + public static final int HOUR0_FIELD = 16; + /** + * Represents the position of the generic + * timezone pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'z'. + */ + public static final int TIMEZONE_FIELD = 17; + /** + * Represents the position of the ISO year + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'Y'. + * This is a GNU extension in accordance with + * the CLDR data used. This value may + * differ from the normal year value. + */ + public static final int ISO_YEAR_FIELD = 18; + /** + * Represents the position of the localized + * day of the week pattern character in the + * array of localized pattern characters. + * In the U.S. locale, this is 'e'. + * This is a GNU extension in accordance with + * the CLDR data used. This value only + * differs from the day of the week with + * numeric formatting, in which case the + * locale's first day of the week is used. + */ + public static final int LOCALIZED_DAY_OF_WEEK_FIELD = 19; + /** + * Represents the position of the extended year + * pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'u'. + * This is a GNU extension in accordance with + * the CLDR data used. This value modifies + * the year value, so as to incorporate the era. + * For example, in the Gregorian calendar system, + * the extended year is negative instead of being + * marked as BC. + */ + public static final int EXTENDED_YEAR_FIELD = 20; + /** + * Represents the position of the modified Julian + * day pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'g'. + * This is a GNU extension in accordance with + * the CLDR data used. This value differs + * from the standard Julian day in that days + * are marked from midnight onwards rather than + * noon, and the local time zone affects the value. + * In simple terms, it can be thought of as all + * the date fields represented as a single number. + */ + public static final int MODIFIED_JULIAN_DAY_FIELD = 21; + /** + * Represents the position of the millisecond + * in the day pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'A'. + * This is a GNU extension in accordance with + * the CLDR data used. This value represents + * all the time fields (excluding the time zone) + * numerically, giving the number of milliseconds + * into the day (e.g. 10 in the morning would + * be 10 * 60 * 60 * 1000). Any daylight savings + * offset also affects this value. + */ + public static final int MILLISECOND_IN_DAY_FIELD = 22; + /** + * Represents the position of the RFC822 + * timezone pattern character in the array of + * localized pattern characters. + * In the U.S. locale, this is 'Z'. + * This is a GNU extension in accordance with + * the CLDR data used. The value is the offset + * of the current time from GMT e.g. -0500 would + * be five hours prior to GMT. + */ + public static final int RFC822_TIMEZONE_FIELD = 23; + + public static class Field extends Format.Field + { + static final long serialVersionUID = 7441350119349544720L; + + private int calendarField; + + public static final DateFormat.Field ERA + = new Field("era", Calendar.ERA); + public static final DateFormat.Field YEAR + = new Field("year", Calendar.YEAR); + public static final DateFormat.Field MONTH + = new Field("month", Calendar.MONTH); + public static final DateFormat.Field DAY_OF_MONTH + = new Field("day of month", Calendar.DAY_OF_MONTH); + public static final DateFormat.Field HOUR_OF_DAY1 + = new Field("hour of day 1", Calendar.HOUR_OF_DAY); + public static final DateFormat.Field HOUR_OF_DAY0 + = new Field("hour of day 0", Calendar.HOUR_OF_DAY); + public static final DateFormat.Field MINUTE + = new Field("minute", Calendar.MINUTE); + public static final DateFormat.Field SECOND + = new Field("second", Calendar.SECOND); + public static final DateFormat.Field MILLISECOND + = new Field("millisecond", Calendar.MILLISECOND); + public static final DateFormat.Field DAY_OF_WEEK + = new Field("day of week", Calendar.DAY_OF_WEEK); + public static final DateFormat.Field DAY_OF_YEAR + = new Field("day of year", Calendar.DAY_OF_YEAR); + public static final DateFormat.Field DAY_OF_WEEK_IN_MONTH + = new Field("day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH); + public static final DateFormat.Field WEEK_OF_YEAR + = new Field("week of year", Calendar.WEEK_OF_YEAR); + public static final DateFormat.Field WEEK_OF_MONTH + = new Field("week of month", Calendar.WEEK_OF_MONTH); + public static final DateFormat.Field AM_PM + = new Field("am/pm", Calendar.AM_PM); + public static final DateFormat.Field HOUR1 + = new Field("hour1", Calendar.HOUR); + public static final DateFormat.Field HOUR0 + = new Field("hour0", Calendar.HOUR); + public static final DateFormat.Field TIME_ZONE + = new Field("timezone", Calendar.ZONE_OFFSET); + public static final DateFormat.Field ISO_YEAR + = new Field("iso year", Calendar.YEAR); + public static final DateFormat.Field LOCALIZED_DAY_OF_WEEK + = new Field("localized day of week", Calendar.DAY_OF_WEEK); + public static final DateFormat.Field EXTENDED_YEAR + = new Field("extended year", Calendar.YEAR); + public static final DateFormat.Field MODIFIED_JULIAN_DAY + = new Field("julian day", -1); + public static final DateFormat.Field MILLISECOND_IN_DAY + = new Field("millisecond in day", -1); + public static final DateFormat.Field RFC822_TIME_ZONE + = new Field("rfc822 timezone", Calendar.ZONE_OFFSET); + + static final DateFormat.Field[] allFields = + { + ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY1, + HOUR_OF_DAY0, MINUTE, SECOND, MILLISECOND, + DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH, + WEEK_OF_YEAR, WEEK_OF_MONTH, AM_PM, HOUR1, HOUR0, + TIME_ZONE, ISO_YEAR, LOCALIZED_DAY_OF_WEEK, + EXTENDED_YEAR, MODIFIED_JULIAN_DAY, MILLISECOND_IN_DAY, + RFC822_TIME_ZONE + }; + + // For deserialization + private Field() + { + super(""); + } + + protected Field(String name, int calendarField) + { + super(name); + this.calendarField = calendarField; + } + + public int getCalendarField() + { + return calendarField; + } + + public static Field ofCalendarField(int calendarField) + { + if (calendarField >= allFields.length || calendarField < 0) + throw new IllegalArgumentException("no such calendar field (" + + calendarField + ")"); + + return allFields[calendarField]; + } + + protected Object readResolve() throws InvalidObjectException + { + String s = getName(); + + for (int i=0;i<allFields.length;i++) + if (s.equals(allFields[i].getName())) + return allFields[i]; + + throw new InvalidObjectException("no such DateFormat field called " + s); + } + } + + /** + * This method initializes a new instance of <code>DateFormat</code>. + */ + protected DateFormat () + { + } + + /** + * This method tests this object for equality against the specified object. + * The two objects will be considered equal if an only if the specified + * object: + * <P> + * <ul> + * <li>Is not <code>null</code>.</li> + * <li>Is an instance of <code>DateFormat</code>.</li> + * <li>Has the same numberFormat field value as this object.</li> + * </ul> + * + * @param obj The object to test for equality against. + * + * @return <code>true</code> if the specified object is equal to this object, + * <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (!(obj instanceof DateFormat)) + return false; + + DateFormat d = (DateFormat) obj; + + return numberFormat.equals(d.numberFormat); + } + + /** + * This method returns a copy of this object. + * + * @return A copy of this object. + */ + public Object clone () + { + // We know the superclass just call's Object's generic cloner. + return super.clone (); + } + + /** + * This method formats the specified <code>Object</code> into a date string + * and appends it to the specified <code>StringBuffer</code>. + * The specified object must be an instance of <code>Number</code> or + * <code>Date</code> or an <code>IllegalArgumentException</code> will be + * thrown. + * + * @param obj The <code>Object</code> to format. + * @param toAppendTo The <code>StringBuffer</code> to append the resultant + * <code>String</code> to. + * @param fieldPosition Is updated to the start and end index of the + * specified field. + * + * @return The <code>StringBuffer</code> supplied on input, with the + * formatted date/time appended. + */ + public final StringBuffer format (Object obj, + StringBuffer buf, FieldPosition pos) + { + if (obj instanceof Number) + obj = new Date(((Number) obj).longValue()); + else if (! (obj instanceof Date)) + throw new IllegalArgumentException + ("Cannot format given Object as a Date"); + + return format ((Date) obj, buf, pos); + } + + /** + * Formats the date argument according to the pattern specified. + * + * @param date The formatted date. + */ + public final String format (Date date) + { + StringBuffer sb = new StringBuffer (); + format (date, sb, new FieldPosition (MONTH_FIELD)); + return sb.toString(); + } + + /** + * This method formats a <code>Date</code> into a string and appends it + * to the specified <code>StringBuffer</code>. + * + * @param date The <code>Date</code> value to format. + * @param toAppendTo The <code>StringBuffer</code> to append the resultant + * <code>String</code> to. + * @param fieldPosition Is updated to the start and end index of the + * specified field. + * + * @return The <code>StringBuffer</code> supplied on input, with the + * formatted date/time appended. + */ + public abstract StringBuffer format (Date date, + StringBuffer buf, FieldPosition pos); + + /** + * This method returns a list of available locales supported by this + * class. + */ + public static Locale[] getAvailableLocales() + { + return Locale.getAvailableLocales(); + } + + /** + * This method returns the <code>Calendar</code> object being used by + * this object to parse/format datetimes. + * + * @return The <code>Calendar</code> being used by this object. + * + * @see java.util.Calendar + */ + public Calendar getCalendar () + { + return calendar; + } + + private static DateFormat computeInstance (int style, Locale loc, + boolean use_date, boolean use_time) + { + return computeInstance (style, style, loc, use_date, use_time); + } + + private static DateFormat computeInstance (int dateStyle, int timeStyle, + Locale loc, boolean use_date, + boolean use_time) + { + ResourceBundle res; + try + { + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + } + catch (MissingResourceException x) + { + res = null; + } + + String pattern = null; + if (use_date) + { + String name, def; + switch (dateStyle) + { + case FULL: + name = "fullDateFormat"; + def = "EEEE MMMM d, yyyy G"; + break; + case LONG: + name = "longDateFormat"; + def = "MMMM d, yyyy"; + break; + case MEDIUM: + name = "mediumDateFormat"; + def = "d-MMM-yy"; + break; + case SHORT: + name = "shortDateFormat"; + def = "M/d/yy"; + break; + default: + throw new IllegalArgumentException (); + } + try + { + pattern = res == null ? def : res.getString(name); + } + catch (MissingResourceException x) + { + pattern = def; + } + } + + if (use_time) + { + if (pattern == null) + pattern = ""; + else + pattern += " "; + + String name, def; + switch (timeStyle) + { + case FULL: + name = "fullTimeFormat"; + def = "h:mm:ss;S 'o''clock' a z"; + break; + case LONG: + name = "longTimeFormat"; + def = "h:mm:ss a z"; + break; + case MEDIUM: + name = "mediumTimeFormat"; + def = "h:mm:ss a"; + break; + case SHORT: + name = "shortTimeFormat"; + def = "h:mm a"; + break; + default: + throw new IllegalArgumentException (); + } + + String s; + try + { + s = res == null ? def : res.getString(name); + } + catch (MissingResourceException x) + { + s = def; + } + pattern += s; + } + + return new SimpleDateFormat (pattern, loc); + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the default formatting style for dates. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getDateInstance () + { + return getDateInstance (DEFAULT, Locale.getDefault()); + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the specified formatting style for dates. + * + * @param style The type of formatting to perform. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getDateInstance (int style) + { + return getDateInstance (style, Locale.getDefault()); + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the specified formatting style for dates. The specified + * localed will be used in place of the default. + * + * @param style The type of formatting to perform. + * @param aLocale The desired locale. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getDateInstance (int style, Locale loc) + { + return computeInstance (style, loc, true, false); + } + + /** + * This method returns a new instance of <code>DateFormat</code> that + * formats both dates and times using the <code>SHORT</code> style. + * + * @return A new <code>DateFormat</code>instance. + */ + public static final DateFormat getDateTimeInstance () + { + return getDateTimeInstance (DEFAULT, DEFAULT, Locale.getDefault()); + } + + /** + * This method returns a new instance of <code>DateFormat</code> that + * formats both dates and times using the <code>DEFAULT</code> style. + * + * @return A new <code>DateFormat</code>instance. + */ + public static final DateFormat getDateTimeInstance (int dateStyle, + int timeStyle) + { + return getDateTimeInstance (dateStyle, timeStyle, Locale.getDefault()); + } + + /** + * This method returns a new instance of <code>DateFormat</code> that + * formats both dates and times using the specified styles. + * + * @param dateStyle The desired style for date formatting. + * @param timeStyle The desired style for time formatting + * + * @return A new <code>DateFormat</code>instance. + */ + public static final DateFormat getDateTimeInstance (int dateStyle, + int timeStyle, + Locale loc) + { + return computeInstance (dateStyle, timeStyle, loc, true, true); + } + + /** + * This method returns a new instance of <code>DateFormat</code> that + * formats both dates and times using the <code>SHORT</code> style. + * + * @return A new <code>DateFormat</code>instance. + */ + public static final DateFormat getInstance () + { + // JCL book says SHORT. + return getDateTimeInstance (SHORT, SHORT, Locale.getDefault()); + } + + /** + * This method returns the <code>NumberFormat</code> object being used + * by this object to parse/format time values. + * + * @return The <code>NumberFormat</code> in use by this object. + */ + public NumberFormat getNumberFormat () + { + return numberFormat; + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the default formatting style for times. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getTimeInstance () + { + return getTimeInstance (DEFAULT, Locale.getDefault()); + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the specified formatting style for times. + * + * @param style The type of formatting to perform. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getTimeInstance (int style) + { + return getTimeInstance (style, Locale.getDefault()); + } + + /** + * This method returns an instance of <code>DateFormat</code> that will + * format using the specified formatting style for times. The specified + * localed will be used in place of the default. + * + * @param style The type of formatting to perform. + * @param aLocale The desired locale. + * + * @return A new <code>DateFormat</code> instance. + */ + public static final DateFormat getTimeInstance (int style, Locale loc) + { + return computeInstance (style, loc, false, true); + } + + /** + * This method returns the <code>TimeZone</code> object being used by + * this instance. + * + * @return The time zone in use. + */ + public TimeZone getTimeZone () + { + return calendar.getTimeZone(); + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + if (numberFormat != null) + return numberFormat.hashCode(); + else + return 0; + } + + /** + * This method indicates whether or not the parsing of date and time + * values should be done in a lenient value. + * + * @return <code>true</code> if date/time parsing is lenient, + * <code>false</code> otherwise. + */ + public boolean isLenient () + { + return calendar.isLenient(); + } + + /** + * This method parses the specified date/time string. + * + * @param source The string to parse. + * @return The resultant date. + * + * @exception ParseException If the specified string cannot be parsed. + */ + public Date parse (String source) throws ParseException + { + ParsePosition pos = new ParsePosition(0); + Date result = parse (source, pos); + if (result == null) + { + int index = pos.getErrorIndex(); + if (index < 0) + index = pos.getIndex(); + throw new ParseException("invalid Date syntax in \"" + + source + '\"', index); + } + return result; + } + + /** + * This method parses the specified <code>String</code> into a + * <code>Date</code>. The <code>pos</code> argument contains the + * starting parse position on method entry and the ending parse + * position on method exit. + * + * @param text The string to parse. + * @param pos The starting parse position in entry, the ending parse + * position on exit. + * + * @return The parsed date, or <code>null</code> if the string cannot + * be parsed. + */ + public abstract Date parse (String source, ParsePosition pos); + + /** + * This method is identical to <code>parse(String, ParsePosition)</code>, + * but returns its result as an <code>Object</code> instead of a + * <code>Date</code>. + * + * @param source The string to parse. + * @param pos The starting parse position in entry, the ending parse + * position on exit. + * + * @return The parsed date, or <code>null</code> if the string cannot + * be parsed. + */ + public Object parseObject (String source, ParsePosition pos) + { + return parse(source, pos); + } + + /** + * This method specified the <code>Calendar</code> that should be used + * by this object to parse/format datetimes. + * + * @param The new <code>Calendar</code> for this object. + * + * @see java.util.Calendar + */ + public void setCalendar (Calendar calendar) + { + this.calendar = calendar; + } + + /** + * This method specifies whether or not this object should be lenient in + * the syntax it accepts while parsing date/time values. + * + * @param lenient <code>true</code> if parsing should be lenient, + * <code>false</code> otherwise. + */ + public void setLenient (boolean lenient) + { + calendar.setLenient(lenient); + } + + /** + * This method specifies the <code>NumberFormat</code> object that should + * be used by this object to parse/format times. + * + * @param The <code>NumberFormat</code> in use by this object. + */ + public void setNumberFormat (NumberFormat numberFormat) + { + this.numberFormat = numberFormat; + } + + /** + * This method sets the time zone that should be used by this object. + * + * @param The new time zone. + */ + public void setTimeZone (TimeZone timeZone) + { + calendar.setTimeZone(timeZone); + } +} diff --git a/libjava/classpath/java/text/DateFormatSymbols.java b/libjava/classpath/java/text/DateFormatSymbols.java new file mode 100644 index 00000000000..543a5c13a78 --- /dev/null +++ b/libjava/classpath/java/text/DateFormatSymbols.java @@ -0,0 +1,525 @@ +/* DateFormatSymbols.java -- Format over a range of numbers + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.StringTokenizer; + +/** + * This class acts as container for locale specific date/time formatting + * information such as the days of the week and the months of the year. + * @author Per Bothner (bothner@cygnus.com) + * @date October 24, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3. + * Status: Believed complete and correct. + */ +public class DateFormatSymbols implements java.io.Serializable, Cloneable +{ + String[] ampms; + String[] eras; + private String localPatternChars; + String[] months; + String[] shortMonths; + String[] shortWeekdays; + String[] weekdays; + private String[][] zoneStrings; + + private static final long serialVersionUID = -5987973545549424702L; + + // The order of these prefixes must be the same as in DateFormat + private static final String[] formatPrefixes = + { + "full", "long", "medium", "short" + }; + + // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL, + // and DEFAULT (constants defined in java.text.DateFormat). While + // not part of the official spec, we need a way to get at locale-specific + // default formatting patterns. They are declared package scope so + // as to be easily accessible where needed (DateFormat, SimpleDateFormat). + transient String[] dateFormats; + transient String[] timeFormats; + + private static String[] getStringArray(ResourceBundle res, String name) + { + return res.getString(name).split("\u00ae"); + } + + private String[][] getZoneStrings(ResourceBundle res) + { + try + { + int index = 0; + String data = res.getString("zoneStrings"); + String[] zones = data.split("\u00a9"); + String[][] array = new String[zones.length][]; + for (int a = 0; a < zones.length; ++a) + array[a] = zones[a].split("\u00ae"); + return array; + } + catch (MissingResourceException e) + { + return new String[0][]; + } + } + + private String[] formatsForKey(ResourceBundle res, String key) + { + String[] values = new String[formatPrefixes.length]; + + for (int i = 0; i < formatPrefixes.length; i++) + values[i] = res.getString(formatPrefixes[i] + key); + + return values; + } + + /** + * This method initializes a new instance of <code>DateFormatSymbols</code> + * by loading the date format information for the specified locale. + * + * @param locale The locale for which date formatting symbols should + * be loaded. + */ + public DateFormatSymbols (Locale locale) throws MissingResourceException + { + ResourceBundle res + = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale, + ClassLoader.getSystemClassLoader()); + + ampms = getStringArray(res, "ampms"); + eras = getStringArray(res, "eras"); + localPatternChars = res.getString("localPatternChars"); + months = getStringArray(res, "months"); + shortMonths = getStringArray(res, "shortMonths"); + shortWeekdays = getStringArray(res, "shortWeekdays"); + weekdays = getStringArray(res, "weekdays"); + zoneStrings = getZoneStrings(res); + dateFormats = formatsForKey(res, "DateFormat"); + timeFormats = formatsForKey(res, "TimeFormat"); + } + + /** + * This method loads the format symbol information for the default + * locale. + */ + public DateFormatSymbols () throws MissingResourceException + { + this (Locale.getDefault()); + } + + /** + * This method returns the list of strings used for displaying AM or PM. + * This is a two element <code>String</code> array indexed by + * <code>Calendar.AM</code> and <code>Calendar.PM</code> + * + * @return The list of AM/PM display strings. + */ + public String[] getAmPmStrings() + { + return ampms; + } + + /** + * This method returns the list of strings used for displaying eras + * (e.g., "BC" and "AD"). This is a two element <code>String</code> + * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. + * + * @return The list of era disply strings. + */ + public String[] getEras() + { + return eras; + } + + /** + * This method returns the pattern character information for this + * object. This is an 18 character string that contains the characters + * that are used in creating the date formatting strings in + * <code>SimpleDateFormat</code>. The following are the character + * positions in the string and which format character they correspond + * to (the character in parentheses is the default value in the US English + * locale): + * <p> + * <ul> + * <li>0 - era (G)</li> + * <li>1 - year (y)</li> + * <li>2 - month (M)</li> + * <li>3 - day of month (d)</li> + * <li>4 - hour out of 12, from 1-12 (h)</li> + * <li>5 - hour out of 24, from 0-23 (H)</li> + * <li>6 - minute (m)</li> + * <li>7 - second (s)</li> + * <li>8 - millisecond (S)</li> + * <li>9 - date of week (E)</li> + * <li>10 - date of year (D)</li> + * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li> + * <li>12 - week in year (w)</li> + * <li>13 - week in month (W)</li> + * <li>14 - am/pm (a)</li> + * <li>15 - hour out of 24, from 1-24 (k)</li> + * <li>16 - hour out of 12, from 0-11 (K)</li> + * <li>17 - time zone (z)</li> + * </ul> + * + * @return The format patter characters + */ + public String getLocalPatternChars() + { + return localPatternChars; + } + + /** + * This method returns the list of strings used for displaying month + * names (e.g., "January" and "February"). This is a thirteen element + * string array indexed by <code>Calendar.JANUARY</code> through + * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen + * elements because some calendars have thriteen months. + * + * @return The list of month display strings. + */ + public String[] getMonths () + { + return months; + } + + /** + * This method returns the list of strings used for displaying abbreviated + * month names (e.g., "Jan" and "Feb"). This is a thirteen element + * <code>String</code> array indexed by <code>Calendar.JANUARY</code> + * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen + * elements because some calendars have thirteen months. + * + * @return The list of abbreviated month display strings. + */ + public String[] getShortMonths () + { + return shortMonths; + } + + /** + * This method returns the list of strings used for displaying abbreviated + * weekday names (e.g., "Sun" and "Mon"). This is an eight element + * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> + * through <code>Calendar.SATURDAY</code>. Note that the first element + * of this array is ignored. + * + * @return This list of abbreviated weekday display strings. + */ + public String[] getShortWeekdays () + { + return shortWeekdays; + } + + /** + * This method returns the list of strings used for displaying weekday + * names (e.g., "Sunday" and "Monday"). This is an eight element + * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> + * through <code>Calendar.SATURDAY</code>. Note that the first element + * of this array is ignored. + * + * @return This list of weekday display strings. + */ + public String[] getWeekdays () + { + return weekdays; + } + + /** + * This method returns this list of localized timezone display strings. + * This is a two dimensional <code>String</code> array where each row in + * the array contains five values: + * <P> + * <ul> + * <li>0 - The non-localized time zone id string.</li> + * <li>1 - The long name of the time zone (standard time).</li> + * <li>2 - The short name of the time zone (standard time).</li> + * <li>3 - The long name of the time zone (daylight savings time).</li> + * <li>4 - the short name of the time zone (daylight savings time).</li> + * </ul> + * + * @return The list of time zone display strings. + */ + public String[] [] getZoneStrings () + { + return zoneStrings; + } + + /** + * This method sets the list of strings used to display AM/PM values to + * the specified list. + * This is a two element <code>String</code> array indexed by + * <code>Calendar.AM</code> and <code>Calendar.PM</code> + * + * @param ampms The new list of AM/PM display strings. + */ + public void setAmPmStrings (String[] value) + { + ampms = value; + } + + /** + * This method sets the list of strings used to display time eras to + * to the specified list. + * This is a two element <code>String</code> + * array indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>. + * + * @param eras The new list of era disply strings. + */ + public void setEras (String[] value) + { + eras = value; + } + + /** + * This method sets the list of characters used to specific date/time + * formatting strings. + * This is an 18 character string that contains the characters + * that are used in creating the date formatting strings in + * <code>SimpleDateFormat</code>. The following are the character + * positions in the string and which format character they correspond + * to (the character in parentheses is the default value in the US English + * locale): + * <p> + * <ul> + * <li>0 - era (G)</li> + * <li>1 - year (y)</li> + * <li>2 - month (M)</li> + * <li>3 - day of month (d)</li> + * <li>4 - hour out of 12, from 1-12 (h)</li> + * <li>5 - hour out of 24, from 0-23 (H)</li> + * <li>6 - minute (m)</li> + * <li>7 - second (s)</li> + * <li>8 - millisecond (S)</li> + * <li>9 - date of week (E)</li> + * <li>10 - date of year (D)</li> + * <li>11 - day of week in month, eg. "4th Thur in Nov" (F)</li> + * <li>12 - week in year (w)</li> + * <li>13 - week in month (W)</li> + * <li>14 - am/pm (a)</li> + * <li>15 - hour out of 24, from 1-24 (k)</li> + * <li>16 - hour out of 12, from 0-11 (K)</li> + * <li>17 - time zone (z)</li> + * </ul> + * + * @param localPatternChars The new format patter characters + */ + public void setLocalPatternChars (String value) + { + localPatternChars = value; + } + + /** + * This method sets the list of strings used to display month names. + * This is a thirteen element + * string array indexed by <code>Calendar.JANUARY</code> through + * <code>Calendar.UNDECEMBER</code>. Note that there are thirteen + * elements because some calendars have thriteen months. + * + * @param months The list of month display strings. + */ + public void setMonths (String[] value) + { + months = value; + } + + /** + * This method sets the list of strings used to display abbreviated month + * names. + * This is a thirteen element + * <code>String</code> array indexed by <code>Calendar.JANUARY</code> + * through <code>Calendar.UNDECEMBER</code>. Note that there are thirteen + * elements because some calendars have thirteen months. + * + * @param shortMonths The new list of abbreviated month display strings. + */ + public void setShortMonths (String[] value) + { + shortMonths = value; + } + + /** + * This method sets the list of strings used to display abbreviated + * weekday names. + * This is an eight element + * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> + * through <code>Calendar.SATURDAY</code>. Note that the first element + * of this array is ignored. + * + * @param shortWeekdays This list of abbreviated weekday display strings. + */ + public void setShortWeekdays (String[] value) + { + shortWeekdays = value; + } + + /** + * This method sets the list of strings used to display weekday names. + * This is an eight element + * <code>String</code> array indexed by <code>Calendar.SUNDAY</code> + * through <code>Calendar.SATURDAY</code>. Note that the first element + * of this array is ignored. + * + * @param weekdays This list of weekday display strings. + */ + public void setWeekdays (String[] value) + { + weekdays = value; + } + + /** + * This method sets the list of display strings for time zones. + * This is a two dimensional <code>String</code> array where each row in + * the array contains five values: + * <P> + * <ul> + * <li>0 - The non-localized time zone id string.</li> + * <li>1 - The long name of the time zone (standard time).</li> + * <li>2 - The short name of the time zone (standard time).</li> + * <li>3 - The long name of the time zone (daylight savings time).</li> + * <li>4 - the short name of the time zone (daylight savings time).</li> + * </ul> + * + * @return The list of time zone display strings. + */ + public void setZoneStrings (String[][] value) + { + zoneStrings = value; + } + + /* Does a "deep" equality test - recurses into arrays. */ + private static boolean equals (Object x, Object y) + { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (! (x instanceof Object[]) || ! (y instanceof Object[])) + return x.equals(y); + Object[] xa = (Object[]) x; + Object[] ya = (Object[]) y; + if (xa.length != ya.length) + return false; + for (int i = xa.length; --i >= 0; ) + { + if (! equals(xa[i], ya[i])) + return false; + } + return true; + } + + private static int hashCode (Object x) + { + if (x == null) + return 0; + if (! (x instanceof Object[])) + return x.hashCode(); + Object[] xa = (Object[]) x; + int hash = 0; + for (int i = 0; i < xa.length; i++) + hash = 37 * hashCode(xa[i]); + return hash; + } + + /** + * This method tests a specified object for equality against this object. + * This will be true if and only if the specified object: + * <p> + * <ul> + * <li> Is not <code>null</code>.</li> + * <li> Is an instance of <code>DateFormatSymbols</code>.</li> + * <li> Contains identical formatting symbols to this object.</li> + * </ul> + * + * @param obj The <code>Object</code> to test for equality against. + * + * @return <code>true</code> if the specified object is equal to this one, + * <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof DateFormatSymbols)) + return false; + DateFormatSymbols other = (DateFormatSymbols) obj; + return (equals(ampms, other.ampms) + && equals(eras, other.eras) + && equals(localPatternChars, other.localPatternChars) + && equals(months, other.months) + && equals(shortMonths, other.shortMonths) + && equals(shortWeekdays, other.shortWeekdays) + && equals(weekdays, other.weekdays) + && equals(zoneStrings, other.zoneStrings)); + } + + /** + * Returns a new copy of this object. + * + * @param A copy of this object + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + return (hashCode(ampms) + ^ hashCode(eras) + ^ hashCode(localPatternChars) + ^ hashCode(months) + ^ hashCode(shortMonths) + ^ hashCode(shortWeekdays) + ^ hashCode(weekdays) + ^ hashCode(zoneStrings)); + } +} diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java new file mode 100644 index 00000000000..6dadb0ce333 --- /dev/null +++ b/libjava/classpath/java/text/DecimalFormat.java @@ -0,0 +1,1435 @@ +/* DecimalFormat.java -- Formats and parses numbers + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text; + +import gnu.java.text.AttributedFormatBuffer; +import gnu.java.text.FormatBuffer; +import gnu.java.text.FormatCharacterIterator; +import gnu.java.text.StringFormatBuffer; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Currency; +import java.util.HashMap; +import java.util.Locale; + +/** + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @date March 4, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2. + * Note however that the docs are very unclear about how format parsing + * should work. No doubt there are problems here. + */ +public class DecimalFormat extends NumberFormat +{ + // This is a helper for applyPatternWithSymbols. It reads a prefix + // or a suffix. It can cause some side-effects. + private int scanFix (String pattern, int index, FormatBuffer buf, + String patChars, DecimalFormatSymbols syms, + boolean is_suffix) + { + int len = pattern.length(); + boolean quoteStarted = false; + buf.clear(); + + boolean multiplierSet = false; + while (index < len) + { + char c = pattern.charAt(index); + + if (quoteStarted) + { + if (c == '\'') + quoteStarted = false; + else + buf.append(c); + index++; + continue; + } + + if (c == '\'' && index + 1 < len + && pattern.charAt(index + 1) == '\'') + { + buf.append(c); + index++; + } + else if (c == '\'') + { + quoteStarted = true; + } + else if (c == '\u00a4') + { + /* Currency interpreted later */ + buf.append(c); + } + else if (c == syms.getPercent()) + { + if (multiplierSet) + throw new IllegalArgumentException ("multiplier already set " + + "- index: " + index); + multiplierSet = true; + multiplier = 100; + buf.append(c, NumberFormat.Field.PERCENT); + } + else if (c == syms.getPerMill()) + { + if (multiplierSet) + throw new IllegalArgumentException ("multiplier already set " + + "- index: " + index); + multiplierSet = true; + multiplier = 1000; + buf.append(c, NumberFormat.Field.PERMILLE); + } + else if (patChars.indexOf(c) != -1) + { + // This is a pattern character. + break; + } + else + { + buf.append(c); + } + index++; + } + + if (quoteStarted) + throw new IllegalArgumentException ("pattern is lacking a closing quote"); + + return index; + } + + // A helper which reads a number format. + private int scanFormat (String pattern, int index, String patChars, + DecimalFormatSymbols syms, boolean is_positive) + { + int max = pattern.length(); + + int countSinceGroup = 0; + int zeroCount = 0; + boolean saw_group = false; + + // + // Scan integer part. + // + while (index < max) + { + char c = pattern.charAt(index); + + if (c == syms.getDigit()) + { + if (zeroCount > 0) + throw new IllegalArgumentException ("digit mark following " + + "zero - index: " + index); + ++countSinceGroup; + } + else if (c == syms.getZeroDigit()) + { + ++zeroCount; + ++countSinceGroup; + } + else if (c == syms.getGroupingSeparator()) + { + countSinceGroup = 0; + saw_group = true; + } + else + break; + + ++index; + } + + // We can only side-effect when parsing the positive format. + if (is_positive) + { + groupingUsed = saw_group; + groupingSize = (byte) countSinceGroup; + minimumIntegerDigits = zeroCount; + } + + // Early termination. + if (index == max || pattern.charAt(index) == syms.getGroupingSeparator()) + { + if (is_positive) + decimalSeparatorAlwaysShown = false; + return index; + } + + if (pattern.charAt(index) == syms.getDecimalSeparator()) + { + ++index; + + // + // Scan fractional part. + // + int hashCount = 0; + zeroCount = 0; + while (index < max) + { + char c = pattern.charAt(index); + if (c == syms.getZeroDigit()) + { + if (hashCount > 0) + throw new IllegalArgumentException ("zero mark " + + "following digit - index: " + index); + ++zeroCount; + } + else if (c == syms.getDigit()) + { + ++hashCount; + } + else if (c != syms.getExponential() + && c != syms.getPatternSeparator() + && c != syms.getPercent() + && c != syms.getPerMill() + && patChars.indexOf(c) != -1) + throw new IllegalArgumentException ("unexpected special " + + "character - index: " + index); + else + break; + + ++index; + } + + if (is_positive) + { + maximumFractionDigits = hashCount + zeroCount; + minimumFractionDigits = zeroCount; + } + + if (index == max) + return index; + } + + if (pattern.charAt(index) == syms.getExponential()) + { + // + // Scan exponential format. + // + zeroCount = 0; + ++index; + while (index < max) + { + char c = pattern.charAt(index); + if (c == syms.getZeroDigit()) + ++zeroCount; + else if (c == syms.getDigit()) + { + if (zeroCount > 0) + throw new + IllegalArgumentException ("digit mark following zero " + + "in exponent - index: " + + index); + } + else if (patChars.indexOf(c) != -1) + throw new IllegalArgumentException ("unexpected special " + + "character - index: " + + index); + else + break; + + ++index; + } + + if (is_positive) + { + useExponentialNotation = true; + minExponentDigits = (byte) zeroCount; + } + + maximumIntegerDigits = groupingSize; + groupingSize = 0; + if (maximumIntegerDigits > minimumIntegerDigits && maximumIntegerDigits > 0) + { + minimumIntegerDigits = 1; + exponentRound = maximumIntegerDigits; + } + else + exponentRound = 1; + } + + return index; + } + + // This helper function creates a string consisting of all the + // characters which can appear in a pattern and must be quoted. + private String patternChars (DecimalFormatSymbols syms) + { + StringBuffer buf = new StringBuffer (); + buf.append(syms.getDecimalSeparator()); + buf.append(syms.getDigit()); + buf.append(syms.getExponential()); + buf.append(syms.getGroupingSeparator()); + // Adding this one causes pattern application to fail. + // Of course, omitting is causes toPattern to fail. + // ... but we already have bugs there. FIXME. + // buf.append(syms.getMinusSign()); + buf.append(syms.getPatternSeparator()); + buf.append(syms.getPercent()); + buf.append(syms.getPerMill()); + buf.append(syms.getZeroDigit()); + buf.append('\u00a4'); + return buf.toString(); + } + + private void applyPatternWithSymbols(String pattern, DecimalFormatSymbols syms) + { + // Initialize to the state the parser expects. + negativePrefix = ""; + negativeSuffix = ""; + positivePrefix = ""; + positiveSuffix = ""; + decimalSeparatorAlwaysShown = false; + groupingSize = 0; + minExponentDigits = 0; + multiplier = 1; + useExponentialNotation = false; + groupingUsed = false; + maximumFractionDigits = 0; + maximumIntegerDigits = MAXIMUM_INTEGER_DIGITS; + minimumFractionDigits = 0; + minimumIntegerDigits = 1; + + AttributedFormatBuffer buf = new AttributedFormatBuffer (); + String patChars = patternChars (syms); + + int max = pattern.length(); + int index = scanFix (pattern, 0, buf, patChars, syms, false); + buf.sync(); + positivePrefix = buf.getBuffer().toString(); + positivePrefixRanges = buf.getRanges(); + positivePrefixAttrs = buf.getAttributes(); + + index = scanFormat (pattern, index, patChars, syms, true); + + index = scanFix (pattern, index, buf, patChars, syms, true); + buf.sync(); + positiveSuffix = buf.getBuffer().toString(); + positiveSuffixRanges = buf.getRanges(); + positiveSuffixAttrs = buf.getAttributes(); + + if (index == pattern.length()) + { + // No negative info. + negativePrefix = null; + negativeSuffix = null; + } + else + { + if (pattern.charAt(index) != syms.getPatternSeparator()) + throw new IllegalArgumentException ("separator character " + + "expected - index: " + index); + + index = scanFix (pattern, index + 1, buf, patChars, syms, false); + buf.sync(); + negativePrefix = buf.getBuffer().toString(); + negativePrefixRanges = buf.getRanges(); + negativePrefixAttrs = buf.getAttributes(); + + // We parse the negative format for errors but we don't let + // it side-effect this object. + index = scanFormat (pattern, index, patChars, syms, false); + + index = scanFix (pattern, index, buf, patChars, syms, true); + buf.sync(); + negativeSuffix = buf.getBuffer().toString(); + negativeSuffixRanges = buf.getRanges(); + negativeSuffixAttrs = buf.getAttributes(); + + if (index != pattern.length()) + throw new IllegalArgumentException ("end of pattern expected " + + "- index: " + index); + } + } + + public void applyLocalizedPattern (String pattern) + { + // JCL p. 638 claims this throws a ParseException but p. 629 + // contradicts this. Empirical tests with patterns of "0,###.0" + // and "#.#.#" corroborate the p. 629 statement that an + // IllegalArgumentException is thrown. + applyPatternWithSymbols (pattern, symbols); + } + + public void applyPattern (String pattern) + { + // JCL p. 638 claims this throws a ParseException but p. 629 + // contradicts this. Empirical tests with patterns of "0,###.0" + // and "#.#.#" corroborate the p. 629 statement that an + // IllegalArgumentException is thrown. + applyPatternWithSymbols (pattern, nonLocalizedSymbols); + } + + public Object clone () + { + DecimalFormat c = (DecimalFormat) super.clone (); + c.symbols = (DecimalFormatSymbols) symbols.clone (); + return c; + } + + /** + * Constructs a <code>DecimalFormat</code> which uses the default + * pattern and symbols. + */ + public DecimalFormat () + { + this ("#,##0.###"); + } + + /** + * Constructs a <code>DecimalFormat</code> which uses the given + * pattern and the default symbols for formatting and parsing. + * + * @param pattern the non-localized pattern to use. + * @throws NullPointerException if any argument is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public DecimalFormat (String pattern) + { + this (pattern, new DecimalFormatSymbols ()); + } + + /** + * Constructs a <code>DecimalFormat</code> using the given pattern + * and formatting symbols. This construction method is used to give + * complete control over the formatting process. + * + * @param pattern the non-localized pattern to use. + * @param symbols the set of symbols used for parsing and formatting. + * @throws NullPointerException if any argument is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public DecimalFormat(String pattern, DecimalFormatSymbols symbols) + { + this.symbols = (DecimalFormatSymbols) symbols.clone(); + applyPattern(pattern); + } + + private boolean equals(String s1, String s2) + { + if (s1 == null || s2 == null) + return s1 == s2; + return s1.equals(s2); + } + + /** + * Tests this instance for equality with an arbitrary object. This method + * returns <code>true</code> if: + * <ul> + * <li><code>obj</code> is not <code>null</code>;</li> + * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li> + * <li>this instance and <code>obj</code> have the same attributes;</li> + * </ul> + * + * @param obj the object (<code>null</code> permitted). + * + * @return A boolean. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof DecimalFormat)) + return false; + DecimalFormat dup = (DecimalFormat) obj; + return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown + && groupingUsed == dup.groupingUsed + && groupingSize == dup.groupingSize + && multiplier == dup.multiplier + && useExponentialNotation == dup.useExponentialNotation + && minExponentDigits == dup.minExponentDigits + && minimumIntegerDigits == dup.minimumIntegerDigits + && maximumIntegerDigits == dup.maximumIntegerDigits + && minimumFractionDigits == dup.minimumFractionDigits + && maximumFractionDigits == dup.maximumFractionDigits + && equals(negativePrefix, dup.negativePrefix) + && equals(negativeSuffix, dup.negativeSuffix) + && equals(positivePrefix, dup.positivePrefix) + && equals(positiveSuffix, dup.positiveSuffix) + && symbols.equals(dup.symbols)); + } + + private void formatInternal (double number, FormatBuffer dest, + FieldPosition fieldPos) + { + // A very special case. + if (Double.isNaN(number)) + { + dest.append(symbols.getNaN()); + if (fieldPos != null && + (fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + int index = dest.length(); + fieldPos.setBeginIndex(index - symbols.getNaN().length()); + fieldPos.setEndIndex(index); + } + return; + } + + boolean is_neg = number < 0; + if (is_neg) + { + if (negativePrefix != null) + { + dest.append(substituteCurrency(negativePrefix, number), + negativePrefixRanges, negativePrefixAttrs); + } + else + { + dest.append(symbols.getMinusSign(), NumberFormat.Field.SIGN); + dest.append(substituteCurrency(positivePrefix, number), + positivePrefixRanges, positivePrefixAttrs); + } + number = - number; + } + else + { + dest.append(substituteCurrency(positivePrefix, number), + positivePrefixRanges, positivePrefixAttrs); + } + int integerBeginIndex = dest.length(); + int integerEndIndex = 0; + int zeroStart = symbols.getZeroDigit() - '0'; + + if (Double.isInfinite (number)) + { + dest.append(symbols.getInfinity()); + integerEndIndex = dest.length(); + } + else + { + number *= multiplier; + + // Compute exponent. + long exponent = 0; + double baseNumber; + if (useExponentialNotation) + { + exponent = (long) Math.floor (Math.log(number) / Math.log(10)); + exponent = exponent - (exponent % exponentRound); + if (minimumIntegerDigits > 0) + exponent -= minimumIntegerDigits - 1; + baseNumber = (number / Math.pow(10.0, exponent)); + } + else + baseNumber = number; + + // Round to the correct number of digits. + baseNumber += 5 * Math.pow(10.0, - maximumFractionDigits - 1); + + int index = dest.length(); + //double intPart = Math.floor(baseNumber); + String intPart = Long.toString((long)Math.floor(baseNumber)); + int count, groupPosition = intPart.length(); + + dest.setDefaultAttribute(NumberFormat.Field.INTEGER); + + for (count = 0; count < minimumIntegerDigits-intPart.length(); count++) + dest.append(symbols.getZeroDigit()); + + for (count = 0; + count < maximumIntegerDigits && count < intPart.length(); + count++) + { + int dig = intPart.charAt(count); + + // Append group separator if required. + if (groupingUsed && count > 0 && groupingSize != 0 && groupPosition % groupingSize == 0) + { + dest.append(symbols.getGroupingSeparator(), NumberFormat.Field.GROUPING_SEPARATOR); + dest.setDefaultAttribute(NumberFormat.Field.INTEGER); + } + dest.append((char) (zeroStart + dig)); + + groupPosition--; + } + dest.setDefaultAttribute(null); + + integerEndIndex = dest.length(); + + int decimal_index = integerEndIndex; + int consecutive_zeros = 0; + int total_digits = 0; + + int localMaximumFractionDigits = maximumFractionDigits; + + if (useExponentialNotation) + localMaximumFractionDigits += minimumIntegerDigits - count; + + // Strip integer part from NUMBER. + double fracPart = baseNumber - Math.floor(baseNumber); + + if ( ((fracPart != 0 || minimumFractionDigits > 0) && localMaximumFractionDigits > 0) + || decimalSeparatorAlwaysShown) + { + dest.append (symbols.getDecimalSeparator(), NumberFormat.Field.DECIMAL_SEPARATOR); + } + + int fraction_begin = dest.length(); + dest.setDefaultAttribute(NumberFormat.Field.FRACTION); + for (count = 0; + count < localMaximumFractionDigits + && (fracPart != 0 || count < minimumFractionDigits); + ++count) + { + ++total_digits; + fracPart *= 10; + long dig = (long) fracPart; + if (dig == 0) + ++consecutive_zeros; + else + consecutive_zeros = 0; + dest.append((char) (symbols.getZeroDigit() + dig)); + + // Strip integer part from FRACPART. + fracPart = fracPart - Math.floor (fracPart); + } + + // Strip extraneous trailing `0's. We can't always detect + // these in the loop. + int extra_zeros = Math.min (consecutive_zeros, + total_digits - minimumFractionDigits); + if (extra_zeros > 0) + { + dest.cutTail(extra_zeros); + total_digits -= extra_zeros; + if (total_digits == 0 && !decimalSeparatorAlwaysShown) + dest.cutTail(1); + } + + if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD) + { + fieldPos.setBeginIndex(fraction_begin); + fieldPos.setEndIndex(dest.length()); + } + + // Finally, print the exponent. + if (useExponentialNotation) + { + dest.append(symbols.getExponential(), NumberFormat.Field.EXPONENT_SYMBOL); + if (exponent < 0) + { + dest.append (symbols.getMinusSign (), NumberFormat.Field.EXPONENT_SIGN); + exponent = - exponent; + } + index = dest.length(); + dest.setDefaultAttribute(NumberFormat.Field.EXPONENT); + String exponentString = Long.toString ((long) exponent); + + for (count = 0; count < minExponentDigits-exponentString.length(); + count++) + dest.append((char) symbols.getZeroDigit()); + + for (count = 0; + count < exponentString.length(); + ++count) + { + int dig = exponentString.charAt(count); + dest.append((char) (zeroStart + dig)); + } + } + } + + if (fieldPos != null && + (fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + fieldPos.setBeginIndex(integerBeginIndex); + fieldPos.setEndIndex(integerEndIndex); + } + + if (is_neg && negativeSuffix != null) + { + dest.append(substituteCurrency(negativeSuffix, number), + negativeSuffixRanges, negativeSuffixAttrs); + } + else + { + dest.append(substituteCurrency(positiveSuffix, number), + positiveSuffixRanges, positiveSuffixAttrs); + } + } + + public StringBuffer format (double number, StringBuffer dest, + FieldPosition fieldPos) + { + formatInternal (number, new StringFormatBuffer(dest), fieldPos); + return dest; + } + + public AttributedCharacterIterator formatToCharacterIterator (Object value) + { + AttributedFormatBuffer sbuf = new AttributedFormatBuffer(); + + if (value instanceof Number) + formatInternal(((Number) value).doubleValue(), sbuf, null); + else + throw new IllegalArgumentException + ("Cannot format given Object as a Number"); + + sbuf.sync(); + return new FormatCharacterIterator(sbuf.getBuffer().toString(), + sbuf.getRanges(), + sbuf.getAttributes()); + } + + public StringBuffer format (long number, StringBuffer dest, + FieldPosition fieldPos) + { + // If using exponential notation, we just format as a double. + if (useExponentialNotation) + return format ((double) number, dest, fieldPos); + + boolean is_neg = number < 0; + if (is_neg) + { + if (negativePrefix != null) + dest.append(substituteCurrency(negativePrefix, number)); + else + { + dest.append(symbols.getMinusSign()); + dest.append(substituteCurrency(positivePrefix, number)); + } + number = - number; + } + else + dest.append(substituteCurrency(positivePrefix, number)); + + int integerBeginIndex = dest.length(); + int index = dest.length(); + int count = 0; + + /* Handle percentages, etc. */ + number *= multiplier; + while (count < maximumIntegerDigits + && (number > 0 || count < minimumIntegerDigits)) + { + long dig = number % 10; + number /= 10; + // NUMBER and DIG will be less than 0 if the original number + // was the most negative long. + if (dig < 0) + { + dig = - dig; + number = - number; + } + + // Append group separator if required. + if (groupingUsed && count > 0 && groupingSize != 0 && count % groupingSize == 0) + dest.insert(index, symbols.getGroupingSeparator()); + + dest.insert(index, (char) (symbols.getZeroDigit() + dig)); + + ++count; + } + + if (fieldPos != null && fieldPos.getField() == INTEGER_FIELD) + { + fieldPos.setBeginIndex(integerBeginIndex); + fieldPos.setEndIndex(dest.length()); + } + + if (decimalSeparatorAlwaysShown || minimumFractionDigits > 0) + { + dest.append(symbols.getDecimalSeparator()); + if (fieldPos != null && fieldPos.getField() == FRACTION_FIELD) + { + fieldPos.setBeginIndex(dest.length()); + fieldPos.setEndIndex(dest.length() + minimumFractionDigits); + } + } + + for (count = 0; count < minimumFractionDigits; ++count) + dest.append(symbols.getZeroDigit()); + + dest.append((is_neg && negativeSuffix != null) + ? substituteCurrency(negativeSuffix, number) + : substituteCurrency(positiveSuffix, number)); + return dest; + } + + /** + * Returns the currency corresponding to the currency symbol stored + * in the instance of <code>DecimalFormatSymbols</code> used by this + * <code>DecimalFormat</code>. + * + * @return A new instance of <code>Currency</code> if + * the currency code matches a known one, null otherwise. + */ + public Currency getCurrency() + { + return symbols.getCurrency(); + } + + /** + * Returns a copy of the symbols used by this instance. + * + * @return A copy of the symbols. + */ + public DecimalFormatSymbols getDecimalFormatSymbols() + { + return (DecimalFormatSymbols) symbols.clone(); + } + + public int getGroupingSize () + { + return groupingSize; + } + + public int getMultiplier () + { + return multiplier; + } + + public String getNegativePrefix () + { + return negativePrefix; + } + + public String getNegativeSuffix () + { + return negativeSuffix; + } + + public String getPositivePrefix () + { + return positivePrefix; + } + + public String getPositiveSuffix () + { + return positiveSuffix; + } + + /** + * Returns a hash code for this object. + * + * @return A hash code. + */ + public int hashCode() + { + return toPattern().hashCode(); + } + + public boolean isDecimalSeparatorAlwaysShown () + { + return decimalSeparatorAlwaysShown; + } + + public Number parse (String str, ParsePosition pos) + { + /* + * Our strategy is simple: copy the text into separate buffers: one for the int part, + * one for the fraction part and for the exponential part. + * We translate or omit locale-specific information. + * If exponential is sufficiently big we merge the fraction and int part and + * remove the '.' and then we use Long to convert the number. In the other + * case, we use Double to convert the full number. + */ + + boolean is_neg = false; + int index = pos.getIndex(); + StringBuffer int_buf = new StringBuffer (); + + // We have to check both prefixes, because one might be empty. We + // want to pick the longest prefix that matches. + boolean got_pos = str.startsWith(positivePrefix, index); + String np = (negativePrefix != null + ? negativePrefix + : positivePrefix + symbols.getMinusSign()); + boolean got_neg = str.startsWith(np, index); + + if (got_pos && got_neg) + { + // By checking this way, we preserve ambiguity in the case + // where the negative format differs only in suffix. We + // check this again later. + if (np.length() > positivePrefix.length()) + { + is_neg = true; + index += np.length(); + } + else + index += positivePrefix.length(); + } + else if (got_neg) + { + is_neg = true; + index += np.length(); + } + else if (got_pos) + index += positivePrefix.length(); + else + { + pos.setErrorIndex (index); + return null; + } + + // FIXME: handle Inf and NaN. + + // FIXME: do we have to respect minimum digits? + // What about multiplier? + + StringBuffer buf = int_buf; + StringBuffer frac_buf = null; + StringBuffer exp_buf = null; + int start_index = index; + int max = str.length(); + int exp_index = -1; + int last = index + maximumIntegerDigits; + + if (maximumFractionDigits > 0) + last += maximumFractionDigits + 1; + + if (useExponentialNotation) + last += minExponentDigits + 1; + + if (last > 0 && max > last) + max = last; + + char zero = symbols.getZeroDigit(); + int last_group = -1; + boolean int_part = true; + boolean exp_part = false; + for (; index < max; ++index) + { + char c = str.charAt(index); + + // FIXME: what about grouping size? + if (groupingUsed && c == symbols.getGroupingSeparator()) + { + if (last_group != -1 + && groupingSize != 0 + && (index - last_group) % groupingSize != 0) + { + pos.setErrorIndex(index); + return null; + } + last_group = index+1; + } + else if (c >= zero && c <= zero + 9) + { + buf.append((char) (c - zero + '0')); + } + else if (parseIntegerOnly) + break; + else if (c == symbols.getDecimalSeparator()) + { + if (last_group != -1 + && groupingSize != 0 + && (index - last_group) % groupingSize != 0) + { + pos.setErrorIndex(index); + return null; + } + buf = frac_buf = new StringBuffer(); + frac_buf.append('.'); + int_part = false; + } + else if (c == symbols.getExponential()) + { + buf = exp_buf = new StringBuffer(); + int_part = false; + exp_part = true; + exp_index = index+1; + } + else if (exp_part + && (c == '+' || c == '-' || c == symbols.getMinusSign())) + { + // For exponential notation. + buf.append(c); + } + else + break; + } + + if (index == start_index) + { + // Didn't see any digits. + pos.setErrorIndex(index); + return null; + } + + // Check the suffix. We must do this before converting the + // buffer to a number to handle the case of a number which is + // the most negative Long. + boolean got_pos_suf = str.startsWith(positiveSuffix, index); + String ns = (negativePrefix == null ? positiveSuffix : negativeSuffix); + boolean got_neg_suf = str.startsWith(ns, index); + if (is_neg) + { + if (! got_neg_suf) + { + pos.setErrorIndex(index); + return null; + } + } + else if (got_pos && got_neg && got_neg_suf) + { + is_neg = true; + } + else if (got_pos != got_pos_suf && got_neg != got_neg_suf) + { + pos.setErrorIndex(index); + return null; + } + else if (! got_pos_suf) + { + pos.setErrorIndex(index); + return null; + } + + String suffix = is_neg ? ns : positiveSuffix; + long parsedMultiplier = 1; + boolean use_long; + + if (is_neg) + int_buf.insert(0, '-'); + + // Now handle the exponential part if there is one. + if (exp_buf != null) + { + int exponent_value; + + try + { + exponent_value = Integer.parseInt(exp_buf.toString()); + } + catch (NumberFormatException x1) + { + pos.setErrorIndex(exp_index); + return null; + } + + if (frac_buf == null) + { + // We only have to add some zeros to the int part. + // Build a multiplier. + for (int i = 0; i < exponent_value; i++) + int_buf.append('0'); + + use_long = true; + } + else + { + boolean long_sufficient; + + if (exponent_value < frac_buf.length()-1) + { + int lastNonNull = -1; + /* We have to check the fraction buffer: it may only be full of '0' + * or be sufficiently filled with it to convert the number into Long. + */ + for (int i = 1; i < frac_buf.length(); i++) + if (frac_buf.charAt(i) != '0') + lastNonNull = i; + + long_sufficient = (lastNonNull < 0 || lastNonNull <= exponent_value); + } + else + long_sufficient = true; + + if (long_sufficient) + { + for (int i = 1; i < frac_buf.length() && i < exponent_value; i++) + int_buf.append(frac_buf.charAt(i)); + for (int i = frac_buf.length()-1; i < exponent_value; i++) + int_buf.append('0'); + use_long = true; + } + else + { + /* + * A long type is not sufficient, we build the full buffer to + * be parsed by Double. + */ + int_buf.append(frac_buf); + int_buf.append('E'); + int_buf.append(exp_buf); + use_long = false; + } + } + } + else + { + if (frac_buf != null) + { + /* Check whether the fraction buffer contains only '0' */ + int i; + for (i = 1; i < frac_buf.length(); i++) + if (frac_buf.charAt(i) != '0') + break; + + if (i != frac_buf.length()) + { + use_long = false; + int_buf.append(frac_buf); + } + else + use_long = true; + } + else + use_long = true; + } + + String t = int_buf.toString(); + Number result = null; + if (use_long) + { + try + { + result = new Long (t); + } + catch (NumberFormatException x1) + { + } + } + else + { + try + { + result = new Double (t); + } + catch (NumberFormatException x2) + { + } + } + if (result == null) + { + pos.setErrorIndex(index); + return null; + } + + pos.setIndex(index + suffix.length()); + + return result; + } + + /** + * Sets the <code>Currency</code> on the + * <code>DecimalFormatSymbols</code> used, which also sets the + * currency symbols on those symbols. + */ + public void setCurrency(Currency currency) + { + symbols.setCurrency(currency); + } + + /** + * Sets the symbols used by this instance. This method makes a copy of + * the supplied symbols. + * + * @param newSymbols the symbols (<code>null</code> not permitted). + */ + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) + { + symbols = (DecimalFormatSymbols) newSymbols.clone(); + } + + public void setDecimalSeparatorAlwaysShown (boolean newValue) + { + decimalSeparatorAlwaysShown = newValue; + } + + public void setGroupingSize (int groupSize) + { + groupingSize = (byte) groupSize; + } + + public void setMaximumFractionDigits (int newValue) + { + super.setMaximumFractionDigits(Math.min(newValue, 340)); + } + + public void setMaximumIntegerDigits (int newValue) + { + super.setMaximumIntegerDigits(Math.min(newValue, 309)); + } + + public void setMinimumFractionDigits (int newValue) + { + super.setMinimumFractionDigits(Math.min(newValue, 340)); + } + + public void setMinimumIntegerDigits (int newValue) + { + super.setMinimumIntegerDigits(Math.min(newValue, 309)); + } + + public void setMultiplier (int newValue) + { + multiplier = newValue; + } + + public void setNegativePrefix (String newValue) + { + negativePrefix = newValue; + } + + public void setNegativeSuffix (String newValue) + { + negativeSuffix = newValue; + } + + public void setPositivePrefix (String newValue) + { + positivePrefix = newValue; + } + + public void setPositiveSuffix (String newValue) + { + positiveSuffix = newValue; + } + + private void quoteFix(StringBuffer buf, String text, String patChars) + { + int len = text.length(); + for (int index = 0; index < len; ++index) + { + char c = text.charAt(index); + if (patChars.indexOf(c) != -1) + { + buf.append('\''); + buf.append(c); + buf.append('\''); + } + else + buf.append(c); + } + } + + private String computePattern(DecimalFormatSymbols syms) + { + StringBuffer mainPattern = new StringBuffer (); + // We have to at least emit a zero for the minimum number of + // digits. Past that we need hash marks up to the grouping + // separator (and one beyond). + int total_digits = Math.max(minimumIntegerDigits, + groupingUsed ? groupingSize + 1: groupingSize); + for (int i = 0; i < total_digits - minimumIntegerDigits; ++i) + mainPattern.append(syms.getDigit()); + for (int i = total_digits - minimumIntegerDigits; i < total_digits; ++i) + mainPattern.append(syms.getZeroDigit()); + // Inserting the gropuing operator afterwards is easier. + if (groupingUsed) + mainPattern.insert(mainPattern.length() - groupingSize, + syms.getGroupingSeparator()); + // See if we need decimal info. + if (minimumFractionDigits > 0 || maximumFractionDigits > 0 + || decimalSeparatorAlwaysShown) + mainPattern.append(syms.getDecimalSeparator()); + for (int i = 0; i < minimumFractionDigits; ++i) + mainPattern.append(syms.getZeroDigit()); + for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i) + mainPattern.append(syms.getDigit()); + if (useExponentialNotation) + { + mainPattern.append(syms.getExponential()); + for (int i = 0; i < minExponentDigits; ++i) + mainPattern.append(syms.getZeroDigit()); + if (minExponentDigits == 0) + mainPattern.append(syms.getDigit()); + } + + String main = mainPattern.toString(); + String patChars = patternChars (syms); + mainPattern.setLength(0); + + quoteFix (mainPattern, positivePrefix, patChars); + mainPattern.append(main); + quoteFix (mainPattern, positiveSuffix, patChars); + + if (negativePrefix != null) + { + quoteFix (mainPattern, negativePrefix, patChars); + mainPattern.append(main); + quoteFix (mainPattern, negativeSuffix, patChars); + } + + return mainPattern.toString(); + } + + public String toLocalizedPattern () + { + return computePattern (symbols); + } + + public String toPattern () + { + return computePattern (nonLocalizedSymbols); + } + + private static final int MAXIMUM_INTEGER_DIGITS = 309; + + // These names are fixed by the serialization spec. + private boolean decimalSeparatorAlwaysShown; + private byte groupingSize; + private byte minExponentDigits; + private int exponentRound; + private int multiplier; + private String negativePrefix; + private String negativeSuffix; + private String positivePrefix; + private String positiveSuffix; + private int[] negativePrefixRanges, positivePrefixRanges; + private HashMap[] negativePrefixAttrs, positivePrefixAttrs; + private int[] negativeSuffixRanges, positiveSuffixRanges; + private HashMap[] negativeSuffixAttrs, positiveSuffixAttrs; + private int serialVersionOnStream = 1; + private DecimalFormatSymbols symbols; + private boolean useExponentialNotation; + private static final long serialVersionUID = 864413376551465018L; + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + useExponentialNotation = false; + serialVersionOnStream = 1; + } + } + + // The locale-independent pattern symbols happen to be the same as + // the US symbols. + private static final DecimalFormatSymbols nonLocalizedSymbols + = new DecimalFormatSymbols (Locale.US); + + /** + * <p> + * Substitutes the currency symbol into the given string, + * based on the value used. Currency symbols can either + * be a simple series of characters (e.g. '$'), which are + * simply used as is, or they can be of a more complex + * form: + * </p> + * <p> + * (lower bound)|(mid value)|(upper bound) + * </p> + * <p> + * where each bound has the syntax '(value)(# or <)(symbol)', + * to indicate the bounding value and the symbol used. + * </p> + * <p> + * The currency symbol replaces the currency specifier, '\u00a4', + * an unlocalised character, which thus is used as such in all formats. + * If this symbol occurs twice, the international currency code is used + * instead. + * </p> + * + * @param string The string containing the currency specifier, '\u00a4'. + * @param number the number being formatted. + * @return a string formatted for the correct currency. + */ + private String substituteCurrency(String string, double number) + { + int index; + int length; + char currentChar; + StringBuffer buf; + + index = 0; + length = string.length(); + buf = new StringBuffer(); + + while (index < length) + { + currentChar = string.charAt(index); + if (string.charAt(index) == '\u00a4') + { + if ((index + 1) < length && string.charAt(index + 1) == '\u00a4') + { + buf.append(symbols.getInternationalCurrencySymbol()); + index += 2; + } + else + { + String symbol; + + symbol = symbols.getCurrencySymbol(); + if (symbol.startsWith("=")) + { + String[] bounds; + int[] boundValues; + String[] boundSymbols; + + bounds = symbol.substring(1).split("\\|"); + boundValues = new int[3]; + boundSymbols = new String[3]; + for (int a = 0; a < 3; ++a) + { + String[] bound; + + bound = bounds[a].split("[#<]"); + boundValues[a] = Integer.parseInt(bound[0]); + boundSymbols[a] = bound[1]; + } + if (number <= boundValues[0]) + { + buf.append(boundSymbols[0]); + } + else if (number >= boundValues[2]) + { + buf.append(boundSymbols[2]); + } + else + { + buf.append(boundSymbols[1]); + } + ++index; + } + else + { + buf.append(symbol); + ++index; + } + } + } + else + { + buf.append(string.charAt(index)); + ++index; + } + } + return buf.toString(); + } + +} diff --git a/libjava/classpath/java/text/DecimalFormatSymbols.java b/libjava/classpath/java/text/DecimalFormatSymbols.java new file mode 100644 index 00000000000..81ea0017822 --- /dev/null +++ b/libjava/classpath/java/text/DecimalFormatSymbols.java @@ -0,0 +1,688 @@ +/* DecimalFormatSymbols.java -- Format symbols used by DecimalFormat + Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Currency; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This class is a container for the symbols used by + * <code>DecimalFormat</code> to format numbers and currency + * for a particular locale. These are + * normally handled automatically, but an application can override + * values as desired using this class. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @date February 24, 1999 + * @see java.text.DecimalFormat + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2. + */ +public final class DecimalFormatSymbols implements Cloneable, Serializable +{ + public Object clone () + { + try + { + return super.clone (); + } + catch(CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method initializes a new instance of + * <code>DecimalFormatSymbols</code> for the default locale. + */ + public DecimalFormatSymbols () + { + this (Locale.getDefault()); + } + + /** + * Retrieves a valid string, either using the supplied resource + * bundle or the default value. + * + * @param bundle the resource bundle to use to find the string. + * @param name key for the string in the resource bundle. + * @param def default value for the string. + */ + private String safeGetString(ResourceBundle bundle, + String name, String def) + { + if (bundle != null) + { + try + { + return bundle.getString(name); + } + catch (MissingResourceException x) + { + } + } + return def; + } + + private char safeGetChar(ResourceBundle bundle, + String name, char def) + { + String r = null; + if (bundle != null) + { + try + { + r = bundle.getString(name); + } + catch (MissingResourceException x) + { + } + } + if (r == null || r.length() < 1) + return def; + return r.charAt(0); + } + + /** + * This method initializes a new instance of + * <code>DecimalFormatSymbols</code> for the specified locale. + * <strong>Note</strong>: if the locale does not have an associated + * <code>Currency</code> instance, the currency symbol and + * international currency symbol will be set to the strings "?" + * and "XXX" respectively. This generally happens with language + * locales (those with no specified country), such as + * <code>Locale.ENGLISH</code>. + * + * @param locale The local to load symbols for. + * @throws NullPointerException if the locale is null. + */ + public DecimalFormatSymbols (Locale loc) + { + ResourceBundle res; + + currency = Currency.getInstance("XXX"); + currencySymbol = "?"; + intlCurrencySymbol = "XXX"; + try + { + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + } + catch (MissingResourceException x) + { + res = null; + } + try + { + Currency localeCurrency = Currency.getInstance(loc); + if (localeCurrency != null) + { + setCurrency(localeCurrency); + } + } + catch(IllegalArgumentException exception) + { + /* Locale has an invalid currency */ + } + decimalSeparator = safeGetChar (res, "decimalSeparator", '.'); + digit = safeGetChar (res, "digit", '#'); + exponential = safeGetChar (res, "exponential", 'E'); + groupingSeparator = safeGetChar (res, "groupingSeparator", ','); + infinity = safeGetString (res, "infinity", "\u221e"); + try + { + monetarySeparator = safeGetChar (res, "monetarySeparator", '.'); + } + catch (MissingResourceException x) + { + monetarySeparator = decimalSeparator; + } + minusSign = safeGetChar (res, "minusSign", '-'); + NaN = safeGetString (res, "NaN", "\ufffd"); + patternSeparator = safeGetChar (res, "patternSeparator", ';'); + percent = safeGetChar (res, "percent", '%'); + perMill = safeGetChar (res, "perMill", '\u2030'); + zeroDigit = safeGetChar (res, "zeroDigit", '0'); + locale = loc; + } + + /** + * This method this this object for equality against the specified object. + * This will be true if and only if the following criteria are met with + * regard to the specified object: + * <p> + * <ul> + * <li>It is not <code>null</code>.</li> + * <li>It is an instance of <code>DecimalFormatSymbols</code>.</li> + * <li>All of its symbols are identical to the symbols in this object.</li> + * </ul> + * + * @return <code>true</code> if the specified object is equal to this + * object, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof DecimalFormatSymbols)) + return false; + DecimalFormatSymbols dfs = (DecimalFormatSymbols) obj; + return (currencySymbol.equals(dfs.currencySymbol) + && decimalSeparator == dfs.decimalSeparator + && digit == dfs.digit + && exponential == dfs.exponential + && groupingSeparator == dfs.groupingSeparator + && infinity.equals(dfs.infinity) + && intlCurrencySymbol.equals(dfs.intlCurrencySymbol) + && minusSign == dfs.minusSign + && monetarySeparator == dfs.monetarySeparator + && NaN.equals(dfs.NaN) + && patternSeparator == dfs.patternSeparator + && percent == dfs.percent + && perMill == dfs.perMill + && zeroDigit == dfs.zeroDigit); + } + + /** + * Returns the currency corresponding to the currency symbol stored + * in this instance of <code>DecimalFormatSymbols</code>. + * + * @return An instance of <code>Currency</code> which matches + * the currency used, or null if there is no corresponding + * instance. + */ + public Currency getCurrency () + { + return currency; + } + + /** + * This method returns the currency symbol in local format. For example, + * "$" for Canadian dollars. + * + * @return The currency symbol in local format. + */ + public String getCurrencySymbol () + { + return currencySymbol; + } + + /** + * This method returns the character used as the decimal point. + * + * @return The character used as the decimal point. + */ + public char getDecimalSeparator () + { + return decimalSeparator; + } + + /** + * This method returns the character used to represent a digit in a + * format pattern string. + * + * @return The character used to represent a digit in a format + * pattern string. + */ + public char getDigit () + { + return digit; + } + + /** + * This method returns the character used to represent the exponential + * format. This is a GNU Classpath extension. + * + * @return the character used to represent an exponential in a format + * pattern string. + */ + char getExponential () + { + return exponential; + } + + /** + * This method sets the character used to separate groups of digits. For + * example, the United States uses a comma (,) to separate thousands in + * a number. + * + * @return The character used to separate groups of digits. + */ + public char getGroupingSeparator () + { + return groupingSeparator; + } + + /** + * This method returns the character used to represent infinity. + * + * @return The character used to represent infinity. + */ + public String getInfinity () + { + return infinity; + } + + /** + * This method returns the ISO 4217 currency code for + * the currency used. + * + * @return the ISO 4217 currency code. + */ + public String getInternationalCurrencySymbol () + { + return intlCurrencySymbol; + } + + /** + * This method returns the character used to represent the minus sign. + * + * @return The character used to represent the minus sign. + */ + public char getMinusSign () + { + return minusSign; + } + + /** + * This method returns the character used to represent the decimal + * point for currency values. + * + * @return The decimal point character used in currency values. + */ + public char getMonetaryDecimalSeparator () + { + return monetarySeparator; + } + + /** + * This method returns the string used to represent the NaN (not a number) + * value. + * + * @return The string used to represent NaN + */ + public String getNaN () + { + return NaN; + } + + /** + * This method returns the character used to separate positive and negative + * subpatterns in a format pattern. + * + * @return The character used to separate positive and negative subpatterns + * in a format pattern. + */ + public char getPatternSeparator () + { + return patternSeparator; + } + + /** + * This method returns the character used as the percent sign. + * + * @return The character used as the percent sign. + */ + public char getPercent () + { + return percent; + } + + /** + * This method returns the character used as the per mille character. + * + * @return The per mille character. + */ + public char getPerMill () + { + return perMill; + } + + /** + * This method returns the character used to represent the digit zero. + * + * @return The character used to represent the digit zero. + */ + public char getZeroDigit () + { + return zeroDigit; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + // Compute based on zero digit, grouping separator, and decimal + // separator -- JCL book. This probably isn't a very good hash + // code. + return zeroDigit << 16 + groupingSeparator << 8 + decimalSeparator; + } + + /** + * This method sets the currency symbol and ISO 4217 currency + * code to the values obtained from the supplied currency. + * + * @param currency the currency from which to obtain the values. + * @throws NullPointerException if the currency is null. + */ + public void setCurrency (Currency currency) + { + intlCurrencySymbol = currency.getCurrencyCode(); + currencySymbol = currency.getSymbol(); + this.currency = currency; + } + + /** + * This method sets the currency symbol to the specified value. + * + * @param currencySymbol The new currency symbol + */ + public void setCurrencySymbol (String currency) + { + currencySymbol = currency; + } + + /** + * This method sets the decimal point character to the specified value. + * + * @param decimalSeparator The new decimal point character + */ + public void setDecimalSeparator (char decimalSep) + { + decimalSeparator = decimalSep; + } + + /** + * This method sets the character used to represents a digit in a format + * string to the specified value. + * + * @param digit The character used to represent a digit in a format pattern. + */ + public void setDigit (char digit) + { + this.digit = digit; + } + + /** + * This method sets the exponential character used in the format string to + * the specified value. This is a GNU Classpath extension. + * + * @param exp the character used for the exponential in a format pattern. + */ + void setExponential (char exp) + { + exponential = exp; + } + + /** + * This method sets the character used to separate groups of digits. + * + * @param groupingSeparator The character used to separate groups of digits. + */ + public void setGroupingSeparator (char groupSep) + { + groupingSeparator = groupSep; + } + + /** + * This method sets the string used to represents infinity. + * + * @param infinity The string used to represent infinity. + */ + public void setInfinity (String infinity) + { + this.infinity = infinity; + } + + /** + * This method sets the international currency symbol to the + * specified value. If a valid <code>Currency</code> instance + * exists for the international currency code, then this is + * used for the currency attribute, and the currency symbol + * is set to the corresponding value from this instance. + * Otherwise, the currency attribute is set to null and the + * symbol is left unmodified. + * + * @param currencyCode The new international currency symbol. + */ + public void setInternationalCurrencySymbol (String currencyCode) + { + intlCurrencySymbol = currencyCode; + try + { + currency = Currency.getInstance(currencyCode); + } + catch (IllegalArgumentException exception) + { + currency = null; + } + if (currency != null) + { + setCurrencySymbol(currency.getSymbol(locale)); + } + } + + /** + * This method sets the character used to represent the minus sign. + * + * @param minusSign The character used to represent the minus sign. + */ + public void setMinusSign (char minusSign) + { + this.minusSign = minusSign; + } + + /** + * This method sets the character used for the decimal point in currency + * values. + * + * @param monetarySeparator The decimal point character used in + * currency values. + */ + public void setMonetaryDecimalSeparator (char decimalSep) + { + monetarySeparator = decimalSep; + } + + /** + * This method sets the string used to represent the NaN (not a + * number) value. + * + * @param NaN The string used to represent NaN + */ + public void setNaN (String nan) + { + NaN = nan; + } + + /** + * This method sets the character used to separate positive and negative + * subpatterns in a format pattern. + * + * @param patternSeparator The character used to separate positive and + * negative subpatterns in a format pattern. + */ + public void setPatternSeparator (char patternSep) + { + patternSeparator = patternSep; + } + + /** + * This method sets the character used as the percent sign. + * + * @param percent The character used as the percent sign. + */ + public void setPercent (char percent) + { + this.percent = percent; + } + + /** + * This method sets the character used as the per mille character. + * + * @param perMill The per mille character. + */ + public void setPerMill (char perMill) + { + this.perMill = perMill; + } + + /** + * This method sets the character used to represent the digit zero. + * + * @param zeroDigit The character used to represent the digit zero. + */ + public void setZeroDigit (char zeroDigit) + { + this.zeroDigit = zeroDigit; + } + + /** + * @serial A string used for the local currency + */ + private String currencySymbol; + /** + * @serial The <code>char</code> used to separate decimals in a number. + */ + private char decimalSeparator; + /** + * @serial This is the <code>char</code> used to represent a digit in + * a format specification. + */ + private char digit; + /** + * @serial This is the <code>char</code> used to represent the exponent + * separator in exponential notation. + */ + private char exponential; + /** + * @serial This separates groups of thousands in numbers. + */ + private char groupingSeparator; + /** + * @serial This string represents infinity. + */ + private String infinity; + /** + * @serial This string represents the local currency in an international + * context, eg, "C$" for Canadian dollars. + */ + private String intlCurrencySymbol; + /** + * @serial This is the character used to represent the minus sign. + */ + private char minusSign; + /** + * @serial This character is used to separate decimals when formatting + * currency values. + */ + private char monetarySeparator; + /** + * @serial This string is used the represent the Java NaN value for + * "not a number". + */ + private String NaN; + /** + * @serial This is the character used to separate positive and negative + * subpatterns in a format pattern. + */ + private char patternSeparator; + /** + * @serial This is the percent symbols + */ + private char percent; + /** + * @serial This character is used for the mille percent sign. + */ + private char perMill; + /** + * @serial This value represents the type of object being de-serialized. + * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later. + * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later, + * 2 indicates 1.4 or later + */ + private int serialVersionOnStream = 2; + /** + * @serial This is the character used to represent 0. + */ + private char zeroDigit; + + /** + * @serial The locale of these currency symbols. + */ + private Locale locale; + + /** + * The currency used for the symbols in this instance. + * This is stored temporarily for efficiency reasons, + * as well as to ensure that the correct instance + * is restored from the currency code. + * + * @serial Ignored. + */ + private transient Currency currency; + + private static final long serialVersionUID = 5772796243397350300L; + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + monetarySeparator = decimalSeparator; + exponential = 'E'; + } + if (serialVersionOnStream < 2) + locale = Locale.getDefault(); + + serialVersionOnStream = 2; + } +} diff --git a/libjava/classpath/java/text/FieldPosition.java b/libjava/classpath/java/text/FieldPosition.java new file mode 100644 index 00000000000..427c07e8e11 --- /dev/null +++ b/libjava/classpath/java/text/FieldPosition.java @@ -0,0 +1,232 @@ +/* FieldPosition.java -- Keeps track of field positions while formatting + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used by the java.text formatting classes to track + * field positions. A field position is defined by an identifier value + * and begin and end index positions. The formatting classes in java.text + * typically define constant values for the field identifiers. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class FieldPosition +{ + /** + * This is the field identifier value. + */ + private int field_id; + + /** + * This is the beginning index of the field. + */ + private int begin; + + /** + * This is the ending index of the field. + */ + private int end; + + /** + * This is the field attribute value. + */ + private Format.Field field_attribute; + + /** + * This method initializes a new instance of <code>FieldPosition</code> + * to have the specified field attribute. The attribute will be used as + * an id. It is formally equivalent to calling FieldPosition(field, -1). + * + * @param field The field format attribute. + */ + public FieldPosition (Format.Field field) + { + this(field, -1); + } + + /** + * This method initializes a new instance of <code>FieldPosition</code> + * to have the specified field attribute. The attribute will be used as + * an id is non null. The integer field id is only used if the Format.Field + * attribute is not used by the formatter. + * + * @param field The field format attribute. + * @param field_id The field identifier value. + */ + public FieldPosition (Format.Field field, int field_id) + { + this.field_attribute = field; + this.field_id = field_id; + } + + /** + * This method initializes a new instance of <code>FieldPosition</code> to + * have the specified field id. + * + * @param field_id The field identifier value. + */ + public FieldPosition (int field_id) + { + this.field_id = field_id; + } + + /** + * This method returns the field identifier value for this object. + * + * @return The field identifier. + */ + public int getField () + { + return field_id; + } + + public Format.Field getFieldAttribute () + { + return field_attribute; + } + + /** + * This method returns the beginning index for this field. + * + * @return The beginning index. + */ + public int getBeginIndex () + { + return begin; + } + + /** + * This method sets the beginning index of this field to the specified value. + * + * @param begin The new beginning index. + */ + public void setBeginIndex (int begin) + { + this.begin = begin; + } + + /** + * This method returns the ending index for the field. + * + * @return The ending index. + */ + public int getEndIndex () + { + return end; + } + + /** + * This method sets the ending index of this field to the specified value. + * + * @param end The new ending index. + */ + public void setEndIndex (int end) + { + this.end = end; + } + + /** + * This method tests this object for equality against the specified object. + * The objects will be considered equal if and only if: + * <p> + * <ul> + * <li>The specified object is not <code>null</code>. + * <li>The specified object has the same class as this object. + * <li>The specified object has the same field identifier, field attribute + * and beginning and ending index as this object. + * </ul> + * + * @param obj The object to test for equality to this object. + * + * @return <code>true</code> if the specified object is equal to + * this object, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (this == obj) + return true; + + if (obj == null || obj.getClass() != this.getClass()) + return false; + + FieldPosition fp = (FieldPosition) obj; + return (field_id == fp.field_id + && (field_attribute == fp.field_attribute + || (field_attribute != null + && field_attribute.equals(fp.field_attribute))) + && begin == fp.begin + && end == fp.end); + } + + + /** + * This method returns a hash value for this object + * + * @return A hash value for this object. + */ + public int hashCode () + { + int hash = 5; + + hash = 31 * hash + field_id; + hash = 31 * hash + begin; + hash = 31 * hash + end; + hash = 31 * hash + + (null == field_attribute ? 0 : field_attribute.hashCode()); + + return hash; + } + + /** + * This method returns a <code>String</code> representation of this + * object. + * + * @return A <code>String</code> representation of this object. + */ + public String toString () + { + return (getClass ().getName () + + "[field=" + getField () + + ",attribute=" + getFieldAttribute () + + ",beginIndex=" + getBeginIndex () + + ",endIndex=" + getEndIndex () + + "]"); + } +} diff --git a/libjava/classpath/java/text/Format.java b/libjava/classpath/java/text/Format.java new file mode 100644 index 00000000000..38fda34ff64 --- /dev/null +++ b/libjava/classpath/java/text/Format.java @@ -0,0 +1,181 @@ +/* Format.java -- Abstract superclass for formatting/parsing strings. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.text.FormatCharacterIterator; + +import java.io.Serializable; + +/** + * This class is the abstract superclass of classes that format and parse + * data to/from <code>Strings</code>. It is guaranteed that any + * <code>String</code> produced by a concrete subclass of <code>Format</code> + * will be parseable by that same subclass. + * <p> + * In addition to implementing the abstract methods in this class, subclasses + * should provide static factory methods of the form + * <code>getInstance()</code> and <code>getInstance(Locale)</code> if the + * subclass loads different formatting/parsing schemes based on locale. + * These subclasses should also implement a static method called + * <code>getAvailableLocales()</code> which returns an array of + * available locales in the current runtime environment. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class Format implements Serializable, Cloneable +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + static final long serialVersionUID = -299282585814624189L; + + public static class Field extends AttributedCharacterIterator.Attribute + { + static final long serialVersionUID = 276966692217360283L; + + protected Field(String name) + { + super(name); + } + } + + /** + * This method initializes a new instance of <code>Format</code>. + * It performs no actions, but acts as a default constructor for + * subclasses. + */ + public Format () + { + } + + /** + * This method formats an <code>Object</code> into a <code>String</code>. + * + * @param obj The <code>Object</code> to format. + * + * @return The formatted <code>String</code>. + * + * @exception IllegalArgumentException If the <code>Object</code> + * cannot be formatted. + */ + public final String format(Object obj) throws IllegalArgumentException + { + StringBuffer sb = new StringBuffer (); + format (obj, sb, new FieldPosition (0)); + return sb.toString (); + } + + /** + * This method formats an <code>Object</code> into a <code>String</code> and + * appends the <code>String</code> to a <code>StringBuffer</code>. + * + * @param obj The <code>Object</code> to format. + * @param sb The <code>StringBuffer</code> to append to. + * @param pos The desired <code>FieldPosition</code>, which is also + * updated by this call. + * + * @return The updated <code>StringBuffer</code>. + * + * @exception IllegalArgumentException If the <code>Object</code> + * cannot be formatted. + */ + public abstract StringBuffer format (Object obj, StringBuffer sb, + FieldPosition pos) + throws IllegalArgumentException; + + /** + * This method parses a <code>String</code> and converts the parsed + * contents into an <code>Object</code>. + * + * @param str The <code>String</code> to parse. + * + * @return The resulting <code>Object</code>. + * + * @exception ParseException If the <code>String</code> cannot be parsed. + */ + public Object parseObject (String str) throws ParseException + { + ParsePosition pos = new ParsePosition(0); + Object result = parseObject (str, pos); + if (result == null) + { + int index = pos.getErrorIndex(); + if (index < 0) + index = pos.getIndex(); + throw new ParseException("parseObject failed", index); + } + return result; + } + + /** + * This method parses a <code>String</code> and converts the parsed + * contents into an <code>Object</code>. + * + * @param str The <code>String</code> to parse. + * @param pos The starting parse index on input, the ending parse + * index on output. + * + * @return The parsed <code>Object</code>, or <code>null</code> in + * case of error. + */ + public abstract Object parseObject (String str, ParsePosition pos); + + public AttributedCharacterIterator formatToCharacterIterator(Object obj) + { + return new FormatCharacterIterator(format(obj), null, null); + } + + /** + * Creates a copy of this object. + * + * @return The copied <code>Object</code>. + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException e) + { + return null; + } + } +} diff --git a/libjava/classpath/java/text/MessageFormat.java b/libjava/classpath/java/text/MessageFormat.java new file mode 100644 index 00000000000..f7a9f1687a6 --- /dev/null +++ b/libjava/classpath/java/text/MessageFormat.java @@ -0,0 +1,832 @@ +/* MessageFormat.java - Localized message formatting. + Copyright (C) 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.text.FormatCharacterIterator; + +import java.io.InvalidObjectException; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Vector; + +public class MessageFormat extends Format +{ + /** + * @author Tom Tromey (tromey@cygnus.com) + * @author Jorge Aliss (jaliss@hotmail.com) + * @date March 3, 1999 + */ + /* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2, except serialization. + * and parsing. + */ + private static final class MessageFormatElement + { + // Argument number. + int argNumber; + // Formatter to be used. This is the format set by setFormat. + Format setFormat; + // Formatter to be used based on the type. + Format format; + + // Argument will be checked to make sure it is an instance of this + // class. + Class formatClass; + + // Formatter type. + String type; + // Formatter style. + String style; + + // Text to follow this element. + String trailer; + + // Recompute the locale-based formatter. + void setLocale (Locale loc) + { + if (type == null) + ; + else if (type.equals("number")) + { + formatClass = java.lang.Number.class; + + if (style == null) + format = NumberFormat.getInstance(loc); + else if (style.equals("currency")) + format = NumberFormat.getCurrencyInstance(loc); + else if (style.equals("percent")) + format = NumberFormat.getPercentInstance(loc); + else if (style.equals("integer")) + { + NumberFormat nf = NumberFormat.getNumberInstance(loc); + nf.setMaximumFractionDigits(0); + nf.setGroupingUsed(false); + format = nf; + } + else + { + format = NumberFormat.getNumberInstance(loc); + DecimalFormat df = (DecimalFormat) format; + df.applyPattern(style); + } + } + else if (type.equals("time") || type.equals("date")) + { + formatClass = java.util.Date.class; + + int val = DateFormat.DEFAULT; + boolean styleIsPattern = false; + if (style == null) + ; + else if (style.equals("short")) + val = DateFormat.SHORT; + else if (style.equals("medium")) + val = DateFormat.MEDIUM; + else if (style.equals("long")) + val = DateFormat.LONG; + else if (style.equals("full")) + val = DateFormat.FULL; + else + styleIsPattern = true; + + if (type.equals("time")) + format = DateFormat.getTimeInstance(val, loc); + else + format = DateFormat.getDateInstance(val, loc); + + if (styleIsPattern) + { + SimpleDateFormat sdf = (SimpleDateFormat) format; + sdf.applyPattern(style); + } + } + else if (type.equals("choice")) + { + formatClass = java.lang.Number.class; + + if (style == null) + throw new + IllegalArgumentException ("style required for choice format"); + format = new ChoiceFormat (style); + } + } + } + + private static final long serialVersionUID = 6479157306784022952L; + + public static class Field extends Format.Field + { + static final long serialVersionUID = 7899943957617360810L; + + /** + * This is the attribute set for all characters produced + * by MessageFormat during a formatting. + */ + public static final MessageFormat.Field ARGUMENT = new MessageFormat.Field("argument"); + + // For deserialization + private Field() + { + super(""); + } + + protected Field(String s) + { + super(s); + } + + /** + * invoked to resolve the true static constant by + * comparing the deserialized object to know name. + * + * @return object constant + */ + protected Object readResolve() throws InvalidObjectException + { + if (getName().equals(ARGUMENT.getName())) + return ARGUMENT; + + throw new InvalidObjectException("no such MessageFormat field called " + getName()); + } + + } + + // Helper that returns the text up to the next format opener. The + // text is put into BUFFER. Returns index of character after end of + // string. Throws IllegalArgumentException on error. + private static int scanString(String pat, int index, StringBuffer buffer) + { + int max = pat.length(); + buffer.setLength(0); + boolean quoted = false; + for (; index < max; ++index) + { + char c = pat.charAt(index); + if (quoted) + { + // In a quoted context, a single quote ends the quoting. + if (c == '\'') + quoted = false; + else + buffer.append(c); + } + // Check for '', which is a single quote. + else if (c == '\'' && index + 1 < max && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + else if (c == '\'') + { + // Start quoting. + quoted = true; + } + else if (c == '{') + break; + else + buffer.append(c); + } + // Note that we explicitly allow an unterminated quote. This is + // done for compatibility. + return index; + } + + // This helper retrieves a single part of a format element. Returns + // the index of the terminating character. + private static int scanFormatElement(String pat, int index, + StringBuffer buffer, char term) + { + int max = pat.length(); + buffer.setLength(0); + int brace_depth = 1; + boolean quoted = false; + + for (; index < max; ++index) + { + char c = pat.charAt(index); + // First see if we should turn off quoting. + if (quoted) + { + if (c == '\'') + quoted = false; + // In both cases we fall through to inserting the + // character here. + } + // See if we have just a plain quote to insert. + else if (c == '\'' && index + 1 < max + && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + // See if quoting should turn on. + else if (c == '\'') + quoted = true; + else if (c == '{') + ++brace_depth; + else if (c == '}') + { + if (--brace_depth == 0) + break; + } + // Check for TERM after braces, because TERM might be `}'. + else if (c == term) + break; + // All characters, including opening and closing quotes, are + // inserted here. + buffer.append(c); + } + return index; + } + + // This is used to parse a format element and whatever non-format + // text might trail it. + private static int scanFormat(String pat, int index, StringBuffer buffer, + Vector elts, Locale locale) + { + MessageFormatElement mfe = new MessageFormatElement (); + elts.addElement(mfe); + + int max = pat.length(); + + // Skip the opening `{'. + ++index; + + // Fetch the argument number. + index = scanFormatElement (pat, index, buffer, ','); + try + { + mfe.argNumber = Integer.parseInt(buffer.toString()); + } + catch (NumberFormatException nfx) + { + IllegalArgumentException iae = new IllegalArgumentException(pat); + iae.initCause(nfx); + throw iae; + } + + // Extract the element format. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, ','); + mfe.type = buffer.toString(); + + // Extract the style. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, '}'); + mfe.style = buffer.toString (); + } + } + + // Advance past the last terminator. + if (index >= max || pat.charAt(index) != '}') + throw new IllegalArgumentException("Missing '}' at end of message format"); + ++index; + + // Now fetch trailing string. + index = scanString (pat, index, buffer); + mfe.trailer = buffer.toString (); + + mfe.setLocale(locale); + + return index; + } + + /** + * Applies the specified pattern to this MessageFormat. + * + * @param aPattern The Pattern + */ + public void applyPattern (String newPattern) + { + pattern = newPattern; + + StringBuffer tempBuffer = new StringBuffer (); + + int index = scanString (newPattern, 0, tempBuffer); + leader = tempBuffer.toString(); + + Vector elts = new Vector (); + while (index < newPattern.length()) + index = scanFormat (newPattern, index, tempBuffer, elts, locale); + + elements = new MessageFormatElement[elts.size()]; + elts.copyInto(elements); + } + + /** + * Overrides Format.clone() + */ + public Object clone () + { + MessageFormat c = (MessageFormat) super.clone (); + c.elements = (MessageFormatElement[]) elements.clone (); + return c; + } + + /** + * Overrides Format.equals(Object obj) + */ + public boolean equals (Object obj) + { + if (! (obj instanceof MessageFormat)) + return false; + MessageFormat mf = (MessageFormat) obj; + return (pattern.equals(mf.pattern) + && locale.equals(mf.locale)); + } + + /** + * A convinience method to format patterns. + * + * @param aPattern The pattern used when formatting. + * @param arguments The array containing the objects to be formatted. + */ + public AttributedCharacterIterator formatToCharacterIterator (Object arguments) + { + Object[] arguments_array = (Object[])arguments; + FormatCharacterIterator iterator = new FormatCharacterIterator(); + + formatInternal(arguments_array, new StringBuffer(), null, iterator); + + return iterator; + } + + /** + * A convinience method to format patterns. + * + * @param aPattern The pattern used when formatting. + * @param arguments The array containing the objects to be formatted. + */ + public static String format (String pattern, Object arguments[]) + { + MessageFormat mf = new MessageFormat (pattern); + StringBuffer sb = new StringBuffer (); + FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD); + return mf.formatInternal(arguments, sb, fp, null).toString(); + } + + /** + * Returns the pattern with the formatted objects. + * + * @param source The array containing the objects to be formatted. + * @param result The StringBuffer where the text is appened. + * @param fp A FieldPosition object (it is ignored). + */ + public final StringBuffer format (Object arguments[], StringBuffer appendBuf, + FieldPosition fp) + { + return formatInternal(arguments, appendBuf, fp, null); + } + + private StringBuffer formatInternal (Object arguments[], + StringBuffer appendBuf, + FieldPosition fp, + FormatCharacterIterator output_iterator) + { + appendBuf.append(leader); + if (output_iterator != null) + output_iterator.append(leader); + + for (int i = 0; i < elements.length; ++i) + { + Object thisArg = null; + boolean unavailable = false; + if (arguments == null || elements[i].argNumber >= arguments.length) + unavailable = true; + else + thisArg = arguments[elements[i].argNumber]; + + AttributedCharacterIterator iterator = null; + + Format formatter = null; + + if (fp != null && i == fp.getField() && fp.getFieldAttribute() == Field.ARGUMENT) + fp.setBeginIndex(appendBuf.length()); + + if (unavailable) + appendBuf.append("{" + elements[i].argNumber + "}"); + else + { + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + { + if (elements[i].formatClass != null + && ! elements[i].formatClass.isInstance(thisArg)) + throw new IllegalArgumentException("Wrong format class"); + + formatter = elements[i].format; + } + else if (thisArg instanceof Number) + formatter = NumberFormat.getInstance(locale); + else if (thisArg instanceof Date) + formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); + else + appendBuf.append(thisArg); + } + + if (fp != null && fp.getField() == i && fp.getFieldAttribute() == Field.ARGUMENT) + fp.setEndIndex(appendBuf.length()); + + if (formatter != null) + { + // Special-case ChoiceFormat. + if (formatter instanceof ChoiceFormat) + { + StringBuffer buf = new StringBuffer (); + formatter.format(thisArg, buf, fp); + MessageFormat mf = new MessageFormat (); + mf.setLocale(locale); + mf.applyPattern(buf.toString()); + mf.format(arguments, appendBuf, fp); + } + else + { + if (output_iterator != null) + iterator = formatter.formatToCharacterIterator(thisArg); + else + formatter.format(thisArg, appendBuf, fp); + } + + elements[i].format = formatter; + } + + if (output_iterator != null) + { + HashMap hash_argument = new HashMap(); + int position = output_iterator.getEndIndex(); + + hash_argument.put (MessageFormat.Field.ARGUMENT, + new Integer(elements[i].argNumber)); + + + if (iterator != null) + { + output_iterator.append(iterator); + output_iterator.addAttributes(hash_argument, position, + output_iterator.getEndIndex()); + } + else + output_iterator.append(thisArg.toString(), hash_argument); + + output_iterator.append(elements[i].trailer); + } + + appendBuf.append(elements[i].trailer); + } + + return appendBuf; + } + + /** + * Returns the pattern with the formatted objects. The first argument + * must be a array of Objects. + * This is equivalent to format((Object[]) objectArray, appendBuf, fpos) + * + * @param objectArray The object array to be formatted. + * @param appendBuf The StringBuffer where the text is appened. + * @param fpos A FieldPosition object (it is ignored). + */ + public final StringBuffer format (Object objectArray, StringBuffer appendBuf, + FieldPosition fpos) + { + return format ((Object[])objectArray, appendBuf, fpos); + } + + /** + * Returns an array with the Formats for + * the arguments. + */ + public Format[] getFormats () + { + Format[] f = new Format[elements.length]; + for (int i = elements.length - 1; i >= 0; --i) + f[i] = elements[i].setFormat; + return f; + } + + /** + * Returns the locale. + */ + public Locale getLocale () + { + return locale; + } + + /** + * Overrides Format.hashCode() + */ + public int hashCode () + { + // FIXME: not a very good hash. + return pattern.hashCode() + locale.hashCode(); + } + + private MessageFormat () + { + } + + /** + * Creates a new MessageFormat object with + * the specified pattern + * + * @param pattern The Pattern + */ + public MessageFormat(String pattern) + { + this(pattern, Locale.getDefault()); + } + + /** + * Creates a new MessageFormat object with + * the specified pattern + * + * @param pattern The Pattern + * @param locale The Locale to use + * + * @since 1.4 + */ + public MessageFormat(String pattern, Locale locale) + { + this.locale = locale; + applyPattern (pattern); + } + + /** + * Parse a string <code>sourceStr</code> against the pattern specified + * to the MessageFormat constructor. + * + * @param sourceStr the string to be parsed. + * @param pos the current parse position (and eventually the error position). + * @return the array of parsed objects sorted according to their argument number + * in the pattern. + */ + public Object[] parse (String sourceStr, ParsePosition pos) + { + // Check initial text. + int index = pos.getIndex(); + if (! sourceStr.startsWith(leader, index)) + { + pos.setErrorIndex(index); + return null; + } + index += leader.length(); + + Vector results = new Vector (elements.length, 1); + // Now check each format. + for (int i = 0; i < elements.length; ++i) + { + Format formatter = null; + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + formatter = elements[i].format; + + Object value = null; + if (formatter instanceof ChoiceFormat) + { + // We must special-case a ChoiceFormat because it might + // have recursive formatting. + ChoiceFormat cf = (ChoiceFormat) formatter; + String[] formats = (String[]) cf.getFormats(); + double[] limits = (double[]) cf.getLimits(); + MessageFormat subfmt = new MessageFormat (); + subfmt.setLocale(locale); + ParsePosition subpos = new ParsePosition (index); + + int j; + for (j = 0; value == null && j < limits.length; ++j) + { + subfmt.applyPattern(formats[j]); + subpos.setIndex(index); + value = subfmt.parse(sourceStr, subpos); + } + if (value != null) + { + index = subpos.getIndex(); + value = new Double (limits[j]); + } + } + else if (formatter != null) + { + pos.setIndex(index); + value = formatter.parseObject(sourceStr, pos); + if (value != null) + index = pos.getIndex(); + } + else + { + // We have a String format. This can lose in a number + // of ways, but we give it a shot. + int next_index; + if (elements[i].trailer.length() > 0) + next_index = sourceStr.indexOf(elements[i].trailer, index); + else + next_index = sourceStr.length(); + if (next_index == -1) + { + pos.setErrorIndex(index); + return null; + } + value = sourceStr.substring(index, next_index); + index = next_index; + } + + if (value == null + || ! sourceStr.startsWith(elements[i].trailer, index)) + { + pos.setErrorIndex(index); + return null; + } + + if (elements[i].argNumber >= results.size()) + results.setSize(elements[i].argNumber + 1); + results.setElementAt(value, elements[i].argNumber); + + index += elements[i].trailer.length(); + } + + Object[] r = new Object[results.size()]; + results.copyInto(r); + return r; + } + + public Object[] parse (String sourceStr) throws ParseException + { + ParsePosition pp = new ParsePosition (0); + Object[] r = parse (sourceStr, pp); + if (r == null) + throw new ParseException ("couldn't parse string", pp.getErrorIndex()); + return r; + } + + public Object parseObject (String sourceStr, ParsePosition pos) + { + return parse (sourceStr, pos); + } + + /** + * Sets the format for the argument at an specified + * index. + * + * @param index The index. + * @format The Format object. + */ + public void setFormat (int variableNum, Format newFormat) + { + elements[variableNum].setFormat = newFormat; + } + + /** + * Sets the formats for the arguments. + * + * @param formats An array of Format objects. + */ + public void setFormats (Format[] newFormats) + { + if (newFormats.length < elements.length) + throw new IllegalArgumentException("Not enough format objects"); + + int len = Math.min(newFormats.length, elements.length); + for (int i = 0; i < len; ++i) + elements[i].setFormat = newFormats[i]; + } + + /** + * Sets the locale. + * + * @param locale A Locale + */ + public void setLocale (Locale loc) + { + locale = loc; + if (elements != null) + { + for (int i = 0; i < elements.length; ++i) + elements[i].setLocale(loc); + } + } + + /** + * Returns the pattern. + */ + public String toPattern () + { + return pattern; + } + + /** + * Return the formatters used sorted by argument index. It uses the + * internal table to fill in this array: if a format has been + * set using <code>setFormat</code> or <code>setFormatByArgumentIndex</code> + * then it returns it at the right index. If not it uses the detected + * formatters during a <code>format</code> call. If nothing is known + * about that argument index it just puts null at that position. + * To get useful informations you may have to call <code>format</code> + * at least once. + * + * @return an array of formatters sorted by argument index. + */ + public Format[] getFormatsByArgumentIndex() + { + int argNumMax = 0; + // First, find the greatest argument number. + for (int i=0;i<elements.length;i++) + if (elements[i].argNumber > argNumMax) + argNumMax = elements[i].argNumber; + + Format[] formats = new Format[argNumMax]; + for (int i=0;i<elements.length;i++) + { + if (elements[i].setFormat != null) + formats[elements[i].argNumber] = elements[i].setFormat; + else if (elements[i].format != null) + formats[elements[i].argNumber] = elements[i].format; + } + return formats; + } + + /** + * Set the format to used using the argument index number. + * + * @param argumentIndex the argument index. + * @param newFormat the format to use for this argument. + */ + public void setFormatByArgumentIndex(int argumentIndex, + Format newFormat) + { + for (int i=0;i<elements.length;i++) + { + if (elements[i].argNumber == argumentIndex) + elements[i].setFormat = newFormat; + } + } + + /** + * Set the format for argument using a specified array of formatters + * which is sorted according to the argument index. If the number of + * elements in the array is fewer than the number of arguments only + * the arguments specified by the array are touched. + * + * @param newFormats array containing the new formats to set. + * + * @throws NullPointerException if newFormats is null + */ + public void setFormatsByArgumentIndex(Format[] newFormats) + { + for (int i=0;i<newFormats.length;i++) + { + // Nothing better than that can exist here. + setFormatByArgumentIndex(i, newFormats[i]); + } + } + + // The pattern string. + private String pattern; + // The locale. + private Locale locale; + // Variables. + private MessageFormatElement[] elements; + // Leader text. + private String leader; +} diff --git a/libjava/classpath/java/text/NumberFormat.java b/libjava/classpath/java/text/NumberFormat.java new file mode 100644 index 00000000000..a2c3997cc52 --- /dev/null +++ b/libjava/classpath/java/text/NumberFormat.java @@ -0,0 +1,808 @@ +/* NumberFormat.java -- Formats and parses numbers + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Currency; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This is the abstract superclass of all classes which format and + * parse numeric values such as decimal numbers, integers, currency values, + * and percentages. These classes perform their parsing and formatting + * in a locale specific manner, accounting for such items as differing + * currency symbols and thousands separators. + * <p> + * To create an instance of a concrete subclass of <code>NumberFormat</code>, + * do not call a class constructor directly. Instead, use one of the + * static factory methods in this class such as + * <code>getCurrencyInstance</code>. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 4, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2, except getAvailableLocales. + */ +public abstract class NumberFormat extends Format implements Cloneable +{ + /** + * This is a constant used to create a <code>FieldPosition</code> object + * that will return the integer portion of a formatted number. + */ + public static final int INTEGER_FIELD = 0; + + /** + * This is a constant used to create a <code>FieldPosition</code> object + * that will return the fractional portion of a formatted number. + */ + public static final int FRACTION_FIELD = 1; + + public static class Field extends Format.Field + { + static final long serialVersionUID = 7494728892700160890L; + + /** + * Attribute set to all characters containing digits of the integer + * part. + */ + public static final NumberFormat.Field INTEGER + = new Field("integer"); + + /** + * Attribute set to all characters containing digits of the fractional + * part. + */ + public static final NumberFormat.Field FRACTION + = new Field("fraction"); + + /** + * Attribute set to all characters containing digits of the exponential + * part. + */ + public static final NumberFormat.Field EXPONENT + = new Field("exponent"); + + /** + * Attribute set to all characters containing a decimal separator. + */ + public static final NumberFormat.Field DECIMAL_SEPARATOR + = new Field("decimal separator"); + + /** + * Attribute set to all characters containing a sign (plus or minus). + */ + public static final NumberFormat.Field SIGN + = new Field("sign"); + + /** + * Attribute set to all characters containing a grouping separator (e.g. + * a comma, a white space,...). + */ + public static final NumberFormat.Field GROUPING_SEPARATOR + = new Field("grouping separator"); + + /** + * Attribute set to all characters containing an exponential symbol (e.g. + * 'E') + */ + public static final NumberFormat.Field EXPONENT_SYMBOL + = new Field("exponent symbol"); + + /** + * Attribute set to all characters containing a percent symbol (e.g. '%') + */ + public static final NumberFormat.Field PERCENT + = new Field("percent"); + + /** + * Attribute set to all characters containing a permille symbol. + */ + public static final NumberFormat.Field PERMILLE + = new Field("permille"); + + /** + * Attribute set to all characters containing the currency unit. + */ + public static final NumberFormat.Field CURRENCY + = new Field("currency"); + + /** + * Attribute set to all characters containing the exponent sign. + */ + public static final NumberFormat.Field EXPONENT_SIGN + = new Field("exponent sign"); + + /** + * Private fields to register all fields contained in this descriptor. + */ + private static final NumberFormat.Field[] allFields = + { + INTEGER, FRACTION, EXPONENT, DECIMAL_SEPARATOR, SIGN, + GROUPING_SEPARATOR, EXPONENT_SYMBOL, PERCENT, + PERMILLE, CURRENCY, EXPONENT_SIGN + }; + + /** + * This constructor is only used by the deserializer. Without it, + * it would fail to construct a valid object. + */ + private Field() + { + super(""); + } + + /** + * Create a Field instance with the specified field name. + * + * @param field_name Field name for the new Field instance. + */ + protected Field(String field_name) + { + super (field_name); + } + + /** + * This function is used by the deserializer to know which object + * to use when it encounters an encoded NumberFormat.Field in a + * serialization stream. If the stream is valid it should return + * one of the above field. In the other case we throw an exception. + * + * @return a valid official NumberFormat.Field instance. + * + * @throws InvalidObjectException if the field name is invalid. + */ + protected Object readResolve() throws InvalidObjectException + { + String s = getName(); + for (int i = 0; i < allFields.length; i++) + if (s.equals(allFields[i].getName())) + return allFields[i]; + + throw new InvalidObjectException("no such NumberFormat field called " + + s); + } + } + + /** + * This method is a specialization of the format method that performs + * a simple formatting of the specified <code>long</code> number. + * + * @param number The <code>long</code> to format. + * + * @return The formatted number + */ + public final String format (long number) + { + StringBuffer sbuf = new StringBuffer(50); + format (number, sbuf, null); + return sbuf.toString(); + } + + public final StringBuffer format (Object obj, StringBuffer sbuf, + FieldPosition pos) + { + if (obj instanceof Number) + return format(((Number) obj).doubleValue(), sbuf, pos); + else + throw new IllegalArgumentException + ("Cannot format given Object as a Number"); + } + + /** + * This method formats the specified <code>double</code> and appends it to + * a <code>StringBuffer</code>. + * + * @param number The <code>double</code> to format. + * @param sb The <code>StringBuffer</code> to append the formatted number to. + * @param pos The desired <code>FieldPosition</code>. + * + * @return The <code>StringBuffer</code> with the appended number. + */ + public abstract StringBuffer format (double number, + StringBuffer sbuf, FieldPosition pos); + + /** + * This method formats the specified <code>long</code> and appends it to + * a <code>StringBuffer</code>. + * + * @param number The <code>long</code> to format. + * @param sb The <code>StringBuffer</code> to append the formatted number to. + * @param pos The desired <code>FieldPosition</code>. + * + * @return The <code>StringBuffer</code> with the appended number. + */ + public abstract StringBuffer format (long number, + StringBuffer sbuf, FieldPosition pos); + + /** + * This method tests the specified object for equality against this object. + * This will be <code>true</code> if the following conditions are met: + * <p> + * <ul> + * <li>The specified object is not <code>null</code>. + * <li>The specified object is an instance of <code>NumberFormat</code>. + * </ul> + * <p> + * Since this method does not test much, it is highly advised that + * concrete subclasses override this method. + * + * @param obj The <code>Object</code> to test against equality with + * this object. + * + * @return <code>true</code> if the specified object is equal to + * this object, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof NumberFormat)) + return false; + NumberFormat nf = (NumberFormat) obj; + return (groupingUsed == nf.groupingUsed + && maximumFractionDigits == nf.maximumFractionDigits + && maximumIntegerDigits == nf.maximumIntegerDigits + && minimumFractionDigits == nf.minimumFractionDigits + && minimumIntegerDigits == nf.minimumIntegerDigits + && parseIntegerOnly == nf.parseIntegerOnly); + } + + /** + * This method returns a list of locales for which concrete instances + * of <code>NumberFormat</code> subclasses may be created. + * + * @return The list of available locales. + */ + public static Locale[] getAvailableLocales () + { + Locale[] list = new Locale[1]; + list[0] = Locale.US; + return list; + } + + private static NumberFormat computeInstance(Locale loc, String resource, + String def) + { + ResourceBundle res; + try + { + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + } + catch (MissingResourceException x) + { + res = null; + } + String fmt; + try + { + fmt = res == null ? def : res.getString(resource); + } + catch (MissingResourceException x) + { + fmt = def; + } + DecimalFormatSymbols dfs = new DecimalFormatSymbols (loc); + return new DecimalFormat (fmt, dfs); + } + + /** + * This method returns an instance of <code>NumberFormat</code> suitable + * for formatting and parsing currency values in the default locale. + * + * @return An instance of <code>NumberFormat</code> for handling currencies. + */ + public static final NumberFormat getCurrencyInstance () + { + return getCurrencyInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>NumberFormat</code> suitable + * for formatting and parsing currency values in the specified locale. + * + * @return An instance of <code>NumberFormat</code> for handling currencies. + */ + public static NumberFormat getCurrencyInstance (Locale loc) + { + NumberFormat format; + + format = computeInstance (loc, "currencyFormat", "$#,##0.00;($#,##0.00)"); + format.setMaximumFractionDigits(format.getCurrency().getDefaultFractionDigits()); + return format; + } + + /** + * This method returns a default instance for the default locale. This + * will be a concrete subclass of <code>NumberFormat</code>, but the + * actual class returned is dependent on the locale. + * + * @return An instance of the default <code>NumberFormat</code> class. + */ + public static final NumberFormat getInstance () + { + return getInstance (Locale.getDefault()); + } + + /** + * This method returns a default instance for the specified locale. This + * will be a concrete subclass of <code>NumberFormat</code>, but the + * actual class returned is dependent on the locale. + * + * @param locale The desired locale. + * + * @return An instance of the default <code>NumberFormat</code> class. + */ + public static NumberFormat getInstance (Locale loc) + { + // For now always return a number instance. + return getNumberInstance (loc); + } + + /** + * This method returns the maximum number of digits allowed in the fraction + * portion of a number. + * + * @return The maximum number of digits allowed in the fraction + * portion of a number. + */ + public int getMaximumFractionDigits () + { + return maximumFractionDigits; + } + + /** + * This method returns the maximum number of digits allowed in the integer + * portion of a number. + * + * @return The maximum number of digits allowed in the integer + * portion of a number. + */ + public int getMaximumIntegerDigits () + { + return maximumIntegerDigits; + } + + /** + * This method returns the minimum number of digits allowed in the fraction + * portion of a number. + * + * @return The minimum number of digits allowed in the fraction + * portion of a number. + */ + public int getMinimumFractionDigits () + { + return minimumFractionDigits; + } + + /** + * This method returns the minimum number of digits allowed in the integer + * portion of a number. + * + * @return The minimum number of digits allowed in the integer + * portion of a number. + */ + public int getMinimumIntegerDigits () + { + return minimumIntegerDigits; + } + + /** + * This method returns a default instance for the specified locale. This + * will be a concrete subclass of <code>NumberFormat</code>, but the + * actual class returned is dependent on the locale. + * + * @param locale The desired locale. + * + * @return An instance of the default <code>NumberFormat</code> class. + */ + public static final NumberFormat getNumberInstance () + { + return getNumberInstance (Locale.getDefault()); + } + + /** + * This method returns a general purpose number formatting and parsing + * class for the default locale. This will be a concrete subclass of + * <code>NumberFormat</code>, but the actual class returned is dependent + * on the locale. + * + * @return An instance of a generic number formatter for the default locale. + */ + public static NumberFormat getNumberInstance (Locale loc) + { + return computeInstance (loc, "numberFormat", "#,##0.###"); + } + + /** + * This method returns an integer formatting and parsing class for the + * default locale. This will be a concrete subclass of <code>NumberFormat</code>, + * but the actual class returned is dependent on the locale. + * + * @return An instance of an integer number formatter for the default locale. + * @since 1.4 + */ + public static final NumberFormat getIntegerInstance() + { + return getIntegerInstance (Locale.getDefault()); + } + + /** + * This method returns an integer formatting and parsing class for the + * default locale. This will be a concrete subclass of <code>NumberFormat</code>, + * but the actual class returned is dependent on the locale. + * + * @param locale the desired locale. + * + * @return An instance of an integer number formatter for the desired locale. + * @since 1.4 + */ + public static NumberFormat getIntegerInstance(Locale locale) + { + NumberFormat format = computeInstance (locale, "numberFormat", "#,##0"); + format.setMaximumFractionDigits(0); + format.setParseIntegerOnly (true); + return format; + } + + /** + * This method returns an instance of <code>NumberFormat</code> suitable + * for formatting and parsing percentage values in the default locale. + * + * @return An instance of <code>NumberFormat</code> for handling percentages. + */ + public static final NumberFormat getPercentInstance () + { + return getPercentInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of <code>NumberFormat</code> suitable + * for formatting and parsing percentage values in the specified locale. + * + * @param locale The desired locale. + * + * @return An instance of <code>NumberFormat</code> for handling percentages. + */ + public static NumberFormat getPercentInstance (Locale loc) + { + return computeInstance (loc, "percentFormat", "#,##0%"); + } + + /** + * This method returns a hash value for this object. + * + * @return The hash code. + */ + public int hashCode () + { + int hash = super.hashCode(); + hash ^= (maximumFractionDigits + maximumIntegerDigits + + minimumFractionDigits + minimumIntegerDigits); + if (groupingUsed) + hash ^= 0xf0f0; + if (parseIntegerOnly) + hash ^= 0x0f0f; + return hash; + } + + /** + * This method tests whether or not grouping is in use. Grouping is + * a method of marking separations in numbers, such as thousand separators + * in the US English locale. The grouping positions and symbols are all + * locale specific. As an example, with grouping disabled, the number one + * million would appear as "1000000". With grouping enabled, this number + * might appear as "1,000,000". (Both of these assume the US English + * locale). + * + * @return <code>true</code> if grouping is enabled, + * <code>false</code> otherwise. + */ + public boolean isGroupingUsed () + { + return groupingUsed; + } + + /** + * This method tests whether or not only integer values should be parsed. + * If this class is parsing only integers, parsing stops at the decimal + * point. + * + * @return <code>true</code> if only integers are parsed, + * <code>false</code> otherwise. + */ + public boolean isParseIntegerOnly () + { + return parseIntegerOnly; + } + + /** + * This is a default constructor for use by subclasses. + */ + public NumberFormat () + { + } + + /** + * This method parses the specified string into a <code>Number</code>. This + * will be a <code>Long</code> if possible, otherwise it will be a + * <code>Double</code>. If no number can be parsed, no exception is + * thrown. Instead, the parse position remains at its initial index. + * + * @param str The string to parse. + * @param pp The desired <code>ParsePosition</code>. + * + * @return The parsed <code>Number</code> + */ + public abstract Number parse (String sourceStr, ParsePosition pos); + + /** + * This method parses the specified string into a <code>Number</code>. This + * will be a <code>Long</code> if possible, otherwise it will be a + * <code>Double</code>. If no number can be parsed, an exception will be + * thrown. + * + * @param str The string to parse. + * + * @return The parsed <code>Number</code> + * + * @exception ParseException If no number can be parsed. + */ + public Number parse (String sourceStr) throws ParseException + { + ParsePosition pp = new ParsePosition (0); + Number r = parse (sourceStr, pp); + if (r == null) + { + int index = pp.getErrorIndex(); + if (index < 0) + index = pp.getIndex(); + throw new ParseException ("couldn't parse number", index); + } + return r; + } + + /** + * This method parses the specified string into an <code>Object</code>. This + * will be a <code>Long</code> if possible, otherwise it will be a + * <code>Double</code>. If no number can be parsed, no exception is + * thrown. Instead, the parse position remains at its initial index. + * + * @param str The string to parse. + * @param pp The desired <code>ParsePosition</code>. + * + * @return The parsed <code>Object</code> + */ + public final Object parseObject (String sourceStr, ParsePosition pos) + { + return parse (sourceStr, pos); + } + + /** + * This method sets the grouping behavior of this formatter. Grouping is + * a method of marking separations in numbers, such as thousand separators + * in the US English locale. The grouping positions and symbols are all + * locale specific. As an example, with grouping disabled, the number one + * million would appear as "1000000". With grouping enabled, this number + * might appear as "1,000,000". (Both of these assume the US English + * locale). + * + * @param groupingUsed <code>true</code> to enable grouping, + * <code>false</code> to disable it. + */ + public void setGroupingUsed (boolean newValue) + { + groupingUsed = newValue; + } + + /** + * This method sets the maximum number of digits allowed in the fraction + * portion of a number to the specified value. If this is less than the + * current minimum allowed digits, the minimum allowed digits value will + * be lowered to be equal to the new maximum allowed digits value. + * + * @param maximumFractionDigits The new maximum fraction digits value. + */ + public void setMaximumFractionDigits (int newValue) + { + maximumFractionDigits = newValue; + if (getMinimumFractionDigits () > maximumFractionDigits) + setMinimumFractionDigits (maximumFractionDigits); + } + + /** + * This method sets the maximum number of digits allowed in the integer + * portion of a number to the specified value. If this is less than the + * current minimum allowed digits, the minimum allowed digits value will + * be lowered to be equal to the new maximum allowed digits value. + * + * @param maximumIntegerDigits The new maximum integer digits value. + */ + public void setMaximumIntegerDigits (int newValue) + { + maximumIntegerDigits = newValue; + if (getMinimumIntegerDigits () > maximumIntegerDigits) + setMinimumIntegerDigits (maximumIntegerDigits); + } + + /** + * This method sets the minimum number of digits allowed in the fraction + * portion of a number to the specified value. If this is greater than the + * current maximum allowed digits, the maximum allowed digits value will + * be raised to be equal to the new minimum allowed digits value. + * + * @param minimumFractionDigits The new minimum fraction digits value. + */ + public void setMinimumFractionDigits (int newValue) + { + minimumFractionDigits = newValue; + if (getMaximumFractionDigits () < minimumFractionDigits) + setMaximumFractionDigits (minimumFractionDigits); + } + + /** + * This method sets the minimum number of digits allowed in the integer + * portion of a number to the specified value. If this is greater than the + * current maximum allowed digits, the maximum allowed digits value will + * be raised to be equal to the new minimum allowed digits value. + * + * @param minimumIntegerDigits The new minimum integer digits value. + */ + public void setMinimumIntegerDigits (int newValue) + { + minimumIntegerDigits = newValue; + if (getMaximumIntegerDigits () < minimumIntegerDigits) + setMaximumIntegerDigits (minimumIntegerDigits); + } + + /** + * This method sets the parsing behavior of this object to parse only + * integers or not. + * + * @param parseIntegerOnly <code>true</code> to parse only integers, + * <code>false</code> otherwise. + */ + public void setParseIntegerOnly (boolean value) + { + parseIntegerOnly = value; + } + + /** + * This method is a specialization of the format method that performs + * a simple formatting of the specified <code>double</code> number. + * + * @param number The <code>double</code> to format. + * + * @return The formatted number + */ + public final String format (double number) + { + StringBuffer sbuf = new StringBuffer(50); + format (number, sbuf, null); + return sbuf.toString(); + } + + // These field names are fixed by the serialization spec. + boolean groupingUsed; + int maximumFractionDigits; + private byte maxFractionDigits; + int maximumIntegerDigits; + private byte maxIntegerDigits; + int minimumFractionDigits; + private byte minFractionDigits; + int minimumIntegerDigits; + private byte minIntegerDigits; + boolean parseIntegerOnly; + private int serialVersionOnStream; + private static final long serialVersionUID = -2308460125733713944L; + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + maximumFractionDigits = maxFractionDigits; + maximumIntegerDigits = maxIntegerDigits; + minimumFractionDigits = minFractionDigits; + minimumIntegerDigits = minIntegerDigits; + serialVersionOnStream = 1; + } + } + + private void writeObject(ObjectOutputStream stream) throws IOException + { + maxFractionDigits = maximumFractionDigits < Byte.MAX_VALUE ? + (byte) maximumFractionDigits : Byte.MAX_VALUE; + maxIntegerDigits = maximumIntegerDigits < Byte.MAX_VALUE ? + (byte) maximumIntegerDigits : Byte.MAX_VALUE; + minFractionDigits = minimumFractionDigits < Byte.MAX_VALUE ? + (byte) minimumFractionDigits : Byte.MAX_VALUE; + minIntegerDigits = minimumIntegerDigits < Byte.MAX_VALUE ? + (byte) minimumIntegerDigits : Byte.MAX_VALUE; + serialVersionOnStream = 1; + stream.defaultWriteObject(); + } + + /** + * Returns the currency used by this number format when formatting currency + * values. + * + * The default implementation throws UnsupportedOperationException. + * + * @return The used currency object, or null. + * + * @throws UnsupportedOperationException If the number format class doesn't + * implement currency formatting. + * + * @since 1.4 + */ + public Currency getCurrency() + { + throw new UnsupportedOperationException(); + } + + /** + * Sets the currency used by this number format when formatting currency + * values. + * + * The default implementation throws UnsupportedOperationException. + * + * @param currency The new currency to be used by this number format. + * + * @throws NullPointerException If currenc is null. + * @throws UnsupportedOperationException If the number format class doesn't + * implement currency formatting. + * + * @since 1.4 + */ + public void setCurrency(Currency currency) + { + if (currency == null) + throw new NullPointerException("currency may not be null"); + + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/text/ParseException.java b/libjava/classpath/java/text/ParseException.java new file mode 100644 index 00000000000..6d014effd2a --- /dev/null +++ b/libjava/classpath/java/text/ParseException.java @@ -0,0 +1,86 @@ +/* ParseException.java -- an error occurred while parsing + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This exception is thrown when an unexpected error occurs during parsing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @see Format + * @see FieldPosition + * @status updated to 1.4 + */ +public class ParseException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2703218443322787634L; + + /** + * This is the position where the error was encountered. + * + * @serial the zero-based offset in the string where the error occurred + */ + private final int errorOffset; + + /** + * This method initializes a new instance of <code>ParseException</code> + * with a detailed error message and a error position. + * + * @param msg the descriptive message describing the error + * @param offset the position where the error was encountered + */ + public ParseException(String s, int offset) + { + super(s); + errorOffset = offset; + } + + /** + * This method returns the position where the error occurred. + * + * @return the position where the error occurred + */ + public int getErrorOffset() + { + return errorOffset; + } +} // class ParseException diff --git a/libjava/classpath/java/text/ParsePosition.java b/libjava/classpath/java/text/ParsePosition.java new file mode 100644 index 00000000000..782f5e0eda2 --- /dev/null +++ b/libjava/classpath/java/text/ParsePosition.java @@ -0,0 +1,151 @@ +/* ParsePosition.java -- Keep track of position while parsing. + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used to keep track of the current position during parsing + * operations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class ParsePosition +{ + /** + * This is the index of the current parse position. + */ + private int index; + + /** + * This is the index of the position where an error occurred during parsing. + */ + private int error_index; + + /** + * This method initializes a new instance of <code>ParsePosition</code> to + * have the specified initial index value. + * + * @param index The initial parsing index. + */ + public ParsePosition (int index) + { + this.index = index; + error_index = -1; + } + + /** + * This method returns the current parsing index. + * + * @return The current parsing index + */ + public int getIndex () + { + return index; + } + + /** + * This method sets the current parsing index to the specified value. + * + * @param index The new parsing index. + */ + public void setIndex (int index) + { + this.index = index; + } + + /** + * This method returns the error index value. This value defaults to -1 + * unless explicitly set to another value. + * + * @return The error index. + */ + public int getErrorIndex () + { + return error_index; + } + + /** + * This method sets the error index to the specified value. + * + * @param error_index The new error index + */ + public void setErrorIndex (int error_index) + { + this.error_index = error_index; + } + + /** + * This method tests the specified object for equality with this + * object. The two objects will be considered equal if and only if + * all of the following conditions are met. + * <p> + * <ul> + * <li>The specified object is not <code>null</code>.</li> + * <li>The specified object is an instance of <code>ParsePosition</code>.</li> + * <li>The specified object has the same index and error index as + * this object.</li> + * </ul> + * + * @param obj The <code>Object</code> to test for equality against + * this object. + * + * @return <code>true</code> if the specified object is equal to + * this object, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof ParsePosition)) + return false; + + ParsePosition other = (ParsePosition) obj; + return index == other.index && error_index == other.error_index; + } + + /** + * This method returns a <code>String</code> representation of this + * object. + * + * @return A <code>String</code> that represents this object. + */ + public String toString () + { + return (getClass ().getName () + "[index=" + getIndex () + + ",errorIndex=" + getErrorIndex () + "]"); + } +} diff --git a/libjava/classpath/java/text/RuleBasedCollator.java b/libjava/classpath/java/text/RuleBasedCollator.java new file mode 100644 index 00000000000..ae84a41032d --- /dev/null +++ b/libjava/classpath/java/text/RuleBasedCollator.java @@ -0,0 +1,1017 @@ +/* RuleBasedCollator.java -- Concrete Collator Class + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.ArrayList; +import java.util.HashMap; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct + */ + +/** + * This class is a concrete subclass of <code>Collator</code> suitable + * for string collation in a wide variety of languages. An instance of + * this class is normally returned by the <code>getInstance</code> method + * of <code>Collator</code> with rules predefined for the requested + * locale. However, an instance of this class can be created manually + * with any desired rules. + * <p> + * Rules take the form of a <code>String</code> with the following syntax + * <ul> + * <li> Modifier: '@'</li> + * <li> Relation: '<' | ';' | ',' | '=' : <text></li> + * <li> Reset: '&' : <text></li> + * </ul> + * The modifier character indicates that accents sort backward as is the + * case with French. The modifier applies to all rules <b>after</b> + * the modifier but before the next primary sequence. If placed at the end + * of the sequence if applies to all unknown accented character. + * The relational operators specify how the text + * argument relates to the previous term. The relation characters have + * the following meanings: + * <ul> + * <li>'<' - The text argument is greater than the prior term at the primary + * difference level.</li> + * <li>';' - The text argument is greater than the prior term at the secondary + * difference level.</li> + * <li>',' - The text argument is greater than the prior term at the tertiary + * difference level.</li> + * <li>'=' - The text argument is equal to the prior term</li> + * </ul> + * <p> + * As for the text argument itself, this is any sequence of Unicode + * characters not in the following ranges: 0x0009-0x000D, 0x0020-0x002F, + * 0x003A-0x0040, 0x005B-0x0060, and 0x007B-0x007E. If these characters are + * desired, they must be enclosed in single quotes. If any whitespace is + * encountered, it is ignored. (For example, "a b" is equal to "ab"). + * <p> + * The reset operation inserts the following rule at the point where the + * text argument to it exists in the previously declared rule string. This + * makes it easy to add new rules to an existing string by simply including + * them in a reset sequence at the end. Note that the text argument, or + * at least the first character of it, must be present somewhere in the + * previously declared rules in order to be inserted properly. If this + * is not satisfied, a <code>ParseException</code> will be thrown. + * <p> + * This system of configuring <code>RuleBasedCollator</code> is needlessly + * complex and the people at Taligent who developed it (along with the folks + * at Sun who accepted it into the Java standard library) deserve a slow + * and agonizing death. + * <p> + * Here are a couple of example of rule strings: + * <p> + * "< a < b < c" - This string says that a is greater than b which is + * greater than c, with all differences being primary differences. + * <p> + * "< a,A < b,B < c,C" - This string says that 'A' is greater than 'a' with + * a tertiary strength comparison. Both 'b' and 'B' are greater than 'a' and + * 'A' during a primary strength comparison. But 'B' is greater than 'b' + * under a tertiary strength comparison. + * <p> + * "< a < c & a < b " - This sequence is identical in function to the + * "< a < b < c" rule string above. The '&' reset symbol indicates that + * the rule "< b" is to be inserted after the text argument "a" in the + * previous rule string segment. + * <p> + * "< a < b & y < z" - This is an error. The character 'y' does not appear + * anywhere in the previous rule string segment so the rule following the + * reset rule cannot be inserted. + * <p> + * "< a & A @ < e & E < f& F" - This sequence is equivalent to the following + * "< a & A < E & e < f & F". + * <p> + * For a description of the various comparison strength types, see the + * documentation for the <code>Collator</code> class. + * <p> + * As an additional complication to this already overly complex rule scheme, + * if any characters precede the first rule, these characters are considered + * ignorable. They will be treated as if they did not exist during + * comparisons. For example, "- < a < b ..." would make '-' an ignorable + * character such that the strings "high-tech" and "hightech" would + * be considered identical. + * <p> + * A <code>ParseException</code> will be thrown for any of the following + * conditions: + * <ul> + * <li>Unquoted punctuation characters in a text argument.</li> + * <li>A relational or reset operator not followed by a text argument</li> + * <li>A reset operator where the text argument is not present in + * the previous rule string section.</li> + * </ul> + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + */ +public class RuleBasedCollator extends Collator +{ + /** + * This class describes what rank has a character (or a sequence of characters) + * in the lexicographic order. Each element in a rule has a collation element. + */ + static final class CollationElement + { + String key; + int primary; + short secondary; + short tertiary; + short equality; + boolean ignore; + String expansion; + + CollationElement(String key, int primary, short secondary, short tertiary, + short equality, String expansion, boolean ignore) + { + this.key = key; + this.primary = primary; + this.secondary = secondary; + this.tertiary = tertiary; + this.equality = equality; + this.ignore = ignore; + this.expansion = expansion; + } + + int getValue() + { + return (primary << 16) + (secondary << 8) + tertiary; + } + } + + /** + * Basic collation instruction (internal format) to build the series of + * collation elements. It contains an instruction which specifies the new + * state of the generator. The sequence of instruction should not contain + * RESET (it is used by + * {@link #mergeRules(int,java.lang.String,java.util.ArrayList,java.util.ArrayList)}) + * as a temporary state while merging two sets of instructions. + */ + static final class CollationSorter + { + static final int GREATERP = 0; + static final int GREATERS = 1; + static final int GREATERT = 2; + static final int EQUAL = 3; + static final int RESET = 4; + static final int INVERSE_SECONDARY = 5; + + int comparisonType; + String textElement; + int hashText; + int offset; + boolean ignore; + + String expansionOrdering; + } + + /** + * This the the original rule string. + */ + private String rules; + + /** + * This is the table of collation element values + */ + private Object[] ce_table; + + /** + * Quick-prefix finder. + */ + HashMap prefix_tree; + + /** + * This is the value of the last sequence entered into + * <code>ce_table</code>. It is used to compute the + * ordering value of unspecified character. + */ + private int last_primary_value; + + /** + * This is the value of the last secondary sequence of the + * primary 0, entered into + * <code>ce_table</code>. It is used to compute the + * ordering value of an unspecified accented character. + */ + private int last_tertiary_value; + + /** + * This variable is true if accents need to be sorted + * in the other direction. + */ + private boolean inverseAccentComparison; + + /** + * This collation element is special to unknown sequence. + * The JDK uses it to mark and sort the characters which has + * no collation rules. + */ + static final CollationElement SPECIAL_UNKNOWN_SEQ = + new CollationElement("", (short) 32767, (short) 0, (short) 0, + (short) 0, null, false); + + /** + * This method initializes a new instance of <code>RuleBasedCollator</code> + * with the specified collation rules. Note that an application normally + * obtains an instance of <code>RuleBasedCollator</code> by calling the + * <code>getInstance</code> method of <code>Collator</code>. That method + * automatically loads the proper set of rules for the desired locale. + * + * @param rules The collation rule string. + * + * @exception ParseException If the rule string contains syntax errors. + */ + public RuleBasedCollator(String rules) throws ParseException + { + if (rules.equals("")) + throw new ParseException("empty rule set", 0); + + this.rules = rules; + + buildCollationVector(parseString(rules)); + buildPrefixAccess(); + } + + /** + * This method returns the number of common characters at the beginning + * of the string of the two parameters. + * + * @param prefix A string considered as a prefix to test against + * the other string. + * @param s A string to test the prefix against. + * @return The number of common characters. + */ + static int findPrefixLength(String prefix, String s) + { + int index; + int len = prefix.length(); + + for (index = 0; index < len && index < s.length(); ++index) + { + if (prefix.charAt(index) != s.charAt(index)) + return index; + } + + + return index; + } + + /** + * Here we are merging two sets of sorting instructions: 'patch' into 'main'. This methods + * checks whether it is possible to find an anchor point for the rules to be merged and + * then insert them at that precise point. + * + * @param offset Offset in the string containing rules of the beginning of the rules + * being merged in. + * @param starter Text of the rules being merged. + * @param main Repository of all already parsed rules. + * @param patch Rules to be merged into the repository. + * @throws ParseException if it is impossible to find an anchor point for the new rules. + */ + private void mergeRules(int offset, String starter, ArrayList main, ArrayList patch) + throws ParseException + { + int insertion_point = -1; + int max_length = 0; + + /* We must check that no rules conflict with another already present. If it + * is the case delete the old rule. + */ + + /* For the moment good old O(N^2) algorithm. + */ + for (int i = 0; i < patch.size(); i++) + { + int j = 0; + + while (j < main.size()) + { + CollationSorter rule1 = (CollationSorter) patch.get(i); + CollationSorter rule2 = (CollationSorter) main.get(j); + + if (rule1.textElement.equals(rule2.textElement)) + main.remove(j); + else + j++; + } + } + + // Find the insertion point... O(N) + for (int i = 0; i < main.size(); i++) + { + CollationSorter sorter = (CollationSorter) main.get(i); + int length = findPrefixLength(starter, sorter.textElement); + + if (length > max_length) + { + max_length = length; + insertion_point = i+1; + } + } + + if (insertion_point < 0) + throw new ParseException("no insertion point found for " + starter, offset); + + if (max_length < starter.length()) + { + /* + * We need to expand the first entry. It must be sorted + * like if it was the reference key itself (like the spec + * said. So the first entry is special: the element is + * replaced by the specified text element for the sorting. + * This text replace the old one for comparisons. However + * to preserve the behaviour we replace the first key (corresponding + * to the found prefix) by a new code rightly ordered in the + * sequence. The rest of the subsequence must be appended + * to the end of the sequence. + */ + CollationSorter sorter = (CollationSorter) patch.get(0); + CollationSorter expansionPrefix = + (CollationSorter) main.get(insertion_point-1); + + sorter.expansionOrdering = starter.substring(max_length); // Skip the first good prefix element + + main.add(insertion_point, sorter); + + /* + * This is a new set of rules. Append to the list. + */ + patch.remove(0); + insertion_point++; + } + + // Now insert all elements of patch at the insertion point. + for (int i = 0; i < patch.size(); i++) + main.add(i+insertion_point, patch.get(i)); + } + + /** + * This method parses a string and build a set of sorting instructions. The parsing + * may only be partial on the case the rules are to be merged sometime later. + * + * @param stop_on_reset If this parameter is true then the parser stops when it + * encounters a reset instruction. In the other case, it tries to parse the subrules + * and merged it in the same repository. + * @param v Output vector for the set of instructions. + * @param base_offset Offset in the string to begin parsing. + * @param rules Rules to be parsed. + * @return -1 if the parser reached the end of the string, an integer representing the + * offset in the string at which it stopped parsing. + * @throws ParseException if something turned wrong during the parsing. To get details + * decode the message. + */ + private int subParseString(boolean stop_on_reset, ArrayList v, + int base_offset, String rules) + throws ParseException + { + boolean ignoreChars = (base_offset == 0); + int operator = -1; + StringBuffer sb = new StringBuffer(); + boolean doubleQuote = false; + boolean eatingChars = false; + boolean nextIsModifier = false; + boolean isModifier = false; + int i; + +main_parse_loop: + for (i = 0; i < rules.length(); i++) + { + char c = rules.charAt(i); + int type = -1; + + if (!eatingChars && + ((c >= 0x09 && c <= 0x0D) || (c == 0x20))) + continue; + + isModifier = nextIsModifier; + nextIsModifier = false; + + if (eatingChars && c != '\'') + { + doubleQuote = false; + sb.append(c); + continue; + } + if (doubleQuote && eatingChars) + { + sb.append(c); + doubleQuote = false; + continue; + } + + switch (c) + { + case '!': + throw new ParseException + ("Modifier '!' is not yet supported by Classpath", i + base_offset); + case '<': + type = CollationSorter.GREATERP; + break; + case ';': + type = CollationSorter.GREATERS; + break; + case ',': + type = CollationSorter.GREATERT; + break; + case '=': + type = CollationSorter.EQUAL; + break; + case '\'': + eatingChars = !eatingChars; + doubleQuote = true; + break; + case '@': + if (ignoreChars) + throw new ParseException + ("comparison list has not yet been started. You may only use" + + "(<,;=&)", i + base_offset); + // Inverse the order of secondaries from now on. + nextIsModifier = true; + type = CollationSorter.INVERSE_SECONDARY; + break; + case '&': + type = CollationSorter.RESET; + if (stop_on_reset) + break main_parse_loop; + break; + default: + if (operator < 0) + throw new ParseException + ("operator missing at " + (i + base_offset), i + base_offset); + if (! eatingChars + && ((c >= 0x21 && c <= 0x2F) + || (c >= 0x3A && c <= 0x40) + || (c >= 0x5B && c <= 0x60) + || (c >= 0x7B && c <= 0x7E))) + throw new ParseException + ("unquoted punctuation character '" + c + "'", i + base_offset); + + //type = ignoreChars ? CollationSorter.IGNORE : -1; + sb.append(c); + break; + } + + if (type < 0) + continue; + + if (operator < 0) + { + operator = type; + continue; + } + + if (sb.length() == 0 && !isModifier) + throw new ParseException + ("text element empty at " + (i+base_offset), i+base_offset); + + if (operator == CollationSorter.RESET) + { + /* Reposition in the sorting list at the position + * indicated by the text element. + */ + String subrules = rules.substring(i); + ArrayList sorted_rules = new ArrayList(); + int idx; + + // Parse the subrules but do not iterate through all + // sublist. This is the priviledge of the first call. + idx = subParseString(true, sorted_rules, base_offset+i, subrules); + + // Merge new parsed rules into the list. + mergeRules(base_offset+i, sb.toString(), v, sorted_rules); + sb.setLength(0); + + // Reset state to none. + operator = -1; + type = -1; + // We have found a new subrule at 'idx' but it has not been parsed. + if (idx >= 0) + { + i += idx-1; + continue main_parse_loop; + } + else + // No more rules. + break main_parse_loop; + } + + CollationSorter sorter = new CollationSorter(); + + if (operator == CollationSorter.GREATERP) + ignoreChars = false; + + sorter.comparisonType = operator; + sorter.textElement = sb.toString(); + sorter.hashText = sorter.textElement.hashCode(); + sorter.offset = base_offset+rules.length(); + sorter.ignore = ignoreChars; + sb.setLength(0); + + v.add(sorter); + operator = type; + } + + if (operator >= 0) + { + CollationSorter sorter = new CollationSorter(); + int pos = rules.length() + base_offset; + + if ((sb.length() != 0 && nextIsModifier) + || (sb.length() == 0 && !nextIsModifier && !eatingChars)) + throw new ParseException("text element empty at " + pos, pos); + + if (operator == CollationSorter.GREATERP) + ignoreChars = false; + + sorter.comparisonType = operator; + sorter.textElement = sb.toString(); + sorter.hashText = sorter.textElement.hashCode(); + sorter.offset = base_offset+pos; + sorter.ignore = ignoreChars; + v.add(sorter); + } + + if (i == rules.length()) + return -1; + else + return i; + } + + /** + * This method creates a copy of this object. + * + * @return A copy of this object. + */ + public Object clone() + { + return super.clone(); + } + + /** + * This method completely parses a string 'rules' containing sorting rules. + * + * @param rules String containing the rules to be parsed. + * @return A set of sorting instructions stored in a Vector. + * @throws ParseException if something turned wrong during the parsing. To get details + * decode the message. + */ + private ArrayList parseString(String rules) + throws ParseException + { + ArrayList v = new ArrayList(); + + // result of the first subParseString is not absolute (may be -1 or a + // positive integer). But we do not care. + subParseString(false, v, 0, rules); + + return v; + } + + /** + * This method uses the sorting instructions built by {@link #parseString} + * to build collation elements which can be directly used to sort strings. + * + * @param parsedElements Parsed instructions stored in a ArrayList. + * @throws ParseException if the order of the instructions are not valid. + */ + private void buildCollationVector(ArrayList parsedElements) + throws ParseException + { + int primary_seq = 0; + int last_tertiary_seq = 0; + short secondary_seq = 0; + short tertiary_seq = 0; + short equality_seq = 0; + boolean inverseComparisons = false; + final boolean DECREASING = false; + final boolean INCREASING = true; + boolean secondaryType = INCREASING; + ArrayList v = new ArrayList(); + + // elts is completely sorted. +element_loop: + for (int i = 0; i < parsedElements.size(); i++) + { + CollationSorter elt = (CollationSorter) parsedElements.get(i); + boolean ignoreChar = false; + + switch (elt.comparisonType) + { + case CollationSorter.GREATERP: + primary_seq++; + if (inverseComparisons) + { + secondary_seq = Short.MAX_VALUE; + secondaryType = DECREASING; + } + else + { + secondary_seq = 0; + secondaryType = INCREASING; + } + tertiary_seq = 0; + equality_seq = 0; + inverseComparisons = false; + break; + case CollationSorter.GREATERS: + if (secondaryType == DECREASING) + secondary_seq--; + else + secondary_seq++; + tertiary_seq = 0; + equality_seq = 0; + break; + case CollationSorter.INVERSE_SECONDARY: + inverseComparisons = true; + continue element_loop; + case CollationSorter.GREATERT: + tertiary_seq++; + if (primary_seq == 0) + last_tertiary_seq = tertiary_seq; + equality_seq = 0; + break; + case CollationSorter.EQUAL: + equality_seq++; + break; + case CollationSorter.RESET: + throw new ParseException + ("Invalid reached state 'RESET'. Internal error", elt.offset); + default: + throw new ParseException + ("Invalid unknown state '" + elt.comparisonType + "'", elt.offset); + } + + v.add(new CollationElement(elt.textElement, primary_seq, + secondary_seq, tertiary_seq, + equality_seq, elt.expansionOrdering, elt.ignore)); + } + + this.inverseAccentComparison = inverseComparisons; + + ce_table = v.toArray(); + + last_primary_value = primary_seq+1; + last_tertiary_value = last_tertiary_seq+1; + } + + /** + * Build a tree where all keys are the texts of collation elements and data is + * the collation element itself. The tree is used when extracting all prefix + * for a given text. + */ + private void buildPrefixAccess() + { + prefix_tree = new HashMap(); + + for (int i = 0; i < ce_table.length; i++) + { + CollationElement e = (CollationElement) ce_table[i]; + + prefix_tree.put(e.key, e); + } + } + + /** + * This method returns an integer which indicates whether the first + * specified <code>String</code> is less than, greater than, or equal to + * the second. The value depends not only on the collation rules in + * effect, but also the strength and decomposition settings of this object. + * + * @param source The first <code>String</code> to compare. + * @param target A second <code>String</code> to compare to the first. + * + * @return A negative integer if source < target, a positive integer + * if source > target, or 0 if source == target. + */ + public int compare(String source, String target) + { + CollationElementIterator cs, ct; + CollationElement ord1block = null; + CollationElement ord2block = null; + boolean advance_block_1 = true; + boolean advance_block_2 = true; + + cs = getCollationElementIterator(source); + ct = getCollationElementIterator(target); + + for(;;) + { + int ord1; + int ord2; + + /* + * We have to check whether the characters are ignorable. + * If it is the case then forget them. + */ + if (advance_block_1) + { + ord1block = cs.nextBlock(); + if (ord1block != null && ord1block.ignore) + continue; + } + + if (advance_block_2) + { + ord2block = ct.nextBlock(); + if (ord2block != null && ord2block.ignore) + { + advance_block_1 = false; + continue; + } + } + else + advance_block_2 = true; + + if (!advance_block_1) + advance_block_1 = true; + + if (ord1block != null) + ord1 = ord1block.getValue(); + else + { + if (ord2block == null) + return 0; + return -1; + } + + if (ord2block == null) + return 1; + + ord2 = ord2block.getValue(); + + // We know chars are totally equal, so skip + if (ord1 == ord2) + { + if (getStrength() == IDENTICAL) + if (!ord1block.key.equals(ord2block.key)) + return ord1block.key.compareTo(ord2block.key); + continue; + } + + // Check for primary strength differences + int prim1 = CollationElementIterator.primaryOrder(ord1); + int prim2 = CollationElementIterator.primaryOrder(ord2); + + if (prim1 == 0 && getStrength() < TERTIARY) + { + advance_block_2 = false; + continue; + } + else if (prim2 == 0 && getStrength() < TERTIARY) + { + advance_block_1 = false; + continue; + } + + if (prim1 < prim2) + return -1; + else if (prim1 > prim2) + return 1; + else if (getStrength() == PRIMARY) + continue; + + // Check for secondary strength differences + int sec1 = CollationElementIterator.secondaryOrder(ord1); + int sec2 = CollationElementIterator.secondaryOrder(ord2); + + if (sec1 < sec2) + return -1; + else if (sec1 > sec2) + return 1; + else if (getStrength() == SECONDARY) + continue; + + // Check for tertiary differences + int tert1 = CollationElementIterator.tertiaryOrder(ord1); + int tert2 = CollationElementIterator.tertiaryOrder(ord2); + + if (tert1 < tert2) + return -1; + else if (tert1 > tert2) + return 1; + else if (getStrength() == TERTIARY) + continue; + + // Apparently JDK does this (at least for my test case). + return ord1block.key.compareTo(ord2block.key); + } + } + + /** + * This method tests this object for equality against the specified + * object. This will be true if and only if the specified object is + * another reference to this object. + * + * @param obj The <code>Object</code> to compare against this object. + * + * @return <code>true</code> if the specified object is equal to this object, + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (obj == this) + return true; + else + return false; + } + + /** + * This method builds a default collation element without invoking + * the database created from the rules passed to the constructor. + * + * @param c Character which needs a collation element. + * @return A valid brand new CollationElement instance. + */ + CollationElement getDefaultElement(char c) + { + int v; + + // Preliminary support for generic accent sorting inversion (I don't know if all + // characters in the range should be sorted backward). This is the place + // to fix this if needed. + if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361)) + v = 0x0361 - ((int) c - 0x02B9); + else + v = (short) c; + return new CollationElement("" + c, last_primary_value + v, + (short) 0, (short) 0, (short) 0, null, false); + } + + /** + * This method builds a default collation element for an accented character + * without invoking the database created from the rules passed to the constructor. + * + * @param c Character which needs a collation element. + * @return A valid brand new CollationElement instance. + */ + CollationElement getDefaultAccentedElement(char c) + { + int v; + + // Preliminary support for generic accent sorting inversion (I don't know if all + // characters in the range should be sorted backward). This is the place + // to fix this if needed. + if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361)) + v = 0x0361 - ((int) c - 0x02B9); + else + v = (short) c; + return new CollationElement("" + c, (short) 0, + (short) 0, (short) (last_tertiary_value + v), (short) 0, null, false); + } + + /** + * This method returns an instance for <code>CollationElementIterator</code> + * for the specified <code>String</code> under the collation rules for this + * object. + * + * @param source The <code>String</code> to return the + * <code>CollationElementIterator</code> instance for. + * + * @return A <code>CollationElementIterator</code> for the specified + * <code>String</code>. + */ + public CollationElementIterator getCollationElementIterator(String source) + { + return new CollationElementIterator(this, source); + } + + /** + * This method returns an instance of <code>CollationElementIterator</code> + * for the <code>String</code> represented by the specified + * <code>CharacterIterator</code>. + * + * @param source The <code>CharacterIterator</code> with the desired <code>String</code>. + * + * @return A <code>CollationElementIterator</code> for the specified <code>String</code>. + */ + public CollationElementIterator getCollationElementIterator(CharacterIterator source) + { + StringBuffer expand = new StringBuffer(""); + + // Right now we assume that we will read from the beginning of the string. + for (char c = source.first(); + c != CharacterIterator.DONE; + c = source.next()) + decomposeCharacter(c, expand); + + return getCollationElementIterator(expand.toString()); + } + + /** + * This method returns an instance of <code>CollationKey</code> for the + * specified <code>String</code>. The object returned will have a + * more efficient mechanism for its comparison function that could + * provide speed benefits if multiple comparisons are performed, such + * as during a sort. + * + * @param source The <code>String</code> to create a <code>CollationKey</code> for. + * + * @return A <code>CollationKey</code> for the specified <code>String</code>. + */ + public CollationKey getCollationKey(String source) + { + CollationElementIterator cei = getCollationElementIterator(source); + ArrayList vect = new ArrayList(); + + int ord = cei.next(); + cei.reset(); //set to start of string + + while (ord != CollationElementIterator.NULLORDER) + { + // If the primary order is null, it means this is an ignorable + // character. + if (CollationElementIterator.primaryOrder(ord) == 0) + { + ord = cei.next(); + continue; + } + switch (getStrength()) + { + case PRIMARY: + ord = CollationElementIterator.primaryOrder(ord); + break; + + case SECONDARY: + ord = CollationElementIterator.primaryOrder(ord) << 8; + ord |= CollationElementIterator.secondaryOrder(ord); + + default: + break; + } + + vect.add(new Integer(ord)); + ord = cei.next(); //increment to next key + } + + Object[] objarr = vect.toArray(); + byte[] key = new byte[objarr.length * 4]; + + for (int i = 0; i < objarr.length; i++) + { + int j = ((Integer) objarr[i]).intValue(); + key [i * 4] = (byte) ((j & 0xFF000000) >> 24); + key [i * 4 + 1] = (byte) ((j & 0x00FF0000) >> 16); + key [i * 4 + 2] = (byte) ((j & 0x0000FF00) >> 8); + key [i * 4 + 3] = (byte) (j & 0x000000FF); + } + + return new CollationKey(this, source, key); + } + + /** + * This method returns a <code>String</code> containing the collation rules + * for this object. + * + * @return The collation rules for this object. + */ + public String getRules() + { + return rules; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode() + { + return System.identityHashCode(this); + } +} diff --git a/libjava/classpath/java/text/SimpleDateFormat.java b/libjava/classpath/java/text/SimpleDateFormat.java new file mode 100644 index 00000000000..789cb83d86d --- /dev/null +++ b/libjava/classpath/java/text/SimpleDateFormat.java @@ -0,0 +1,1257 @@ +/* SimpleDateFormat.java -- A class for parsing/formating simple + date constructs + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.text.AttributedFormatBuffer; +import gnu.java.text.FormatBuffer; +import gnu.java.text.FormatCharacterIterator; +import gnu.java.text.StringFormatBuffer; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.Locale; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SimpleDateFormat provides convenient methods for parsing and formatting + * dates using Gregorian calendars (see java.util.GregorianCalendar). + */ +public class SimpleDateFormat extends DateFormat +{ + /** + * This class is used by <code>SimpleDateFormat</code> as a + * compiled representation of a format string. The field + * ID, size, and character used are stored for each sequence + * of pattern characters. + */ + private class CompiledField + { + /** + * The ID of the field within the local pattern characters. + * Package private for use in out class. + */ + int field; + + /** + * The size of the character sequence. + * Package private for use in out class. + */ + int size; + + /** + * The character used. + */ + private char character; + + /** + * Constructs a compiled field using the + * the given field ID, size and character + * values. + * + * @param f the field ID. + * @param s the size of the field. + * @param c the character used. + */ + public CompiledField(int f, int s, char c) + { + field = f; + size = s; + character = c; + } + + /** + * Retrieves the ID of the field relative to + * the local pattern characters. + */ + public int getField() + { + return field; + } + + /** + * Retrieves the size of the character sequence. + */ + public int getSize() + { + return size; + } + + /** + * Retrieves the character used in the sequence. + */ + public char getCharacter() + { + return character; + } + + /** + * Returns a <code>String</code> representation + * of the compiled field, primarily for debugging + * purposes. + * + * @return a <code>String</code> representation. + */ + public String toString() + { + StringBuffer builder; + + builder = new StringBuffer(getClass().getName()); + builder.append("[field="); + builder.append(field); + builder.append(", size="); + builder.append(size); + builder.append(", character="); + builder.append(character); + builder.append("]"); + + return builder.toString(); + } + } + + /** + * A list of <code>CompiledField</code>s, + * representing the compiled version of the pattern. + * + * @see CompiledField + * @serial Ignored. + */ + private transient ArrayList tokens; + + /** + * The localised data used in formatting, + * such as the day and month names in the local + * language, and the localized pattern characters. + * + * @see DateFormatSymbols + * @serial The localisation data. May not be null. + */ + private DateFormatSymbols formatData; + + /** + * The date representing the start of the century + * used for interpreting two digit years. For + * example, 24/10/2004 would cause two digit + * years to be interpreted as representing + * the years between 2004 and 2104. + * + * @see get2DigitYearStart() + * @see set2DigitYearStart(java.util.Date) + * @see Date + * @serial The start date of the century for parsing two digit years. + * May not be null. + */ + private Date defaultCenturyStart; + + /** + * The year at which interpretation of two + * digit years starts. + * + * @see get2DigitYearStart() + * @see set2DigitYearStart(java.util.Date) + * @serial Ignored. + */ + private transient int defaultCentury; + + /** + * The non-localized pattern string. This + * only ever contains the pattern characters + * stored in standardChars. Localized patterns + * are translated to this form. + * + * @see applyPattern(String) + * @see applyLocalizedPattern(String) + * @see toPattern() + * @see toLocalizedPattern() + * @serial The non-localized pattern string. May not be null. + */ + private String pattern; + + /** + * The version of serialized data used by this class. + * Version 0 only includes the pattern and formatting + * data. Version 1 adds the start date for interpreting + * two digit years. + * + * @serial This specifies the version of the data being serialized. + * Version 0 (or no version) specifies just <code>pattern</code> + * and <code>formatData</code>. Version 1 adds + * the <code>defaultCenturyStart</code>. This implementation + * always writes out version 1 data. + */ + private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier + + /** + * For compatability. + */ + private static final long serialVersionUID = 4774881970558875024L; + + // This string is specified in the root of the CLDR. We set it here + // rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars() + // since someone could theoretically change those values (though unlikely). + private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ"; + + /** + * Reads the serialized version of this object. + * If the serialized data is only version 0, + * then the date for the start of the century + * for interpreting two digit years is computed. + * The pattern is parsed and compiled following the process + * of reading in the serialized data. + * + * @param stream the object stream to read the data from. + * @throws IOException if an I/O error occurs. + * @throws ClassNotFoundException if the class of the serialized data + * could not be found. + * @throws InvalidObjectException if the pattern is invalid. + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + computeCenturyStart (); + serialVersionOnStream = 1; + } + else + // Ensure that defaultCentury gets set. + set2DigitYearStart(defaultCenturyStart); + + // Set up items normally taken care of by the constructor. + tokens = new ArrayList(); + try + { + compileFormat(pattern); + } + catch (IllegalArgumentException e) + { + throw new InvalidObjectException("The stream pattern was invalid."); + } + } + + /** + * Compiles the supplied non-localized pattern into a form + * from which formatting and parsing can be performed. + * This also detects errors in the pattern, which will + * be raised on later use of the compiled data. + * + * @param pattern the non-localized pattern to compile. + * @throws IllegalArgumentException if the pattern is invalid. + */ + private void compileFormat(String pattern) + { + // Any alphabetical characters are treated as pattern characters + // unless enclosed in single quotes. + + char thisChar; + int pos; + int field; + CompiledField current = null; + + for (int i=0; i<pattern.length(); i++) { + thisChar = pattern.charAt(i); + field = standardChars.indexOf(thisChar); + if (field == -1) { + current = null; + if ((thisChar >= 'A' && thisChar <= 'Z') + || (thisChar >= 'a' && thisChar <= 'z')) { + // Not a valid letter + throw new IllegalArgumentException("Invalid letter " + thisChar + + "encountered at character " + i + + "."); + } else if (thisChar == '\'') { + // Quoted text section; skip to next single quote + pos = pattern.indexOf('\'',i+1); + if (pos == -1) { + throw new IllegalArgumentException("Quotes starting at character " + + i + " not closed."); + } + if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) { + tokens.add(pattern.substring(i+1,pos+1)); + } else { + tokens.add(pattern.substring(i+1,pos)); + } + i = pos; + } else { + // A special character + tokens.add(new Character(thisChar)); + } + } else { + // A valid field + if ((current != null) && (field == current.field)) { + current.size++; + } else { + current = new CompiledField(field,1,thisChar); + tokens.add(current); + } + } + } + } + + /** + * Returns a string representation of this + * class. + * + * @return a string representation of the <code>SimpleDateFormat</code> + * instance. + */ + public String toString() + { + StringBuffer output = new StringBuffer(getClass().getName()); + output.append("[tokens="); + output.append(tokens); + output.append(", formatData="); + output.append(formatData); + output.append(", defaultCenturyStart="); + output.append(defaultCenturyStart); + output.append(", defaultCentury="); + output.append(defaultCentury); + output.append(", pattern="); + output.append(pattern); + output.append(", serialVersionOnStream="); + output.append(serialVersionOnStream); + output.append(", standardChars="); + output.append(standardChars); + output.append("]"); + return output.toString(); + } + + /** + * Constructs a SimpleDateFormat using the default pattern for + * the default locale. + */ + public SimpleDateFormat() + { + /* + * There does not appear to be a standard API for determining + * what the default pattern for a locale is, so use package-scope + * variables in DateFormatSymbols to encapsulate this. + */ + super(); + Locale locale = Locale.getDefault(); + calendar = new GregorianCalendar(locale); + computeCenturyStart(); + tokens = new ArrayList(); + formatData = new DateFormatSymbols(locale); + pattern = (formatData.dateFormats[DEFAULT] + ' ' + + formatData.timeFormats[DEFAULT]); + compileFormat(pattern); + numberFormat = NumberFormat.getInstance(locale); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * Creates a date formatter using the specified non-localized pattern, + * with the default DateFormatSymbols for the default locale. + * + * @param pattern the pattern to use. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern) + { + this(pattern, Locale.getDefault()); + } + + /** + * Creates a date formatter using the specified non-localized pattern, + * with the default DateFormatSymbols for the given locale. + * + * @param pattern the non-localized pattern to use. + * @param locale the locale to use for the formatting symbols. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern, Locale locale) + { + super(); + calendar = new GregorianCalendar(locale); + computeCenturyStart(); + tokens = new ArrayList(); + formatData = new DateFormatSymbols(locale); + compileFormat(pattern); + this.pattern = pattern; + numberFormat = NumberFormat.getInstance(locale); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * Creates a date formatter using the specified non-localized + * pattern. The specified DateFormatSymbols will be used when + * formatting. + * + * @param pattern the non-localized pattern to use. + * @param formatData the formatting symbols to use. + * @throws NullPointerException if the pattern or formatData is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern, DateFormatSymbols formatData) + { + super(); + calendar = new GregorianCalendar(); + computeCenturyStart (); + tokens = new ArrayList(); + if (formatData == null) + throw new NullPointerException("formatData"); + this.formatData = formatData; + compileFormat(pattern); + this.pattern = pattern; + numberFormat = NumberFormat.getInstance(); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. This string is unlocalized. + * + * @return The format string. + */ + public String toPattern() + { + return pattern; + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. This string is localized. + * + * @return The format string. + */ + public String toLocalizedPattern() + { + String localChars = formatData.getLocalPatternChars(); + return translateLocalizedPattern(pattern, standardChars, localChars); + } + + /** + * This method sets the formatting pattern that should be used by this + * object. This string is not localized. + * + * @param pattern The new format pattern. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public void applyPattern(String pattern) + { + tokens = new ArrayList(); + compileFormat(pattern); + this.pattern = pattern; + } + + /** + * This method sets the formatting pattern that should be used by this + * object. This string is localized. + * + * @param pattern The new format pattern. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public void applyLocalizedPattern(String pattern) + { + String localChars = formatData.getLocalPatternChars(); + pattern = translateLocalizedPattern(pattern, localChars, standardChars); + applyPattern(pattern); + } + + /** + * Translates either from or to a localized variant of the pattern + * string. For example, in the German locale, 't' (for 'tag') is + * used instead of 'd' (for 'date'). This method translates + * a localized pattern (such as 'ttt') to a non-localized pattern + * (such as 'ddd'), or vice versa. Non-localized patterns use + * a standard set of characters, which match those of the U.S. English + * locale. + * + * @param pattern the pattern to translate. + * @param oldChars the old set of characters (used in the pattern). + * @param newChars the new set of characters (which will be used in the + * pattern). + * @return a version of the pattern using the characters in + * <code>newChars</code>. + */ + private String translateLocalizedPattern(String pattern, + String oldChars, String newChars) + { + int len = pattern.length(); + StringBuffer buf = new StringBuffer(len); + boolean quoted = false; + for (int i = 0; i < len; i++) + { + char ch = pattern.charAt(i); + if (ch == '\'') + quoted = ! quoted; + if (! quoted) + { + int j = oldChars.indexOf(ch); + if (j >= 0) + ch = newChars.charAt(j); + } + buf.append(ch); + } + return buf.toString(); + } + + /** + * Returns the start of the century used for two digit years. + * + * @return A <code>Date</code> representing the start of the century + * for two digit years. + */ + public Date get2DigitYearStart() + { + return defaultCenturyStart; + } + + /** + * Sets the start of the century used for two digit years. + * + * @param date A <code>Date</code> representing the start of the century for + * two digit years. + */ + public void set2DigitYearStart(Date date) + { + defaultCenturyStart = date; + calendar.clear(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + defaultCentury = year - (year % 100); + } + + /** + * This method returns a copy of the format symbol information used + * for parsing and formatting dates. + * + * @return a copy of the date format symbols. + */ + public DateFormatSymbols getDateFormatSymbols() + { + return (DateFormatSymbols) formatData.clone(); + } + + /** + * This method sets the format symbols information used for parsing + * and formatting dates. + * + * @param formatData The date format symbols. + * @throws NullPointerException if <code>formatData</code> is null. + */ + public void setDateFormatSymbols(DateFormatSymbols formatData) + { + if (formatData == null) + { + throw new + NullPointerException("The supplied format data was null."); + } + this.formatData = formatData; + } + + /** + * This methods tests whether the specified object is equal to this + * object. This will be true if and only if the specified object: + * <p> + * <ul> + * <li>Is not <code>null</code>.</li> + * <li>Is an instance of <code>SimpleDateFormat</code>.</li> + * <li>Is equal to this object at the superclass (i.e., <code>DateFormat</code>) + * level.</li> + * <li>Has the same formatting pattern.</li> + * <li>Is using the same formatting symbols.</li> + * <li>Is using the same century for two digit years.</li> + * </ul> + * + * @param obj The object to compare for equality against. + * + * @return <code>true</code> if the specified object is equal to this object, + * <code>false</code> otherwise. + */ + public boolean equals(Object o) + { + if (!super.equals(o)) + return false; + + if (!(o instanceof SimpleDateFormat)) + return false; + + SimpleDateFormat sdf = (SimpleDateFormat)o; + + if (defaultCentury != sdf.defaultCentury) + return false; + + if (!toPattern().equals(sdf.toPattern())) + return false; + + if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols())) + return false; + + return true; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode() + { + return super.hashCode() ^ toPattern().hashCode() ^ defaultCentury ^ + getDateFormatSymbols().hashCode(); + } + + + /** + * Formats the date input according to the format string in use, + * appending to the specified StringBuffer. The input StringBuffer + * is returned as output for convenience. + */ + private void formatWithAttribute(Date date, FormatBuffer buffer, FieldPosition pos) + { + String temp; + AttributedCharacterIterator.Attribute attribute; + calendar.setTime(date); + + // go through vector, filling in fields where applicable, else toString + Iterator iter = tokens.iterator(); + while (iter.hasNext()) + { + Object o = iter.next(); + if (o instanceof CompiledField) + { + CompiledField cf = (CompiledField) o; + int beginIndex = buffer.length(); + + switch (cf.getField()) + { + case ERA_FIELD: + buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); + break; + case YEAR_FIELD: + // If we have two digits, then we truncate. Otherwise, we + // use the size of the pattern, and zero pad. + buffer.setDefaultAttribute (DateFormat.Field.YEAR); + if (cf.getSize() == 2) + { + temp = "00"+String.valueOf (calendar.get (Calendar.YEAR)); + buffer.append (temp.substring (temp.length() - 2)); + } + else + withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer); + break; + case MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MONTH); + if (cf.getSize() < 3) + withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer); + else if (cf.getSize() < 4) + buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); + else + buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); + break; + case DATE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); + withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer); + break; + case HOUR_OF_DAY1_FIELD: // 1-24 + buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); + withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, + cf.getSize(), buffer); + break; + case HOUR_OF_DAY0_FIELD: // 0-23 + buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0); + withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer); + break; + case MINUTE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MINUTE); + withLeadingZeros (calendar.get (Calendar.MINUTE), + cf.getSize(), buffer); + break; + case SECOND_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.SECOND); + withLeadingZeros(calendar.get (Calendar.SECOND), + cf.getSize(), buffer); + break; + case MILLISECOND_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); + withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer); + break; + case DAY_OF_WEEK_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK); + if (cf.getSize() < 4) + buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]); + else + buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); + break; + case DAY_OF_YEAR_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR); + withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer); + break; + case DAY_OF_WEEK_IN_MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); + withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), + cf.getSize(), buffer); + break; + case WEEK_OF_YEAR_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); + withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), + cf.getSize(), buffer); + break; + case WEEK_OF_MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); + withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), + cf.getSize(), buffer); + break; + case AM_PM_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.AM_PM); + buffer.append (formatData.ampms[calendar.get (Calendar.AM_PM)]); + break; + case HOUR1_FIELD: // 1-12 + buffer.setDefaultAttribute (DateFormat.Field.HOUR1); + withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1, + cf.getSize(), buffer); + break; + case HOUR0_FIELD: // 0-11 + buffer.setDefaultAttribute (DateFormat.Field.HOUR0); + withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer); + break; + case TIMEZONE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); + TimeZone zone = calendar.getTimeZone(); + boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; + // FIXME: XXX: This should be a localized time zone. + String zoneID = zone.getDisplayName + (isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT); + buffer.append (zoneID); + break; + case RFC822_TIMEZONE_FIELD: + buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE); + int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET)) / (1000 * 60); + String sign = (pureMinutes < 0) ? "-" : "+"; + int hours = pureMinutes / 60; + int minutes = pureMinutes % 60; + buffer.append(sign); + withLeadingZeros(hours, 2, buffer); + withLeadingZeros(minutes, 2, buffer); + break; + default: + throw new IllegalArgumentException ("Illegal pattern character " + + cf.getCharacter()); + } + if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() + || cf.getField() == pos.getField())) + { + pos.setBeginIndex(beginIndex); + pos.setEndIndex(buffer.length()); + } + } + else + { + buffer.append(o.toString(), null); + } + } + } + + public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos) + { + formatWithAttribute(date, new StringFormatBuffer (buffer), pos); + + return buffer; + } + + public AttributedCharacterIterator formatToCharacterIterator(Object date) + throws IllegalArgumentException + { + if (date == null) + throw new NullPointerException("null argument"); + if (!(date instanceof Date)) + throw new IllegalArgumentException("argument should be an instance of java.util.Date"); + + AttributedFormatBuffer buf = new AttributedFormatBuffer(); + formatWithAttribute((Date)date, buf, + null); + buf.sync(); + + return new FormatCharacterIterator(buf.getBuffer().toString(), + buf.getRanges(), + buf.getAttributes()); + } + + private void withLeadingZeros(int value, int length, FormatBuffer buffer) + { + String valStr = String.valueOf(value); + for (length -= valStr.length(); length > 0; length--) + buffer.append('0'); + buffer.append(valStr); + } + + private boolean expect(String source, ParsePosition pos, char ch) + { + int x = pos.getIndex(); + boolean r = x < source.length() && source.charAt(x) == ch; + if (r) + pos.setIndex(x + 1); + else + pos.setErrorIndex(x); + return r; + } + + /** + * This method parses the specified string into a date. + * + * @param dateStr The date string to parse. + * @param pos The input and output parse position + * + * @return The parsed date, or <code>null</code> if the string cannot be + * parsed. + */ + public Date parse (String dateStr, ParsePosition pos) + { + int fmt_index = 0; + int fmt_max = pattern.length(); + + calendar.clear(); + boolean saw_timezone = false; + int quote_start = -1; + boolean is2DigitYear = false; + try + { + for (; fmt_index < fmt_max; ++fmt_index) + { + char ch = pattern.charAt(fmt_index); + if (ch == '\'') + { + int index = pos.getIndex(); + if (fmt_index < fmt_max - 1 + && pattern.charAt(fmt_index + 1) == '\'') + { + if (! expect (dateStr, pos, ch)) + return null; + ++fmt_index; + } + else + quote_start = quote_start < 0 ? fmt_index : -1; + continue; + } + + if (quote_start != -1 + || ((ch < 'a' || ch > 'z') + && (ch < 'A' || ch > 'Z'))) + { + if (! expect (dateStr, pos, ch)) + return null; + continue; + } + + // We've arrived at a potential pattern character in the + // pattern. + int fmt_count = 1; + while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) + { + ++fmt_count; + } + + // We might need to limit the number of digits to parse in + // some cases. We look to the next pattern character to + // decide. + boolean limit_digits = false; + if (fmt_index < fmt_max + && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0) + limit_digits = true; + --fmt_index; + + // We can handle most fields automatically: most either are + // numeric or are looked up in a string vector. In some cases + // we need an offset. When numeric, `offset' is added to the + // resulting value. When doing a string lookup, offset is the + // initial index into the string array. + int calendar_field; + boolean is_numeric = true; + int offset = 0; + boolean maybe2DigitYear = false; + boolean oneBasedHour = false; + boolean oneBasedHourOfDay = false; + Integer simpleOffset; + String[] set1 = null; + String[] set2 = null; + switch (ch) + { + case 'd': + calendar_field = Calendar.DATE; + break; + case 'D': + calendar_field = Calendar.DAY_OF_YEAR; + break; + case 'F': + calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH; + break; + case 'E': + is_numeric = false; + offset = 1; + calendar_field = Calendar.DAY_OF_WEEK; + set1 = formatData.getWeekdays(); + set2 = formatData.getShortWeekdays(); + break; + case 'w': + calendar_field = Calendar.WEEK_OF_YEAR; + break; + case 'W': + calendar_field = Calendar.WEEK_OF_MONTH; + break; + case 'M': + calendar_field = Calendar.MONTH; + if (fmt_count <= 2) + offset = -1; + else + { + is_numeric = false; + set1 = formatData.getMonths(); + set2 = formatData.getShortMonths(); + } + break; + case 'y': + calendar_field = Calendar.YEAR; + if (fmt_count <= 2) + maybe2DigitYear = true; + break; + case 'K': + calendar_field = Calendar.HOUR; + break; + case 'h': + calendar_field = Calendar.HOUR; + oneBasedHour = true; + break; + case 'H': + calendar_field = Calendar.HOUR_OF_DAY; + break; + case 'k': + calendar_field = Calendar.HOUR_OF_DAY; + oneBasedHourOfDay = true; + break; + case 'm': + calendar_field = Calendar.MINUTE; + break; + case 's': + calendar_field = Calendar.SECOND; + break; + case 'S': + calendar_field = Calendar.MILLISECOND; + break; + case 'a': + is_numeric = false; + calendar_field = Calendar.AM_PM; + set1 = formatData.getAmPmStrings(); + break; + case 'z': + case 'Z': + // We need a special case for the timezone, because it + // uses a different data structure than the other cases. + is_numeric = false; + calendar_field = Calendar.ZONE_OFFSET; + String[][] zoneStrings = formatData.getZoneStrings(); + int zoneCount = zoneStrings.length; + int index = pos.getIndex(); + boolean found_zone = false; + simpleOffset = computeOffset(dateStr.substring(index), pos); + if (simpleOffset != null) + { + found_zone = true; + saw_timezone = true; + calendar.set(Calendar.DST_OFFSET, 0); + offset = simpleOffset.intValue(); + } + else + { + for (int j = 0; j < zoneCount; j++) + { + String[] strings = zoneStrings[j]; + int k; + for (k = 0; k < strings.length; ++k) + { + if (dateStr.startsWith(strings[k], index)) + break; + } + if (k != strings.length) + { + found_zone = true; + saw_timezone = true; + TimeZone tz = TimeZone.getTimeZone (strings[0]); + // Check if it's a DST zone or ordinary + if(k == 3 || k == 4) + calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings()); + else + calendar.set (Calendar.DST_OFFSET, 0); + offset = tz.getRawOffset (); + pos.setIndex(index + strings[k].length()); + break; + } + } + } + if (! found_zone) + { + pos.setErrorIndex(pos.getIndex()); + return null; + } + break; + default: + pos.setErrorIndex(pos.getIndex()); + return null; + } + + // Compute the value we should assign to the field. + int value; + int index = -1; + if (is_numeric) + { + numberFormat.setMinimumIntegerDigits(fmt_count); + if (limit_digits) + numberFormat.setMaximumIntegerDigits(fmt_count); + if (maybe2DigitYear) + index = pos.getIndex(); + Number n = numberFormat.parse(dateStr, pos); + if (pos == null || ! (n instanceof Long)) + return null; + value = n.intValue() + offset; + } + else if (set1 != null) + { + index = pos.getIndex(); + int i; + boolean found = false; + for (i = offset; i < set1.length; ++i) + { + if (set1[i] != null) + if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(), + index)) + { + found = true; + pos.setIndex(index + set1[i].length()); + break; + } + } + if (!found && set2 != null) + { + for (i = offset; i < set2.length; ++i) + { + if (set2[i] != null) + if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(), + index)) + { + found = true; + pos.setIndex(index + set2[i].length()); + break; + } + } + } + if (!found) + { + pos.setErrorIndex(index); + return null; + } + value = i; + } + else + value = offset; + + if (maybe2DigitYear) + { + // Parse into default century if the numeric year string has + // exactly 2 digits. + int digit_count = pos.getIndex() - index; + if (digit_count == 2) + { + is2DigitYear = true; + value += defaultCentury; + } + } + + // Calendar uses 0-based hours. + // I.e. 00:00 AM is midnight, not 12 AM or 24:00 + if (oneBasedHour && value == 12) + value = 0; + + if (oneBasedHourOfDay && value == 24) + value = 0; + + // Assign the value and move on. + calendar.set(calendar_field, value); + } + + if (is2DigitYear) + { + // Apply the 80-20 heuristic to dermine the full year based on + // defaultCenturyStart. + int year = calendar.get(Calendar.YEAR); + if (calendar.getTime().compareTo(defaultCenturyStart) < 0) + calendar.set(Calendar.YEAR, year + 100); + } + if (! saw_timezone) + { + // Use the real rules to determine whether or not this + // particular time is in daylight savings. + calendar.clear (Calendar.DST_OFFSET); + calendar.clear (Calendar.ZONE_OFFSET); + } + return calendar.getTime(); + } + catch (IllegalArgumentException x) + { + pos.setErrorIndex(pos.getIndex()); + return null; + } + } + + /** + * <p> + * Computes the time zone offset in milliseconds + * relative to GMT, based on the supplied + * <code>String</code> representation. + * </p> + * <p> + * The supplied <code>String</code> must be a three + * or four digit signed number, with an optional 'GMT' + * prefix. The first one or two digits represents the hours, + * while the last two represent the minutes. The + * two sets of digits can optionally be separated by + * ':'. The mandatory sign prefix (either '+' or '-') + * indicates the direction of the offset from GMT. + * </p> + * <p> + * For example, 'GMT+0200' specifies 2 hours after + * GMT, while '-05:00' specifies 5 hours prior to + * GMT. The special case of 'GMT' alone can be used + * to represent the offset, 0. + * </p> + * <p> + * If the <code>String</code> can not be parsed, + * the result will be null. The resulting offset + * is wrapped in an <code>Integer</code> object, in + * order to allow such failure to be represented. + * </p> + * + * @param zoneString a string in the form + * (GMT)? sign hours : minutes + * where sign = '+' or '-', hours + * is a one or two digits representing + * a number between 0 and 23, and + * minutes is two digits representing + * a number between 0 and 59. + * @return the parsed offset, or null if parsing + * failed. + */ + private Integer computeOffset(String zoneString, ParsePosition pos) + { + Pattern pattern = + Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})"); + Matcher matcher = pattern.matcher(zoneString); + + // Match from start, but ignore trailing parts + boolean hasAll = matcher.lookingAt(); + try + { + // Do we have at least the sign, hour and minute? + matcher.group(2); + matcher.group(4); + matcher.group(5); + } + catch (IllegalStateException ise) + { + hasAll = false; + } + if (hasAll) + { + int sign = matcher.group(2).equals("+") ? 1 : -1; + int hour = Integer.parseInt(matcher.group(4)); + if (!matcher.group(3).equals("")) + hour += (Integer.parseInt(matcher.group(3)) * 10); + int minutes = Integer.parseInt(matcher.group(5)); + + if (hour > 23) + return null; + int offset = sign * ((hour * 60) + minutes) * 60000; + + // advance the index + pos.setIndex(pos.getIndex() + matcher.end()); + return new Integer(offset); + } + else if (zoneString.startsWith("GMT")) + { + pos.setIndex(pos.getIndex() + 3); + return new Integer(0); + } + return null; + } + + // Compute the start of the current century as defined by + // get2DigitYearStart. + private void computeCenturyStart() + { + int year = calendar.get(Calendar.YEAR); + calendar.set(Calendar.YEAR, year - 80); + set2DigitYearStart(calendar.getTime()); + } + + /** + * Returns a copy of this instance of + * <code>SimpleDateFormat</code>. The copy contains + * clones of the formatting symbols and the 2-digit + * year century start date. + */ + public Object clone() + { + SimpleDateFormat clone = (SimpleDateFormat) super.clone(); + clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone()); + clone.set2DigitYearStart((Date) defaultCenturyStart.clone()); + return clone; + } + +} diff --git a/libjava/classpath/java/text/StringCharacterIterator.java b/libjava/classpath/java/text/StringCharacterIterator.java new file mode 100644 index 00000000000..e3adc857e51 --- /dev/null +++ b/libjava/classpath/java/text/StringCharacterIterator.java @@ -0,0 +1,356 @@ +/* StringCharacterIterator.java -- Iterate over a character range in a string + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class iterates over a range of characters in a <code>String</code>. + * For a given range of text, a beginning and ending index, + * as well as a current index are defined. These values can be queried + * by the methods in this interface. Additionally, various methods allow + * the index to be set. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public final class StringCharacterIterator implements CharacterIterator +{ + /** + * This is the string to iterate over + */ + private String text; + + /** + * This is the value of the start position of the text range. + */ + private int begin; + + /** + * This is the value of the ending position of the text range. + */ + private int end; + + /** + * This is the current value of the scan index. + */ + private int index; + + /** + * This method initializes a new instance of + * <code>StringCharacterIterator</code> to iterate over the entire + * text of the specified <code>String</code>. The initial index + * value will be set to the first character in the string. + * + * @param text The <code>String</code> to iterate through. + */ + public StringCharacterIterator (String text) + { + this (text, 0, text.length (), 0); + } + + /*************************************************************************/ + + /** + * This method initializes a new instance of + * <code>StringCharacterIterator</code> to iterate over the entire + * text of the specified <code>String</code>. The initial index + * value will be set to the specified value. + * + * @param text The <code>String</code> to iterate through. + * @param index The initial index position. + */ + public StringCharacterIterator (String text, int index) + { + this (text, 0, text.length (), index); + } + + /*************************************************************************/ + + /** + * This method initializes a new instance of + * <code>StringCharacterIterator</code> that iterates over the text + * in a subrange of the specified <code>String</code>. The + * beginning and end of the range are specified by the caller, as is + * the initial index position. + * + * @param text The <code>String</code> to iterate through. + * @param begin The beginning position in the character range. + * @param end The ending position in the character range. + * @param index The initial index position. + * + * @param IllegalArgumentException If any of the range values are + * invalid. + */ + public StringCharacterIterator (String text, int begin, int end, int index) + { + int len = text.length (); + + if ((begin < 0) || (begin > len)) + throw new IllegalArgumentException ("Bad begin position"); + + if ((end < begin) || (end > len)) + throw new IllegalArgumentException ("Bad end position"); + + if ((index < begin) || (index > end)) + throw new IllegalArgumentException ("Bad initial index position"); + + this.text = text; + this.begin = begin; + this.end = end; + this.index = index; + } + + /** + * This is a package level constructor that copies the text out of + * an existing StringCharacterIterator and resets the beginning and + * ending index. + * + * @param scci The StringCharacterIterator to copy the info from + * @param begin The beginning index of the range we are interested in. + * @param end The ending index of the range we are interested in. + */ + StringCharacterIterator (StringCharacterIterator sci, int begin, int end) + { + this (sci.text, begin, end, begin); + } + + /** + * This method returns the character at the current index position + * + * @return The character at the current index position. + */ + public char current () + { + return (index < end) ? text.charAt (index) : DONE; + } + + /*************************************************************************/ + + /** + * This method increments the current index and then returns the + * character at the new index value. If the index is already at + * <code>getEndIndex () - 1</code>, it will not be incremented. + * + * @return The character at the position of the incremented index + * value, or <code>DONE</code> if the index has reached + * getEndIndex () - 1. + */ + public char next () + { + if (index == end) + return DONE; + + ++index; + return current (); + } + + /*************************************************************************/ + + /** + * This method decrements the current index and then returns the + * character at the new index value. If the index value is already + * at the beginning index, it will not be decremented. + * + * @return The character at the position of the decremented index + * value, or <code>DONE</code> if index was already equal to the + * beginning index value. + */ + public char previous () + { + if (index == begin) + return DONE; + + --index; + return current (); + } + + /*************************************************************************/ + + /** + * This method sets the index value to the beginning of the range and returns + * the character there. + * + * @return The character at the beginning of the range, or + * <code>DONE</code> if the range is empty. + */ + public char first () + { + index = begin; + return current (); + } + + /*************************************************************************/ + + /** + * This method sets the index value to <code>getEndIndex () - 1</code> and + * returns the character there. If the range is empty, then the index value + * will be set equal to the beginning index. + * + * @return The character at the end of the range, or + * <code>DONE</code> if the range is empty. + */ + public char last () + { + if (end == begin) + return DONE; + + index = end - 1; + return current (); + } + + /*************************************************************************/ + + /** + * This method returns the current value of the index. + * + * @return The current index value + */ + public int getIndex () + { + return index; + } + + /*************************************************************************/ + + /** + * This method sets the value of the index to the specified value, then + * returns the character at that position. + * + * @param index The new index value. + * + * @return The character at the new index value or <code>DONE</code> + * if the index value is equal to <code>getEndIndex</code>. + * + * @exception IllegalArgumentException If the specified index is not valid + */ + public char setIndex (int index) + { + if ((index < begin) || (index > end)) + throw new IllegalArgumentException ("Bad index specified"); + + this.index = index; + return current (); + } + + /*************************************************************************/ + + /** + * This method returns the character position of the first character in the + * range. + * + * @return The index of the first character in the range. + */ + public int getBeginIndex () + { + return begin; + } + + /*************************************************************************/ + + /** + * This method returns the character position of the end of the text range. + * This will actually be the index of the first character following the + * end of the range. In the event the text range is empty, this will be + * equal to the first character in the range. + * + * @return The index of the end of the range. + */ + public int getEndIndex () + { + return end; + } + + /*************************************************************************/ + + /** + * This method creates a copy of this <code>CharacterIterator</code>. + * + * @return A copy of this <code>CharacterIterator</code>. + */ + public Object clone () + { + return new StringCharacterIterator (text, begin, end, index); + } + + /*************************************************************************/ + + /** + * This method tests this object for equality againt the specified + * object. This will be true if and only if the specified object: + * <p> + * <ul> + * <li>is not <code>null</code>.</li> + * <li>is an instance of <code>StringCharacterIterator</code></li> + * <li>has the same text as this object</li> + * <li>has the same beginning, ending, and current index as this object.</li> + * </ul> + * + * @param obj The object to test for equality against. + * + * @return <code>true</code> if the specified object is equal to this + * object, <code>false</code> otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof StringCharacterIterator)) + return false; + + StringCharacterIterator sci = (StringCharacterIterator) obj; + + return (begin == sci.begin + && end == sci.end + && index == sci.index + && text.equals (sci.text)); + } + + /*************************************************************************/ + + /** + * This method allows other classes in java.text to change the value + * of the underlying text being iterated through. + * + * @param text The new <code>String</code> to iterate through. + */ + public void setText (String text) + { + this.text = text; + this.begin = 0; + this.end = text.length (); + this.index = 0; + } +} diff --git a/libjava/classpath/java/text/class-dependencies.conf b/libjava/classpath/java/text/class-dependencies.conf new file mode 100644 index 00000000000..011b146ce10 --- /dev/null +++ b/libjava/classpath/java/text/class-dependencies.conf @@ -0,0 +1,220 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + +# end of file + +# All locales supported are loaded via classes from java.text (see below) +# from class gnu/java/locale/LocaleInformation_<locale_id> +# +# This introduces a dependency for all locales. To allow an easy selection +# and addition of locales, the library variable {text_locales} can be set to +# the set of supported locales. +# + +{text_locales}: \ + af_ZA \ + ar_AE \ + ar_BH \ + ar_DZ \ + ar_EG \ + ar_IN \ + ar_IQ \ + ar_JO \ + ar_KW \ + ar_LB \ + ar_LY \ + ar_MA \ + ar_OM \ + ar_QA \ + ar_SD \ + ar_SY \ + ar_TN \ + ar_YE \ + be_BY \ + bn_IN \ + br_FR \ + bs_BA \ + ca_ES \ + cs_CZ \ + cy_GB \ + da_DK \ + de \ + de_AT \ + de_BE \ + de_CH \ + de_DE \ + de_LU \ + el_GR \ + en \ + en_AU \ + en_BW \ + en_CA \ + en_DK \ + en_GB \ + en_HK \ + en_IE \ + en_IN \ + en_NZ \ + en_PH \ + en_SG \ + en_US \ + en_ZA \ + en_ZW \ + es_AR \ + es_BO \ + es_CL \ + es_CO \ + es_CR \ + es_DO \ + es_EC \ + es_ES \ + es_GT \ + es_HN \ + es_MX \ + es_NI \ + es_PA \ + es_PE \ + es_PR \ + es_PY \ + es_SV \ + es_US \ + es_UY \ + es_VE \ + et_EE \ + eu_ES \ + fa_IR \ + fi_FI \ + fo_FO \ + fr_BE \ + fr_CA \ + fr_CH \ + fr_FR \ + fr_LU \ + ga_IE \ + gd_GB \ + gl_ES \ + gv_GB \ + he_IL \ + hi_IN \ + hr_HR \ + hu_HU \ + id_ID \ + it_CH \ + it_IT \ + iw_IL \ + ja_JP \ + ka_GE \ + kl_GL \ + ko_KR \ + kw_GB \ + lt_LT \ + lv_LV \ + mi_NZ \ + mk_MK \ + mr_IN \ + mt_MT \ + nl \ + nl_BE \ + nl_NL \ + nn_NO \ + no_NO \ + oc_FR \ + pl_PL \ + pt_BR \ + pt_PT \ + ro_RO \ + ru_RU \ + ru_UA \ + se_NO \ + sk_SK \ + sl_SI \ + sq_AL \ + sr_YU \ + sv_FI \ + sv_SE \ + ta_IN \ + te_IN \ + tg_TJ \ + tl_PH \ + tr_TR \ + uk_UA \ + ur_PK \ + uz_UZ \ + vi_VN \ + yi_US \ + zh_CN \ + zh_HK \ + zh_SG \ + zh_TW + +java/text/Collator.getInstance(Ljava/util/Locale;)Ljava/text/Collator;: \ + gnu/java/locale/LocaleInformation_{text_locales}.* + +java/text/DateFormatSymbols.<init>(Ljava/util/Locale;)V: \ + gnu/java/locale/LocaleInformation_{text_locales}.* + +java/text/DecimalFormatSymbols.<init>(Ljava/util/Locale;)V: \ + gnu/java/locale/LocaleInformation_{text_locales}.* + +java/text/BreakIterator.getInstance(Ljava/lang/String;Ljava/util/Locale;)Ljava/text/BreakIterator;: \ + gnu/java/locale/LocaleInformation_{text_locales}.* + +java/text/NumberFormat.computeInstance(Ljava/util/Locale;Ljava/lang/String;Ljava/lang/String;)Ljava/text/NumberFormat;: \ + gnu/java/locale/LocaleInformation_{text_locales}.* + +java/text/DateFormat.computeInstance(IILjava/util/Locale;ZZ)Ljava/text/DateFormat;: \ + gnu/java/locale/LocaleInformation_{text_locales}.* diff --git a/libjava/classpath/java/text/package.html b/libjava/classpath/java/text/package.html new file mode 100644 index 00000000000..3c2e22ba5e4 --- /dev/null +++ b/libjava/classpath/java/text/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.text package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.text</title></head> + +<body> +<p>Classes to iterate over strings and to format texts according to a +specific locale.</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/.cvsignore b/libjava/classpath/java/util/.cvsignore new file mode 100644 index 00000000000..d41ae8d8147 --- /dev/null +++ b/libjava/classpath/java/util/.cvsignore @@ -0,0 +1 @@ +LocaleData.java diff --git a/libjava/classpath/java/util/AbstractCollection.java b/libjava/classpath/java/util/AbstractCollection.java new file mode 100644 index 00000000000..00ee23ebd43 --- /dev/null +++ b/libjava/classpath/java/util/AbstractCollection.java @@ -0,0 +1,470 @@ +/* AbstractCollection.java -- Abstract implementation of most of Collection + Copyright (C) 1998, 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.lang.reflect.Array; + +/** + * A basic implementation of most of the methods in the Collection interface to + * make it easier to create a collection. To create an unmodifiable Collection, + * just subclass AbstractCollection and provide implementations of the + * iterator() and size() methods. The Iterator returned by iterator() need only + * provide implementations of hasNext() and next() (that is, it may throw an + * UnsupportedOperationException if remove() is called). To create a modifiable + * Collection, you must in addition provide an implementation of the + * add(Object) method and the Iterator returned by iterator() must provide an + * implementation of remove(). Other methods should be overridden if the + * backing data structure allows for a more efficient implementation. The + * precise implementation used by AbstractCollection is documented, so that + * subclasses can tell which methods could be implemented more efficiently. + * <p> + * + * The programmer should provide a no-argument constructor, and one that + * accepts another Collection, as recommended by the Collection interface. + * Unfortunately, there is no way to enforce this in Java. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see AbstractSet + * @see AbstractList + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractCollection implements Collection +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractCollection() + { + } + + /** + * Return an Iterator over this collection. The iterator must provide the + * hasNext and next methods and should in addition provide remove if the + * collection is modifiable. + * + * @return an iterator + */ + public abstract Iterator iterator(); + + /** + * Return the number of elements in this collection. If there are more than + * Integer.MAX_VALUE elements, return Integer.MAX_VALUE. + * + * @return the size + */ + public abstract int size(); + + /** + * Add an object to the collection (optional operation). This implementation + * always throws an UnsupportedOperationException - it should be + * overridden if the collection is to be modifiable. If the collection + * does not accept duplicates, simply return false. Collections may specify + * limitations on what may be added. + * + * @param o the object to add + * @return true if the add operation caused the Collection to change + * @throws UnsupportedOperationException if the add operation is not + * supported on this collection + * @throws NullPointerException if the collection does not support null + * @throws ClassCastException if the object is of the wrong type + * @throws IllegalArgumentException if some aspect of the object prevents + * it from being added + */ + public boolean add(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Add all the elements of a given collection to this collection (optional + * operation). This implementation obtains an Iterator over the given + * collection and iterates over it, adding each element with the + * add(Object) method (thus this method will fail with an + * UnsupportedOperationException if the add method does). The behavior is + * unspecified if the specified collection is modified during the iteration, + * including the special case of trying addAll(this) on a non-empty + * collection. + * + * @param c the collection to add the elements of to this collection + * @return true if the add operation caused the Collection to change + * @throws UnsupportedOperationException if the add operation is not + * supported on this collection + * @throws NullPointerException if the specified collection is null + * @throws ClassCastException if the type of any element in c is + * not a valid type for addition. + * @throws IllegalArgumentException if some aspect of any element + * in c prevents it being added. + * @throws NullPointerException if any element in c is null and this + * collection doesn't allow null values. + * @see #add(Object) + */ + public boolean addAll(Collection c) + { + Iterator itr = c.iterator(); + boolean modified = false; + int pos = c.size(); + while (--pos >= 0) + modified |= add(itr.next()); + return modified; + } + + /** + * Remove all elements from the collection (optional operation). This + * implementation obtains an iterator over the collection and calls next + * and remove on it repeatedly (thus this method will fail with an + * UnsupportedOperationException if the Iterator's remove method does) + * until there are no more elements to remove. + * Many implementations will have a faster way of doing this. + * + * @throws UnsupportedOperationException if the Iterator returned by + * iterator does not provide an implementation of remove + * @see Iterator#remove() + */ + public void clear() + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + { + itr.next(); + itr.remove(); + } + } + + /** + * Test whether this collection contains a given object. That is, if the + * collection has an element e such that (o == null ? e == null : + * o.equals(e)). This implementation obtains an iterator over the collection + * and iterates over it, testing each element for equality with the given + * object. If it is equal, true is returned. Otherwise false is returned when + * the end of the collection is reached. + * + * @param o the object to remove from this collection + * @return true if this collection contains an object equal to o + */ + public boolean contains(Object o) + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(o, itr.next())) + return true; + return false; + } + + /** + * Tests whether this collection contains all the elements in a given + * collection. This implementation iterates over the given collection, + * testing whether each element is contained in this collection. If any one + * is not, false is returned. Otherwise true is returned. + * + * @param c the collection to test against + * @return true if this collection contains all the elements in the given + * collection + * @throws NullPointerException if the given collection is null + * @see #contains(Object) + */ + public boolean containsAll(Collection c) + { + Iterator itr = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (!contains(itr.next())) + return false; + return true; + } + + /** + * Test whether this collection is empty. This implementation returns + * size() == 0. + * + * @return true if this collection is empty. + * @see #size() + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * Remove a single instance of an object from this collection (optional + * operation). That is, remove one element e such that + * <code>(o == null ? e == null : o.equals(e))</code>, if such an element + * exists. This implementation obtains an iterator over the collection + * and iterates over it, testing each element for equality with the given + * object. If it is equal, it is removed by the iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). After the first element has been + * removed, true is returned; if the end of the collection is reached, false + * is returned. + * + * @param o the object to remove from this collection + * @return true if the remove operation caused the Collection to change, or + * equivalently if the collection did contain o. + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @see Iterator#remove() + */ + public boolean remove(Object o) + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(o, itr.next())) + { + itr.remove(); + return true; + } + return false; + } + + /** + * Remove from this collection all its elements that are contained in a given + * collection (optional operation). This implementation iterates over this + * collection, and for each element tests if it is contained in the given + * collection. If so, it is removed by the Iterator's remove method (thus + * this method will fail with an UnsupportedOperationException if the + * Iterator's remove method does). + * + * @param c the collection to remove the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + public boolean removeAll(Collection c) + { + return removeAllInternal(c); + } + + /** + * Remove from this collection all its elements that are contained in a given + * collection (optional operation). This implementation iterates over this + * collection, and for each element tests if it is contained in the given + * collection. If so, it is removed by the Iterator's remove method (thus + * this method will fail with an UnsupportedOperationException if the + * Iterator's remove method does). This method is necessary for ArrayList, + * which cannot publicly override removeAll but can optimize this call. + * + * @param c the collection to remove the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + // Package visible for use throughout java.util. + boolean removeAllInternal(Collection c) + { + Iterator itr = iterator(); + boolean modified = false; + int pos = size(); + while (--pos >= 0) + if (c.contains(itr.next())) + { + itr.remove(); + modified = true; + } + return modified; + } + + /** + * Remove from this collection all its elements that are not contained in a + * given collection (optional operation). This implementation iterates over + * this collection, and for each element tests if it is contained in the + * given collection. If not, it is removed by the Iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). + * + * @param c the collection to retain the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + public boolean retainAll(Collection c) + { + return retainAllInternal(c); + } + + /** + * Remove from this collection all its elements that are not contained in a + * given collection (optional operation). This implementation iterates over + * this collection, and for each element tests if it is contained in the + * given collection. If not, it is removed by the Iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). This method is necessary for + * ArrayList, which cannot publicly override retainAll but can optimize + * this call. + * + * @param c the collection to retain the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + // Package visible for use throughout java.util. + boolean retainAllInternal(Collection c) + { + Iterator itr = iterator(); + boolean modified = false; + int pos = size(); + while (--pos >= 0) + if (!c.contains(itr.next())) + { + itr.remove(); + modified = true; + } + return modified; + } + + /** + * Return an array containing the elements of this collection. This + * implementation creates an Object array of size size() and then iterates + * over the collection, setting each element of the array from the value + * returned by the iterator. The returned array is safe, and is not backed + * by the collection. + * + * @return an array containing the elements of this collection + */ + public Object[] toArray() + { + Iterator itr = iterator(); + int size = size(); + Object[] a = new Object[size]; + for (int pos = 0; pos < size; pos++) + a[pos] = itr.next(); + return a; + } + + /** + * Copy the collection into a given array if it will fit, or into a + * dynamically created array of the same run-time type as the given array if + * not. If there is space remaining in the array, the first element after the + * end of the collection is set to null (this is only useful if the + * collection is known to contain no null elements, however). This + * implementation first tests whether the given array is large enough to hold + * all the elements of the collection. If not, the reflection API is used to + * allocate a new array of the same run-time type. Next an iterator is + * obtained over the collection and the elements are placed in the array as + * they are returned by the iterator. Finally the first spare element, if + * any, of the array is set to null, and the created array is returned. + * The returned array is safe; it is not backed by the collection. Note that + * null may not mark the last element, if the collection allows null + * elements. + * + * @param a the array to copy into, or of the correct run-time type + * @return the array that was produced + * @throws NullPointerException if the given array is null + * @throws ArrayStoreException if the type of the array precludes holding + * one of the elements of the Collection + */ + public Object[] toArray(Object[] a) + { + int size = size(); + if (a.length < size) + a = (Object[]) Array.newInstance(a.getClass().getComponentType(), + size); + else if (a.length > size) + a[size] = null; + + Iterator itr = iterator(); + for (int pos = 0; pos < size; pos++) + a[pos] = itr.next(); + + return a; + } + + /** + * Creates a String representation of the Collection. The string returned is + * of the form "[a, b, ...]" where a and b etc are the results of calling + * toString on the elements of the collection. This implementation obtains an + * Iterator over the Collection and adds each element to a StringBuffer as it + * is returned by the iterator. + * + * @return a String representation of the Collection + */ + public String toString() + { + Iterator itr = iterator(); + StringBuffer r = new StringBuffer("["); + for (int pos = size(); pos > 0; pos--) + { + r.append(itr.next()); + if (pos > 1) + r.append(", "); + } + r.append("]"); + return r.toString(); + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == null ? o2 == null : o1.equals(o2) + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } +} diff --git a/libjava/classpath/java/util/AbstractList.java b/libjava/classpath/java/util/AbstractList.java new file mode 100644 index 00000000000..8a9b6f10d7a --- /dev/null +++ b/libjava/classpath/java/util/AbstractList.java @@ -0,0 +1,1225 @@ +/* AbstractList.java -- Abstract implementation of most of List + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A basic implementation of most of the methods in the List interface to make + * it easier to create a List based on a random-access data structure. If + * the list is sequential (such as a linked list), use AbstractSequentialList. + * To create an unmodifiable list, it is only necessary to override the + * size() and get(int) methods (this contrasts with all other abstract + * collection classes which require an iterator to be provided). To make the + * list modifiable, the set(int, Object) method should also be overridden, and + * to make the list resizable, the add(int, Object) and remove(int) methods + * should be overridden too. Other methods should be overridden if the + * backing data structure allows for a more efficient implementation. + * The precise implementation used by AbstractList is documented, so that + * subclasses can tell which methods could be implemented more efficiently. + * <p> + * + * As recommended by Collection and List, the subclass should provide at + * least a no-argument and a Collection constructor. This class is not + * synchronized. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see AbstractSequentialList + * @see AbstractCollection + * @see ListIterator + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractList extends AbstractCollection implements List +{ + /** + * A count of the number of structural modifications that have been made to + * the list (that is, insertions and removals). Structural modifications + * are ones which change the list size or affect how iterations would + * behave. This field is available for use by Iterator and ListIterator, + * in order to throw a {@link ConcurrentModificationException} in response + * to the next operation on the iterator. This <i>fail-fast</i> behavior + * saves the user from many subtle bugs otherwise possible from concurrent + * modification during iteration. + * <p> + * + * To make lists fail-fast, increment this field by just 1 in the + * <code>add(int, Object)</code> and <code>remove(int)</code> methods. + * Otherwise, this field may be ignored. + */ + protected transient int modCount; + + /** + * The main constructor, for use by subclasses. + */ + protected AbstractList() + { + } + + /** + * Returns the elements at the specified position in the list. + * + * @param index the element to return + * @return the element at that position + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public abstract Object get(int index); + + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. This implementation + * always throws UnsupportedOperationException, and must be overridden to + * make a modifiable List. If you want fail-fast iterators, be sure to + * increment modCount when overriding this. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @see #modCount + */ + public void add(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Add an element to the end of the list (optional operation). If the list + * imposes restraints on what can be inserted, such as no null elements, + * this should be documented. This implementation calls + * <code>add(size(), o);</code>, and will fail if that version does. + * + * @param o the object to add + * @return true, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @see #add(int, Object) + */ + public boolean add(Object o) + { + add(size(), o); + return true; + } + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). This implementation uses the iterator of + * the collection, repeatedly calling add(int, Object); this will fail + * if add does. This can often be made more efficient. + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @see #add(int, Object) + */ + public boolean addAll(int index, Collection c) + { + Iterator itr = c.iterator(); + int size = c.size(); + for (int pos = size; pos > 0; pos--) + add(index++, itr.next()); + return size > 0; + } + + /** + * Clear the list, such that a subsequent call to isEmpty() would return + * true (optional operation). This implementation calls + * <code>removeRange(0, size())</code>, so it will fail unless remove + * or removeRange is overridden. + * + * @throws UnsupportedOperationException if this list does not support the + * clear operation + * @see #remove(int) + * @see #removeRange(int, int) + */ + public void clear() + { + removeRange(0, size()); + } + + /** + * Test whether this list is equal to another object. A List is defined to be + * equal to an object if and only if that object is also a List, and the two + * lists have the same sequence. Two lists l1 and l2 are equal if and only + * if <code>l1.size() == l2.size()</code>, and for every integer n between 0 + * and <code>l1.size() - 1</code> inclusive, <code>l1.get(n) == null ? + * l2.get(n) == null : l1.get(n).equals(l2.get(n))</code>. + * <p> + * + * This implementation returns true if the object is this, or false if the + * object is not a List. Otherwise, it iterates over both lists (with + * iterator()), returning false if two elements compare false or one list + * is shorter, and true if the iteration completes successfully. + * + * @param o the object to test for equality with this list + * @return true if o is equal to this list + * @see Object#equals(Object) + * @see #hashCode() + */ + public boolean equals(Object o) + { + if (o == this) + return true; + if (! (o instanceof List)) + return false; + int size = size(); + if (size != ((List) o).size()) + return false; + + Iterator itr1 = iterator(); + Iterator itr2 = ((List) o).iterator(); + + while (--size >= 0) + if (! equals(itr1.next(), itr2.next())) + return false; + return true; + } + + /** + * Obtains a hash code for this list. In order to obey the general + * contract of the hashCode method of class Object, this value is + * calculated as follows: + * +<pre>hashCode = 1; +Iterator i = list.iterator(); +while (i.hasNext()) +{ + Object obj = i.next(); + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); +}</pre> + * + * This ensures that the general contract of Object.hashCode() is adhered to. + * + * @return the hash code of this list + * + * @see Object#hashCode() + * @see #equals(Object) + */ + public int hashCode() + { + int hashCode = 1; + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + hashCode = 31 * hashCode + hashCode(itr.next()); + return hashCode; + } + + /** + * Obtain the first index at which a given object is to be found in this + * list. This implementation follows a listIterator() until a match is found, + * or returns -1 if the list end is reached. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index + */ + public int indexOf(Object o) + { + ListIterator itr = listIterator(); + int size = size(); + for (int pos = 0; pos < size; pos++) + if (equals(o, itr.next())) + return pos; + return -1; + } + + /** + * Obtain an Iterator over this list, whose sequence is the list order. + * This implementation uses size(), get(int), and remove(int) of the + * backing list, and does not support remove unless the list does. This + * implementation is fail-fast if you correctly maintain modCount. + * Also, this implementation is specified by Sun to be distinct from + * listIterator, although you could easily implement it as + * <code>return listIterator(0)</code>. + * + * @return an Iterator over the elements of this list, in order + * @see #modCount + */ + public Iterator iterator() + { + // Bah, Sun's implementation forbids using listIterator(0). + return new Iterator() + { + private int pos = 0; + private int size = size(); + private int last = -1; + private int knownMod = modCount; + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public boolean hasNext() + { + checkMod(); + return pos < size; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are + * no more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public Object next() + { + checkMod(); + if (pos == size) + throw new NoSuchElementException(); + last = pos; + return get(pos++); + } + + /** + * Removes the last object retrieved by <code>next()</code> + * from the list, if the list supports object removal. + * + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + public void remove() + { + checkMod(); + if (last < 0) + throw new IllegalStateException(); + AbstractList.this.remove(last); + pos--; + size--; + last = -1; + knownMod = modCount; + } + }; + } + + /** + * Obtain the last index at which a given object is to be found in this + * list. This implementation grabs listIterator(size()), then searches + * backwards for a match or returns -1. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index + */ + public int lastIndexOf(Object o) + { + int pos = size(); + ListIterator itr = listIterator(pos); + while (--pos >= 0) + if (equals(o, itr.previous())) + return pos; + return -1; + } + + /** + * Obtain a ListIterator over this list, starting at the beginning. This + * implementation returns listIterator(0). + * + * @return a ListIterator over the elements of this list, in order, starting + * at the beginning + */ + public ListIterator listIterator() + { + return listIterator(0); + } + + /** + * Obtain a ListIterator over this list, starting at a given position. + * A first call to next() would return the same as get(index), and a + * first call to previous() would return the same as get(index - 1). + * <p> + * + * This implementation uses size(), get(int), set(int, Object), + * add(int, Object), and remove(int) of the backing list, and does not + * support remove, set, or add unless the list does. This implementation + * is fail-fast if you correctly maintain modCount. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return a ListIterator over the elements of this list, in order, starting + * at index + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @see #modCount + */ + public ListIterator listIterator(final int index) + { + if (index < 0 || index > size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + + return new ListIterator() + { + private int knownMod = modCount; + private int position = index; + private int lastReturned = -1; + private int size = size(); + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public boolean hasNext() + { + checkMod(); + return position < size; + } + + /** + * Tests to see if there are objects prior to the + * current position in the list. + * + * @return True if objects exist prior to the current + * position of the iterator. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public boolean hasPrevious() + { + checkMod(); + return position > 0; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public Object next() + { + checkMod(); + if (position == size) + throw new NoSuchElementException(); + lastReturned = position; + return get(position++); + } + + /** + * Retrieves the previous object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * previous objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public Object previous() + { + checkMod(); + if (position == 0) + throw new NoSuchElementException(); + lastReturned = --position; + return get(lastReturned); + } + + /** + * Returns the index of the next element in the + * list, which will be retrieved by <code>next()</code> + * + * @return The index of the next element. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public int nextIndex() + { + checkMod(); + return position; + } + + /** + * Returns the index of the previous element in the + * list, which will be retrieved by <code>previous()</code> + * + * @return The index of the previous element. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public int previousIndex() + { + checkMod(); + return position - 1; + } + + /** + * Removes the last object retrieved by <code>next()</code> + * or <code>previous()</code> from the list, if the list + * supports object removal. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void remove() + { + checkMod(); + if (lastReturned < 0) + throw new IllegalStateException(); + AbstractList.this.remove(lastReturned); + size--; + position = lastReturned; + lastReturned = -1; + knownMod = modCount; + } + + /** + * Replaces the last object retrieved by <code>next()</code> + * or <code>previous</code> with o, if the list supports object + * replacement and an add or remove operation has not already + * been performed. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list doesn't support + * the addition or removal of elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void set(Object o) + { + checkMod(); + if (lastReturned < 0) + throw new IllegalStateException(); + AbstractList.this.set(lastReturned, o); + } + + /** + * Adds the supplied object before the element that would be returned + * by a call to <code>next()</code>, if the list supports addition. + * + * @param o The object to add to the list. + * @throws UnsupportedOperationException if the list doesn't support + * the addition of new elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void add(Object o) + { + checkMod(); + AbstractList.this.add(position++, o); + size++; + lastReturned = -1; + knownMod = modCount; + } + }; + } + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. This + * implementation always throws an UnsupportedOperationException. + * If you want fail-fast iterators, be sure to increment modCount when + * overriding this. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @see #modCount + */ + public Object remove(int index) + { + throw new UnsupportedOperationException(); + } + + /** + * Remove a subsection of the list. This is called by the clear and + * removeRange methods of the class which implements subList, which are + * difficult for subclasses to override directly. Therefore, this method + * should be overridden instead by the more efficient implementation, if one + * exists. Overriding this can reduce quadratic efforts to constant time + * in some cases! + * <p> + * + * This implementation first checks for illegal or out of range arguments. It + * then obtains a ListIterator over the list using listIterator(fromIndex). + * It then calls next() and remove() on this iterator repeatedly, toIndex - + * fromIndex times. + * + * @param fromIndex the index, inclusive, to remove from. + * @param toIndex the index, exclusive, to remove to. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + protected void removeRange(int fromIndex, int toIndex) + { + ListIterator itr = listIterator(fromIndex); + for (int index = fromIndex; index < toIndex; index++) + { + itr.next(); + itr.remove(); + } + } + + /** + * Replace an element of this list with another object (optional operation). + * This implementation always throws an UnsupportedOperationException. + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + */ + public Object set(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + * <p> + * + * This implementation returns a subclass of AbstractList. It stores, in + * private fields, the offset and size of the sublist, and the expected + * modCount of the backing list. If the backing list implements RandomAccess, + * the sublist will also. + * <p> + * + * The subclass's <code>set(int, Object)</code>, <code>get(int)</code>, + * <code>add(int, Object)</code>, <code>remove(int)</code>, + * <code>addAll(int, Collection)</code> and + * <code>removeRange(int, int)</code> methods all delegate to the + * corresponding methods on the backing abstract list, after + * bounds-checking the index and adjusting for the offset. The + * <code>addAll(Collection c)</code> method merely returns addAll(size, c). + * The <code>listIterator(int)</code> method returns a "wrapper object" + * over a list iterator on the backing list, which is created with the + * corresponding method on the backing list. The <code>iterator()</code> + * method merely returns listIterator(), and the <code>size()</code> method + * merely returns the subclass's size field. + * <p> + * + * All methods first check to see if the actual modCount of the backing + * list is equal to its expected value, and throw a + * ConcurrentModificationException if it is not. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() + * @throws IllegalArgumentException if fromIndex > toIndex + * @see ConcurrentModificationException + * @see RandomAccess + */ + public List subList(int fromIndex, int toIndex) + { + // This follows the specification of AbstractList, but is inconsistent + // with the one in List. Don't you love Sun's inconsistencies? + if (fromIndex > toIndex) + throw new IllegalArgumentException(fromIndex + " > " + toIndex); + if (fromIndex < 0 || toIndex > size()) + throw new IndexOutOfBoundsException(); + + if (this instanceof RandomAccess) + return new RandomAccessSubList(this, fromIndex, toIndex); + return new SubList(this, fromIndex, toIndex); + } + + /** + * This class follows the implementation requirements set forth in + * {@link AbstractList#subList(int, int)}. It matches Sun's implementation + * by using a non-public top-level class in the same package. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SubList extends AbstractList + { + // Package visible, for use by iterator. + /** The original list. */ + final AbstractList backingList; + /** The index of the first element of the sublist. */ + final int offset; + /** The size of the sublist. */ + int size; + + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + SubList(AbstractList backing, int fromIndex, int toIndex) + { + backingList = backing; + modCount = backing.modCount; + offset = fromIndex; + size = toIndex - fromIndex; + } + + /** + * This method checks the two modCount fields to ensure that there has + * not been a concurrent modification, returning if all is okay. + * + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + // This can be inlined. Package visible, for use by iterator. + void checkMod() + { + if (modCount != backingList.modCount) + throw new ConcurrentModificationException(); + } + + /** + * This method checks that a value is between 0 and size (inclusive). If + * it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + // This will get inlined, since it is private. + private void checkBoundsInclusive(int index) + { + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * This method checks that a value is between 0 (inclusive) and size + * (exclusive). If it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + // This will get inlined, since it is private. + private void checkBoundsExclusive(int index) + { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Specified by AbstractList.subList to return the private field size. + * + * @return the sublist size + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + public int size() + { + checkMod(); + return size; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to modify + * @param o the new value + * @return the old value + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if the backing list does not + * support the set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to the backing list due + * to its type + * @throws IllegalArgumentException if o cannot be added to the backing list + * for some other reason + */ + public Object set(int index, Object o) + { + checkMod(); + checkBoundsExclusive(index); + return backingList.set(index + offset, o); + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to get from + * @return the object at that location + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + checkMod(); + checkBoundsExclusive(index); + return backingList.get(index + offset); + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to insert at + * @param o the object to add + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if the backing list does not + * support the add operation. + * @throws ClassCastException if o cannot be added to the backing list due + * to its type. + * @throws IllegalArgumentException if o cannot be added to the backing + * list for some other reason. + */ + public void add(int index, Object o) + { + checkMod(); + checkBoundsInclusive(index); + backingList.add(index + offset, o); + size++; + modCount = backingList.modCount; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to remove + * @return the removed object + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws UnsupportedOperationException if the backing list does not + * support the remove operation + */ + public Object remove(int index) + { + checkMod(); + checkBoundsExclusive(index); + Object o = backingList.remove(index + offset); + size--; + modCount = backingList.modCount; + return o; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * This does no bounds checking, as it assumes it will only be called + * by trusted code like clear() which has already checked the bounds. + * + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if the backing list does + * not support removing elements. + */ + protected void removeRange(int fromIndex, int toIndex) + { + checkMod(); + + backingList.removeRange(offset + fromIndex, offset + toIndex); + size -= toIndex - fromIndex; + modCount = backingList.modCount; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to insert at + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) + { + checkMod(); + checkBoundsInclusive(index); + int csize = c.size(); + boolean result = backingList.addAll(offset + index, c); + size += csize; + modCount = backingList.modCount; + return result; + } + + /** + * Specified by AbstractList.subList to return addAll(size, c). + * + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Specified by AbstractList.subList to return listIterator(). + * + * @return an iterator over the sublist + */ + public Iterator iterator() + { + return listIterator(); + } + + /** + * Specified by AbstractList.subList to return a wrapper around the + * backing list's iterator. + * + * @param index the start location of the iterator + * @return a list iterator over the sublist + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if the value is out of range + */ + public ListIterator listIterator(final int index) + { + checkMod(); + checkBoundsInclusive(index); + + return new ListIterator() + { + private final ListIterator i = backingList.listIterator(index + offset); + private int position = index; + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public boolean hasNext() + { + checkMod(); + return position < size; + } + + /** + * Tests to see if there are objects prior to the + * current position in the list. + * + * @return True if objects exist prior to the current + * position of the iterator. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public boolean hasPrevious() + { + checkMod(); + return position > 0; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public Object next() + { + if (position == size) + throw new NoSuchElementException(); + position++; + return i.next(); + } + + /** + * Retrieves the previous object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * previous objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public Object previous() + { + if (position == 0) + throw new NoSuchElementException(); + position--; + return i.previous(); + } + + /** + * Returns the index of the next element in the + * list, which will be retrieved by <code>next()</code> + * + * @return The index of the next element. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public int nextIndex() + { + return i.nextIndex() - offset; + } + + /** + * Returns the index of the previous element in the + * list, which will be retrieved by <code>previous()</code> + * + * @return The index of the previous element. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public int previousIndex() + { + return i.previousIndex() - offset; + } + + /** + * Removes the last object retrieved by <code>next()</code> + * from the list, if the list supports object removal. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + public void remove() + { + i.remove(); + size--; + position = nextIndex(); + modCount = backingList.modCount; + } + + + /** + * Replaces the last object retrieved by <code>next()</code> + * or <code>previous</code> with o, if the list supports object + * replacement and an add or remove operation has not already + * been performed. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list doesn't support + * the addition or removal of elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void set(Object o) + { + i.set(o); + } + + /** + * Adds the supplied object before the element that would be returned + * by a call to <code>next()</code>, if the list supports addition. + * + * @param o The object to add to the list. + * @throws UnsupportedOperationException if the list doesn't support + * the addition of new elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void add(Object o) + { + i.add(o); + size++; + position++; + modCount = backingList.modCount; + } + + // Here is the reason why the various modCount fields are mostly + // ignored in this wrapper listIterator. + // If the backing listIterator is failfast, then the following holds: + // Using any other method on this list will call a corresponding + // method on the backing list *after* the backing listIterator + // is created, which will in turn cause a ConcurrentModException + // when this listIterator comes to use the backing one. So it is + // implicitly failfast. + // If the backing listIterator is NOT failfast, then the whole of + // this list isn't failfast, because the modCount field of the + // backing list is not valid. It would still be *possible* to + // make the iterator failfast wrt modifications of the sublist + // only, but somewhat pointless when the list can be changed under + // us. + // Either way, no explicit handling of modCount is needed. + // However modCount = backingList.modCount must be executed in add + // and remove, and size must also be updated in these two methods, + // since they do not go through the corresponding methods of the subList. + }; + } + } // class SubList + + /** + * This class is a RandomAccess version of SubList, as required by + * {@link AbstractList#subList(int, int)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class RandomAccessSubList extends SubList + implements RandomAccess + { + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + RandomAccessSubList(AbstractList backing, int fromIndex, int toIndex) + { + super(backing, fromIndex, toIndex); + } + } // class RandomAccessSubList + +} // class AbstractList diff --git a/libjava/classpath/java/util/AbstractMap.java b/libjava/classpath/java/util/AbstractMap.java new file mode 100644 index 00000000000..7cd6436a308 --- /dev/null +++ b/libjava/classpath/java/util/AbstractMap.java @@ -0,0 +1,749 @@ +/* AbstractMap.java -- Abstract implementation of most of Map + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An abstract implementation of Map to make it easier to create your own + * implementations. In order to create an unmodifiable Map, subclass + * AbstractMap and implement the <code>entrySet</code> (usually via an + * AbstractSet). To make it modifiable, also implement <code>put</code>, + * and have <code>entrySet().iterator()</code> support <code>remove</code>. + * <p> + * + * It is recommended that classes which extend this support at least the + * no-argument constructor, and a constructor which accepts another Map. + * Further methods in this class may be overridden if you have a more + * efficient implementation. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Collection + * @see HashMap + * @see LinkedHashMap + * @see TreeMap + * @see WeakHashMap + * @see IdentityHashMap + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractMap implements Map +{ + /** An "enum" of iterator types. */ + // Package visible for use by subclasses. + static final int KEYS = 0, + VALUES = 1, + ENTRIES = 2; + + /** + * The cache for {@link #keySet()}. + */ + // Package visible for use by subclasses. + Set keys; + + /** + * The cache for {@link #values()}. + */ + // Package visible for use by subclasses. + Collection values; + + /** + * The main constructor, for use by subclasses. + */ + protected AbstractMap() + { + } + + /** + * Returns a set view of the mappings in this Map. Each element in the + * set must be an implementation of Map.Entry. The set is backed by + * the map, so that changes in one show up in the other. Modifications + * made while an iterator is in progress cause undefined behavior. If + * the set supports removal, these methods must be valid: + * <code>Iterator.remove</code>, <code>Set.remove</code>, + * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. + * Element addition is not supported via this set. + * + * @return the entry set + * @see Map.Entry + */ + public abstract Set entrySet(); + + /** + * Remove all entries from this Map (optional operation). This default + * implementation calls entrySet().clear(). NOTE: If the entry set does + * not permit clearing, then this will fail, too. Subclasses often + * override this for efficiency. Your implementation of entrySet() should + * not call <code>AbstractMap.clear</code> unless you want an infinite loop. + * + * @throws UnsupportedOperationException if <code>entrySet().clear()</code> + * does not support clearing. + * @see Set#clear() + */ + public void clear() + { + entrySet().clear(); + } + + /** + * Create a shallow copy of this Map, no keys or values are copied. The + * default implementation simply calls <code>super.clone()</code>. + * + * @return the shallow clone + * @throws CloneNotSupportedException if a subclass is not Cloneable + * @see Cloneable + * @see Object#clone() + */ + protected Object clone() throws CloneNotSupportedException + { + AbstractMap copy = (AbstractMap) super.clone(); + // Clear out the caches; they are stale. + copy.keys = null; + copy.values = null; + return copy; + } + + /** + * Returns true if this contains a mapping for the given key. This + * implementation does a linear search, O(n), over the + * <code>entrySet()</code>, returning <code>true</code> if a match + * is found, <code>false</code> if the iteration ends. Many subclasses + * can implement this more efficiently. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys + * @see #containsValue(Object) + */ + public boolean containsKey(Object key) + { + Iterator entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(key, ((Map.Entry) entries.next()).getKey())) + return true; + return false; + } + + /** + * Returns true if this contains at least one mapping with the given value. + * This implementation does a linear search, O(n), over the + * <code>entrySet()</code>, returning <code>true</code> if a match + * is found, <code>false</code> if the iteration ends. A match is + * defined as a value, v, where <code>(value == null ? v == null : + * value.equals(v))</code>. Subclasses are unlikely to implement + * this more efficiently. + * + * @param value the value to search for + * @return true if the map contains the value + * @see #containsKey(Object) + */ + public boolean containsValue(Object value) + { + Iterator entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(value, ((Map.Entry) entries.next()).getValue())) + return true; + return false; + } + + /** + * Compares the specified object with this map for equality. Returns + * <code>true</code> if the other object is a Map with the same mappings, + * that is,<br> + * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code> + * + * @param o the object to be compared + * @return true if the object equals this map + * @see Set#equals(Object) + */ + public boolean equals(Object o) + { + return (o == this || + (o instanceof Map && + entrySet().equals(((Map) o).entrySet()))); + } + + /** + * Returns the value mapped by the given key. Returns <code>null</code> if + * there is no mapping. However, in Maps that accept null values, you + * must rely on <code>containsKey</code> to determine if a mapping exists. + * This iteration takes linear time, searching entrySet().iterator() of + * the key. Many implementations override this method. + * + * @param key the key to look up + * @return the value associated with the key, or null if key not in map + * @throws NullPointerException if this map does not accept null keys + * @see #containsKey(Object) + */ + public Object get(Object key) + { + Iterator entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + { + Map.Entry entry = (Map.Entry) entries.next(); + if (equals(key, entry.getKey())) + return entry.getValue(); + } + return null; + } + + /** + * Returns the hash code for this map. As defined in Map, this is the sum + * of all hashcodes for each Map.Entry object in entrySet, or basically + * entrySet().hashCode(). + * + * @return the hash code + * @see Map.Entry#hashCode() + * @see Set#hashCode() + */ + public int hashCode() + { + return entrySet().hashCode(); + } + + /** + * Returns true if the map contains no mappings. This is implemented by + * <code>size() == 0</code>. + * + * @return true if the map is empty + * @see #size() + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * Returns a set view of this map's keys. The set is backed by the map, + * so changes in one show up in the other. Modifications while an iteration + * is in progress produce undefined behavior. The set supports removal + * if entrySet() does, but does not support element addition. + * <p> + * + * This implementation creates an AbstractSet, where the iterator wraps + * the entrySet iterator, size defers to the Map's size, and contains + * defers to the Map's containsKey. The set is created on first use, and + * returned on subsequent uses, although since no synchronization occurs, + * there is a slight possibility of creating two sets. + * + * @return a Set view of the keys + * @see Set#iterator() + * @see #size() + * @see #containsKey(Object) + * @see #values() + */ + public Set keySet() + { + if (keys == null) + keys = new AbstractSet() + { + /** + * Retrieves the number of keys in the backing map. + * + * @return The number of keys. + */ + public int size() + { + return AbstractMap.this.size(); + } + + /** + * Returns true if the backing map contains the + * supplied key. + * + * @param key The key to search for. + * @return True if the key was found, false otherwise. + */ + public boolean contains(Object key) + { + return containsKey(key); + } + + /** + * Returns an iterator which iterates over the keys + * in the backing map, using a wrapper around the + * iterator returned by <code>entrySet()</code>. + * + * @return An iterator over the keys. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The iterator returned by <code>entrySet()</code>. + */ + private final Iterator map_iterator = entrySet().iterator(); + + /** + * Returns true if a call to <code>next()</code> will + * return another key. + * + * @return True if the iterator has not yet reached + * the last key. + */ + public boolean hasNext() + { + return map_iterator.hasNext(); + } + + /** + * Returns the key from the next entry retrieved + * by the underlying <code>entrySet()</code> iterator. + * + * @return The next key. + */ + public Object next() + { + return ((Map.Entry) map_iterator.next()).getKey(); + } + + /** + * Removes the map entry which has a key equal + * to that returned by the last call to + * <code>next()</code>. + * + * @throws UnsupportedOperationException if the + * map doesn't support removal. + */ + public void remove() + { + map_iterator.remove(); + } + }; + } + }; + return keys; + } + + /** + * Associates the given key to the given value (optional operation). If the + * map already contains the key, its value is replaced. This implementation + * simply throws an UnsupportedOperationException. Be aware that in a map + * that permits <code>null</code> values, a null return does not always + * imply that the mapping was created. + * + * @param key the key to map + * @param value the value to be mapped + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values + * @see #containsKey(Object) + */ + public Object put(Object key, Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Copies all entries of the given map to this one (optional operation). If + * the map already contains a key, its value is replaced. This implementation + * simply iterates over the map's entrySet(), calling <code>put</code>, + * so it is not supported if puts are not. + * + * @param m the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * by this map. + * @throws ClassCastException if a key or value is of the wrong type for + * adding to this map. + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map. + * @throws NullPointerException if the map forbids null keys or values. + * @throws NullPointerException if <code>m</code> is null. + * @see #put(Object, Object) + */ + public void putAll(Map m) + { + Iterator entries = m.entrySet().iterator(); + int pos = m.size(); + while (--pos >= 0) + { + Map.Entry entry = (Map.Entry) entries.next(); + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes the mapping for this key if present (optional operation). This + * implementation iterates over the entrySet searching for a matching + * key, at which point it calls the iterator's <code>remove</code> method. + * It returns the result of <code>getValue()</code> on the entry, if found, + * or null if no entry is found. Note that maps which permit null values + * may also return null if the key was removed. If the entrySet does not + * support removal, this will also fail. This is O(n), so many + * implementations override it for efficiency. + * + * @param key the key to remove + * @return the value the key mapped to, or null if not present. + * Null may also be returned if null values are allowed + * in the map and the value of this mapping is null. + * @throws UnsupportedOperationException if deletion is unsupported + * @see Iterator#remove() + */ + public Object remove(Object key) + { + Iterator entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + { + Map.Entry entry = (Map.Entry) entries.next(); + if (equals(key, entry.getKey())) + { + // Must get the value before we remove it from iterator. + Object r = entry.getValue(); + entries.remove(); + return r; + } + } + return null; + } + + /** + * Returns the number of key-value mappings in the map. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is + * implemented as <code>entrySet().size()</code>. + * + * @return the number of mappings + * @see Set#size() + */ + public int size() + { + return entrySet().size(); + } + + /** + * Returns a String representation of this map. This is a listing of the + * map entries (which are specified in Map.Entry as being + * <code>getKey() + "=" + getValue()</code>), separated by a comma and + * space (", "), and surrounded by braces ('{' and '}'). This implementation + * uses a StringBuffer and iterates over the entrySet to build the String. + * Note that this can fail with an exception if underlying keys or + * values complete abruptly in toString(). + * + * @return a String representation + * @see Map.Entry#toString() + */ + public String toString() + { + Iterator entries = entrySet().iterator(); + StringBuffer r = new StringBuffer("{"); + for (int pos = size(); pos > 0; pos--) + { + Map.Entry entry = (Map.Entry) entries.next(); + r.append(entry.getKey()); + r.append('='); + r.append(entry.getValue()); + if (pos > 1) + r.append(", "); + } + r.append("}"); + return r.toString(); + } + + /** + * Returns a collection or bag view of this map's values. The collection + * is backed by the map, so changes in one show up in the other. + * Modifications while an iteration is in progress produce undefined + * behavior. The collection supports removal if entrySet() does, but + * does not support element addition. + * <p> + * + * This implementation creates an AbstractCollection, where the iterator + * wraps the entrySet iterator, size defers to the Map's size, and contains + * defers to the Map's containsValue. The collection is created on first + * use, and returned on subsequent uses, although since no synchronization + * occurs, there is a slight possibility of creating two collections. + * + * @return a Collection view of the values + * @see Collection#iterator() + * @see #size() + * @see #containsValue(Object) + * @see #keySet() + */ + public Collection values() + { + if (values == null) + values = new AbstractCollection() + { + /** + * Returns the number of values stored in + * the backing map. + * + * @return The number of values. + */ + public int size() + { + return AbstractMap.this.size(); + } + + /** + * Returns true if the backing map contains + * the supplied value. + * + * @param value The value to search for. + * @return True if the value was found, false otherwise. + */ + public boolean contains(Object value) + { + return containsValue(value); + } + + /** + * Returns an iterator which iterates over the + * values in the backing map, by using a wrapper + * around the iterator returned by <code>entrySet()</code>. + * + * @return An iterator over the values. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The iterator returned by <code>entrySet()</code>. + */ + private final Iterator map_iterator = entrySet().iterator(); + + /** + * Returns true if a call to <code>next()</call> will + * return another value. + * + * @return True if the iterator has not yet reached + * the last value. + */ + public boolean hasNext() + { + return map_iterator.hasNext(); + } + + /** + * Returns the value from the next entry retrieved + * by the underlying <code>entrySet()</code> iterator. + * + * @return The next value. + */ + public Object next() + { + return ((Map.Entry) map_iterator.next()).getValue(); + } + + /** + * Removes the map entry which has a key equal + * to that returned by the last call to + * <code>next()</code>. + * + * @throws UnsupportedOperationException if the + * map doesn't support removal. + */ + public void remove() + { + map_iterator.remove(); + } + }; + } + }; + return values; + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == null ? o2 == null : o1.equals(o2) + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } + + /** + * A class which implements Map.Entry. It is shared by HashMap, TreeMap, + * Hashtable, and Collections. It is not specified by the JDK, but makes + * life much easier. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + */ + // XXX - FIXME Use fully qualified implements as gcj 3.1 workaround. + // Bug still exists in 3.4.1 + static class BasicMapEntry implements Map.Entry + { + /** + * The key. Package visible for direct manipulation. + */ + Object key; + + /** + * The value. Package visible for direct manipulation. + */ + Object value; + + /** + * Basic constructor initializes the fields. + * @param newKey the key + * @param newValue the value + */ + BasicMapEntry(Object newKey, Object newValue) + { + key = newKey; + value = newValue; + } + + /** + * Compares the specified object with this entry. Returns true only if + * the object is a mapping of identical key and value. In other words, + * this must be:<br> + * <pre>(o instanceof Map.Entry) + * && (getKey() == null ? ((HashMap) o).getKey() == null + * : getKey().equals(((HashMap) o).getKey())) + * && (getValue() == null ? ((HashMap) o).getValue() == null + * : getValue().equals(((HashMap) o).getValue()))</pre> + * + * @param o the object to compare + * @return <code>true</code> if it is equal + */ + public final boolean equals(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + // Optimize for our own entries. + if (o instanceof BasicMapEntry) + { + BasicMapEntry e = (BasicMapEntry) o; + return (AbstractMap.equals(key, e.key) + && AbstractMap.equals(value, e.value)); + } + Map.Entry e = (Map.Entry) o; + return (AbstractMap.equals(key, e.getKey()) + && AbstractMap.equals(value, e.getValue())); + } + + /** + * Get the key corresponding to this entry. + * + * @return the key + */ + public final Object getKey() + { + return key; + } + + /** + * Get the value corresponding to this entry. If you already called + * Iterator.remove(), the behavior undefined, but in this case it works. + * + * @return the value + */ + public final Object getValue() + { + return value; + } + + /** + * Returns the hash code of the entry. This is defined as the exclusive-or + * of the hashcodes of the key and value (using 0 for null). In other + * words, this must be:<br> + * <pre>(getKey() == null ? 0 : getKey().hashCode()) + * ^ (getValue() == null ? 0 : getValue().hashCode())</pre> + * + * @return the hash code + */ + public final int hashCode() + { + return (AbstractMap.hashCode(key) ^ AbstractMap.hashCode(value)); + } + + /** + * Replaces the value with the specified object. This writes through + * to the map, unless you have already called Iterator.remove(). It + * may be overridden to restrict a null value. + * + * @param newVal the new value to store + * @return the old value + * @throws NullPointerException if the map forbids null values. + * @throws UnsupportedOperationException if the map doesn't support + * <code>put()</code>. + * @throws ClassCastException if the value is of a type unsupported + * by the map. + * @throws IllegalArgumentException if something else about this + * value prevents it being stored in the map. + */ + public Object setValue(Object newVal) + { + Object r = value; + value = newVal; + return r; + } + + /** + * This provides a string representation of the entry. It is of the form + * "key=value", where string concatenation is used on key and value. + * + * @return the string representation + */ + public final String toString() + { + return key + "=" + value; + } + } // class BasicMapEntry +} diff --git a/libjava/classpath/java/util/AbstractSequentialList.java b/libjava/classpath/java/util/AbstractSequentialList.java new file mode 100644 index 00000000000..79583228d96 --- /dev/null +++ b/libjava/classpath/java/util/AbstractSequentialList.java @@ -0,0 +1,235 @@ +/* AbstractSequentialList.java -- List implementation for sequential access + Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Abstract superclass to make it easier to implement the List interface when + * backed by a sequential-access store, such as a linked list. For random + * access data, use AbstractList. This class implements the random access + * methods (<code>get</code>, <code>set</code>, <code>add</code>, and + * <code>remove</code>) atop the list iterator, opposite of AbstractList's + * approach of implementing the iterator atop random access. + * <p> + * + * To implement a list, you need an implementation for <code>size()</code> + * and <code>listIterator</code>. With just <code>hasNext</code>, + * <code>next</code>, <code>hasPrevious</code>, <code>previous</code>, + * <code>nextIndex</code>, and <code>previousIndex</code>, you have an + * unmodifiable list. For a modifiable one, add <code>set</code>, and for + * a variable-size list, add <code>add</code> and <code>remove</code>. + * <p> + * + * The programmer should provide a no-argument constructor, and one that + * accepts another Collection, as recommended by the Collection interface. + * Unfortunately, there is no way to enforce this in Java. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see AbstractList + * @see AbstractCollection + * @see ListIterator + * @see LinkedList + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractSequentialList extends AbstractList +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractSequentialList() + { + } + + /** + * Returns a ListIterator over the list, starting from position index. + * Subclasses must provide an implementation of this method. + * + * @param index the starting position of the list + * @return the list iterator + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public abstract ListIterator listIterator(int index); + + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. This iteration + * uses listIterator(index).add(o). + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason. + * @throws NullPointerException if o is null and the list does not permit + * the addition of null values. + */ + public void add(int index, Object o) + { + listIterator(index).add(o); + } + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). + * <p> + * + * This implementation grabs listIterator(index), then proceeds to use add + * for each element returned by c's iterator. Sun's online specs are wrong, + * claiming that this also calls next(): listIterator.add() correctly + * skips the added element. + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @throws NullPointerException if an object, o, in c is null and the list + * does not permit the addition of null values. + * @see #add(int, Object) + */ + public boolean addAll(int index, Collection c) + { + Iterator ci = c.iterator(); + int size = c.size(); + ListIterator i = listIterator(index); + for (int pos = size; pos > 0; pos--) + i.add(ci.next()); + return size > 0; + } + + /** + * Get the element at a given index in this list. This implementation + * returns listIterator(index).next(). + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + // This is a legal listIterator position, but an illegal get. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + return listIterator(index).next(); + } + + /** + * Obtain an Iterator over this list, whose sequence is the list order. This + * implementation returns listIterator(). + * + * @return an Iterator over the elements of this list, in order + */ + public Iterator iterator() + { + return listIterator(); + } + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. This + * implementation uses listIterator(index) and ListIterator.remove(). + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object remove(int index) + { + // This is a legal listIterator position, but an illegal remove. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + ListIterator i = listIterator(index); + Object removed = i.next(); + i.remove(); + return removed; + } + + /** + * Replace an element of this list with another object (optional operation). + * This implementation uses listIterator(index) and ListIterator.set(o). + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and the list does not allow + * a value to be set to null. + */ + public Object set(int index, Object o) + { + // This is a legal listIterator position, but an illegal set. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + ListIterator i = listIterator(index); + Object old = i.next(); + i.set(o); + return old; + } +} diff --git a/libjava/classpath/java/util/AbstractSet.java b/libjava/classpath/java/util/AbstractSet.java new file mode 100644 index 00000000000..f0d7cb19908 --- /dev/null +++ b/libjava/classpath/java/util/AbstractSet.java @@ -0,0 +1,139 @@ +/* AbstractSet.java -- Abstract implementation of most of Set + Copyright (C) 1998, 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An abstract implementation of Set to make it easier to create your own + * implementations. In order to create a Set, subclass AbstractSet and + * implement the same methods that are required for AbstractCollection + * (although these methods must of course meet the requirements that Set puts + * on them - specifically, no element may be in the set more than once). This + * class simply provides implementations of equals() and hashCode() to fulfil + * the requirements placed on them by the Set interface. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see AbstractCollection + * @see Set + * @see HashSet + * @see TreeSet + * @see LinkedHashSet + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractSet extends AbstractCollection implements Set +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractSet() + { + } + + /** + * Tests whether the given object is equal to this Set. This implementation + * first checks whether this set <em>is</em> the given object, and returns + * true if so. Otherwise, if o is a Set and is the same size as this one, it + * returns the result of calling containsAll on the given Set. Otherwise, it + * returns false. + * + * @param o the Object to be tested for equality with this Set + * @return true if the given object is equal to this Set + */ + public boolean equals(Object o) + { + return (o == this || + (o instanceof Set && ((Set) o).size() == size() + && containsAll((Collection) o))); + } + + /** + * Returns a hash code for this Set. The hash code of a Set is the sum of the + * hash codes of all its elements, except that the hash code of null is + * defined to be zero. This implementation obtains an Iterator over the Set, + * and sums the results. + * + * @return a hash code for this Set + */ + public int hashCode() + { + Iterator itr = iterator(); + int hash = 0; + int pos = size(); + while (--pos >= 0) + hash += hashCode(itr.next()); + return hash; + } + + /** + * Removes from this set all elements in the given collection (optional + * operation). This implementation uses <code>size()</code> to determine + * the smaller collection. Then, if this set is smaller, it iterates + * over the set, calling Iterator.remove if the collection contains + * the element. If this set is larger, it iterates over the collection, + * calling Set.remove for all elements in the collection. Note that + * this operation will fail if a remove methods is not supported. + * + * @param c the collection of elements to remove + * @return true if the set was modified as a result + * @throws UnsupportedOperationException if remove is not supported + * @throws NullPointerException if the collection is null + * @see AbstractCollection#remove(Object) + * @see Collection#contains(Object) + * @see Iterator#remove() + */ + public boolean removeAll(Collection c) + { + int oldsize = size(); + int count = c.size(); + Iterator i; + if (oldsize < count) + { + for (i = iterator(), count = oldsize; count > 0; count--) + if (c.contains(i.next())) + i.remove(); + } + else + for (i = c.iterator(); count > 0; count--) + remove(i.next()); + return oldsize != size(); + } + +} diff --git a/libjava/classpath/java/util/ArrayList.java b/libjava/classpath/java/util/ArrayList.java new file mode 100644 index 00000000000..82bcca8c3e0 --- /dev/null +++ b/libjava/classpath/java/util/ArrayList.java @@ -0,0 +1,591 @@ +/* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed + implementation of the List interface + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * An array-backed implementation of the List interface. This implements + * all optional list operations, and permits null elements, so that it is + * better than Vector, which it replaces. Random access is roughly constant + * time, and iteration is roughly linear time, so it is nice and fast, with + * less overhead than a LinkedList. + * <p> + * + * Each list has a capacity, and as the array reaches that capacity it + * is automatically transferred to a larger array. You also have access to + * ensureCapacity and trimToSize to control the backing array's size, avoiding + * reallocation or wasted memory. + * <p> + * + * ArrayList is not synchronized, so if you need multi-threaded access, + * consider using:<br> + * <code>List l = Collections.synchronizedList(new ArrayList(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Jon A. Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see LinkedList + * @see Vector + * @see Collections#synchronizedList(List) + * @see AbstractList + * @status updated to 1.4 + */ +public class ArrayList extends AbstractList + implements List, RandomAccess, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2 + */ + private static final long serialVersionUID = 8683452581122892189L; + + /** + * The default capacity for new ArrayLists. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The number of elements in this list. + * @serial the list size + */ + private int size; + + /** + * Where the data is stored. + */ + private transient Object[] data; + + /** + * Construct a new ArrayList with the supplied initial capacity. + * + * @param capacity initial capacity of this ArrayList + * @throws IllegalArgumentException if capacity is negative + */ + public ArrayList(int capacity) + { + // Must explicitly check, to get correct exception. + if (capacity < 0) + throw new IllegalArgumentException(); + data = new Object[capacity]; + } + + /** + * Construct a new ArrayList with the default capacity (16). + */ + public ArrayList() + { + this(DEFAULT_CAPACITY); + } + + /** + * Construct a new ArrayList, and initialize it with the elements + * in the supplied Collection. The initial capacity is 110% of the + * Collection's size. + * + * @param c the collection whose elements will initialize this list + * @throws NullPointerException if c is null + */ + public ArrayList(Collection c) + { + this((int) (c.size() * 1.1f)); + addAll(c); + } + + /** + * Trims the capacity of this List to be equal to its size; + * a memory saver. + */ + public void trimToSize() + { + // Not a structural change from the perspective of iterators on this list, + // so don't update modCount. + if (size != data.length) + { + Object[] newData = new Object[size]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + /** + * Guarantees that this list will have at least enough capacity to + * hold minCapacity elements. This implementation will grow the list to + * max(current * 2, minCapacity) if (minCapacity > current). The JCL says + * explictly that "this method increases its capacity to minCap", while + * the JDK 1.3 online docs specify that the list will grow to at least the + * size specified. + * + * @param minCapacity the minimum guaranteed capacity + */ + public void ensureCapacity(int minCapacity) + { + int current = data.length; + + if (minCapacity > current) + { + Object[] newData = new Object[Math.max(current * 2, minCapacity)]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + /** + * Returns the number of elements in this list. + * + * @return the list size + */ + public int size() + { + return size; + } + + /** + * Checks if the list is empty. + * + * @return true if there are no elements + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Returns true iff element is in this ArrayList. + * + * @param e the element whose inclusion in the List is being tested + * @return true if the list contains e + */ + public boolean contains(Object e) + { + return indexOf(e) != -1; + } + + /** + * Returns the lowest index at which element appears in this List, or + * -1 if it does not appear. + * + * @param e the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int indexOf(Object e) + { + for (int i = 0; i < size; i++) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Returns the highest index at which element appears in this List, or + * -1 if it does not appear. + * + * @param e the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int lastIndexOf(Object e) + { + for (int i = size - 1; i >= 0; i--) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Creates a shallow copy of this ArrayList (elements are not cloned). + * + * @return the cloned object + */ + public Object clone() + { + ArrayList clone = null; + try + { + clone = (ArrayList) super.clone(); + clone.data = (Object[]) data.clone(); + } + catch (CloneNotSupportedException e) + { + // Impossible to get here. + } + return clone; + } + + /** + * Returns an Object array containing all of the elements in this ArrayList. + * The array is independent of this list. + * + * @return an array representation of this list + */ + public Object[] toArray() + { + Object[] array = new Object[size]; + System.arraycopy(data, 0, array, 0, size); + return array; + } + + /** + * Returns an Array whose component type is the runtime component type of + * the passed-in Array. The returned Array is populated with all of the + * elements in this ArrayList. If the passed-in Array is not large enough + * to store all of the elements in this List, a new Array will be created + * and returned; if the passed-in Array is <i>larger</i> than the size + * of this List, then size() index will be set to null. + * + * @param a the passed-in Array + * @return an array representation of this list + * @throws ArrayStoreException if the runtime type of a does not allow + * an element in this list + * @throws NullPointerException if a is null + */ + public Object[] toArray(Object[] a) + { + if (a.length < size) + a = (Object[]) Array.newInstance(a.getClass().getComponentType(), + size); + else if (a.length > size) + a[size] = null; + System.arraycopy(data, 0, a, 0, size); + return a; + } + + /** + * Retrieves the element at the user-supplied index. + * + * @param index the index of the element we are fetching + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + checkBoundExclusive(index); + return data[index]; + } + + /** + * Sets the element at the specified index. The new element, e, + * can be an object of any type or null. + * + * @param index the index at which the element is being set + * @param e the element to be set + * @return the element previously at the specified index + * @throws IndexOutOfBoundsException if index < 0 || index >= 0 + */ + public Object set(int index, Object e) + { + checkBoundExclusive(index); + Object result = data[index]; + data[index] = e; + return result; + } + + /** + * Appends the supplied element to the end of this list. + * The element, e, can be an object of any type or null. + * + * @param e the element to be appended to this list + * @return true, the add will always succeed + */ + public boolean add(Object e) + { + modCount++; + if (size == data.length) + ensureCapacity(size + 1); + data[size++] = e; + return true; + } + + /** + * Adds the supplied element at the specified index, shifting all + * elements currently at that index or higher one to the right. + * The element, e, can be an object of any type or null. + * + * @param index the index at which the element is being added + * @param e the item being added + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, Object e) + { + checkBoundInclusive(index); + modCount++; + if (size == data.length) + ensureCapacity(size + 1); + if (index != size) + System.arraycopy(data, index, data, index + 1, size - index); + data[index] = e; + size++; + } + + /** + * Removes the element at the user-supplied index. + * + * @param index the index of the element to be removed + * @return the removed Object + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object remove(int index) + { + checkBoundExclusive(index); + Object r = data[index]; + modCount++; + if (index != --size) + System.arraycopy(data, index + 1, data, index, size - index); + // Aid for garbage collection by releasing this pointer. + data[size] = null; + return r; + } + + /** + * Removes all elements from this List + */ + public void clear() + { + if (size > 0) + { + modCount++; + // Allow for garbage collection. + Arrays.fill(data, 0, size, null); + size = 0; + } + } + + /** + * Add each element in the supplied Collection to this List. It is undefined + * what happens if you modify the list while this is taking place; for + * example, if the collection contains this list. c can contain objects + * of any type, as well as null values. + * + * @param c a Collection containing elements to be added to this List + * @return true if the list was modified, in other words c is not empty + * @throws NullPointerException if c is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Add all elements in the supplied collection, inserting them beginning + * at the specified index. c can contain objects of any type, as well + * as null values. + * + * @param index the index at which the elements will be inserted + * @param c the Collection containing the elements to be inserted + * @throws IndexOutOfBoundsException if index < 0 || index > 0 + * @throws NullPointerException if c is null + */ + public boolean addAll(int index, Collection c) + { + checkBoundInclusive(index); + Iterator itr = c.iterator(); + int csize = c.size(); + + modCount++; + if (csize + size > data.length) + ensureCapacity(size + csize); + int end = index + csize; + if (size > 0 && index != size) + System.arraycopy(data, index, data, end, size - index); + size += csize; + for ( ; index < end; index++) + data[index] = itr.next(); + return csize > 0; + } + + /** + * Removes all elements in the half-open interval [fromIndex, toIndex). + * Does nothing when toIndex is equal to fromIndex. + * + * @param fromIndex the first index which will be removed + * @param toIndex one greater than the last index which will be removed + * @throws IndexOutOfBoundsException if fromIndex > toIndex + */ + protected void removeRange(int fromIndex, int toIndex) + { + int change = toIndex - fromIndex; + if (change > 0) + { + modCount++; + System.arraycopy(data, toIndex, data, fromIndex, size - toIndex); + size -= change; + } + else if (change < 0) + throw new IndexOutOfBoundsException(); + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index > size + */ + private void checkBoundInclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException, + // a subclass of the required exception, with no effort on our part. + if (index > size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + + size); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index >= size + */ + private void checkBoundExclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException, + // a subclass of the required exception, with no effort on our part. + if (index >= size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + + size); + } + + /** + * Remove from this list all elements contained in the given collection. + * This is not public, due to Sun's API, but this performs in linear + * time while the default behavior of AbstractList would be quadratic. + * + * @param c the collection to filter out + * @return true if this list changed + * @throws NullPointerException if c is null + */ + boolean removeAllInternal(Collection c) + { + int i; + int j; + for (i = 0; i < size; i++) + if (c.contains(data[i])) + break; + if (i == size) + return false; + + modCount++; + for (j = i++; i < size; i++) + if (! c.contains(data[i])) + data[j++] = data[i]; + size -= i - j; + return true; + } + + /** + * Retain in this vector only the elements contained in the given collection. + * This is not public, due to Sun's API, but this performs in linear + * time while the default behavior of AbstractList would be quadratic. + * + * @param c the collection to filter by + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + boolean retainAllInternal(Collection c) + { + int i; + int j; + for (i = 0; i < size; i++) + if (! c.contains(data[i])) + break; + if (i == size) + return false; + + modCount++; + for (j = i++; i < size; i++) + if (c.contains(data[i])) + data[j++] = data[i]; + size -= i - j; + return true; + } + + /** + * Serializes this object to the given stream. + * + * @param out the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the size field (int), the length of the backing array + * (int), followed by its elements (Objects) in proper order. + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + // The 'size' field. + s.defaultWriteObject(); + // We serialize unused list entries to preserve capacity. + int len = data.length; + s.writeInt(len); + // it would be more efficient to just write "size" items, + // this need readObject read "size" items too. + for (int i = 0; i < size; i++) + s.writeObject(data[i]); + } + + /** + * Deserializes this object from the given stream. + * + * @param in the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the size field (int), the length of the backing array + * (int), followed by its elements (Objects) in proper order. + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // the `size' field. + s.defaultReadObject(); + int capacity = s.readInt(); + data = new Object[capacity]; + for (int i = 0; i < size; i++) + data[i] = s.readObject(); + } +} diff --git a/libjava/classpath/java/util/Arrays.java b/libjava/classpath/java/util/Arrays.java new file mode 100644 index 00000000000..15c1a5f33bf --- /dev/null +++ b/libjava/classpath/java/util/Arrays.java @@ -0,0 +1,2510 @@ +/* Arrays.java -- Utility class with methods to operate on arrays + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * This class contains various static utility methods performing operations on + * arrays, and a method to provide a List "view" of an array to facilitate + * using arrays with Collection-based APIs. All methods throw a + * {@link NullPointerException} if the parameter array is null. + * <p> + * + * Implementations may use their own algorithms, but must obey the general + * properties; for example, the sort must be stable and n*log(n) complexity. + * Sun's implementation of sort, and therefore ours, is a tuned quicksort, + * adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering a Sort + * Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265 + * (November 1993). This algorithm offers n*log(n) performance on many data + * sets that cause other quicksorts to degrade to quadratic performance. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Comparable + * @see Comparator + * @since 1.2 + * @status updated to 1.4 + */ +public class Arrays +{ + /** + * This class is non-instantiable. + */ + private Arrays() + { + } + + +// binarySearch + /** + * Perform a binary search of a byte array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(byte[] a, byte key) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final byte d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a char array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(char[] a, char key) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final char d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a short array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(short[] a, short key) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final short d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of an int array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(int[] a, int key) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final int d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a long array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(long[] a, long key) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final long d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a float array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(float[] a, float key) + { + // Must use Float.compare to take into account NaN, +-0. + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final int r = Float.compare(a[mid], key); + if (r == 0) + return mid; + else if (r > 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a double array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(double[] a, double key) + { + // Must use Double.compare to take into account NaN, +-0. + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final int r = Double.compare(a[mid], key); + if (r == 0) + return mid; + else if (r > 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of an Object array for a key, using the natural + * ordering of the elements. The array must be sorted (as by the sort() + * method) - if it is not, the behaviour of this method is undefined, and may + * be an infinite loop. Further, the key must be comparable with every item + * in the array. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this (JCL) + * implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws NullPointerException if a null element in a is compared + */ + public static int binarySearch(Object[] a, Object key) + { + return binarySearch(a, key, null); + } + + /** + * Perform a binary search of an Object array for a key, using a supplied + * Comparator. The array must be sorted (as by the sort() method with the + * same Comparator) - if it is not, the behaviour of this method is + * undefined, and may be an infinite loop. Further, the key must be + * comparable with every item in the array. If the array contains the key + * more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this (JCL) implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @param c the comparator by which the array is sorted; or null to + * use the elements' natural order + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static int binarySearch(Object[] a, Object key, Comparator c) + { + int low = 0; + int hi = a.length - 1; + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >> 1; + final int d = Collections.compare(key, a[mid], c); + if (d == 0) + return mid; + else if (d < 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + +// equals + /** + * Compare two boolean arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(boolean[] a1, boolean[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two byte arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(byte[] a1, byte[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two char arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(char[] a1, char[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two short arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(short[] a1, short[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two int arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(int[] a1, int[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two long arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(long[] a1, long[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two float arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(float[] a1, float[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // Must use Float.compare to take into account NaN, +-0. + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (Float.compare(a1[i], a2[i]) != 0) + return false; + return true; + } + return false; + } + + /** + * Compare two double arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(double[] a1, double[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // Must use Double.compare to take into account NaN, +-0. + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (Double.compare(a1[i], a2[i]) != 0) + return false; + return true; + } + return false; + } + + /** + * Compare two Object arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a1 is of the same length + * as a2, and for each 0 <= i < a.length, a1[i] == null ? + * a2[i] == null : a1[i].equals(a2[i]). + */ + public static boolean equals(Object[] a1, Object[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (! AbstractCollection.equals(a1[i], a2[i])) + return false; + return true; + } + return false; + } + + +// fill + /** + * Fill an array with a boolean value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(boolean[] a, boolean val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a boolean value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a byte value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(byte[] a, byte val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a byte value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(byte[] a, int fromIndex, int toIndex, byte val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a char value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(char[] a, char val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a char value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(char[] a, int fromIndex, int toIndex, char val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a short value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(short[] a, short val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a short value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(short[] a, int fromIndex, int toIndex, short val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with an int value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(int[] a, int val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with an int value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(int[] a, int fromIndex, int toIndex, int val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a long value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(long[] a, long val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a long value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(long[] a, int fromIndex, int toIndex, long val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a float value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(float[] a, float val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a float value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(float[] a, int fromIndex, int toIndex, float val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a double value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(double[] a, double val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a double value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(double[] a, int fromIndex, int toIndex, double val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with an Object value. + * + * @param a the array to fill + * @param val the value to fill it with + * @throws ClassCastException if val is not an instance of the element + * type of a. + */ + public static void fill(Object[] a, Object val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with an Object value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws ClassCastException if val is not an instance of the element + * type of a. + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(Object[] a, int fromIndex, int toIndex, Object val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + +// sort + // Thanks to Paul Fisher (rao@gnu.org) for finding this quicksort algorithm + // as specified by Sun and porting it to Java. The algorithm is an optimised + // quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's + // "Engineering a Sort Function", Software-Practice and Experience, Vol. + // 23(11) P. 1249-1265 (November 1993). This algorithm gives n*log(n) + // performance on many arrays that would take quadratic time with a standard + // quicksort. + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the byte array to sort + */ + public static void sort(byte[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the byte array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(byte[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, byte[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, byte[] a) + { + byte c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, byte[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(byte[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the char array to sort + */ + public static void sort(char[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the char array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(char[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, char[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, char[] a) + { + char c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, char[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(char[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the short array to sort + */ + public static void sort(short[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the short array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(short[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, short[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, short[] a) + { + short c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, short[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(short[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the int array to sort + */ + public static void sort(int[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the int array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(int[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, int[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, int[] a) + { + int c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, int[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Compares two integers in natural order, since a - b is inadequate. + * + * @param a the first int + * @param b the second int + * @return < 0, 0, or > 0 accorting to the comparison + */ + private static int compare(int a, int b) + { + return a < b ? -1 : a == b ? 0 : 1; + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(int[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the long array to sort + */ + public static void sort(long[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the long array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(long[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, long[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, long[] a) + { + long c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, long[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Compares two longs in natural order, since a - b is inadequate. + * + * @param a the first long + * @param b the second long + * @return < 0, 0, or > 0 accorting to the comparison + */ + private static int compare(long a, long b) + { + return a < b ? -1 : a == b ? 0 : 1; + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(long[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the float array to sort + */ + public static void sort(float[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the float array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(float[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, float[] d) + { + return (Float.compare(d[a], d[b]) < 0 + ? (Float.compare(d[b], d[c]) < 0 ? b + : Float.compare(d[a], d[c]) < 0 ? c : a) + : (Float.compare(d[b], d[c]) > 0 ? b + : Float.compare(d[a], d[c]) > 0 ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, float[] a) + { + float c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, float[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(float[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; + j > from && Float.compare(array[j - 1], array[j]) > 0; + j--) + { + swap(j, j - 1, array); + } + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = Float.compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = Float.compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the double array to sort + */ + public static void sort(double[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the double array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(double[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, double[] d) + { + return (Double.compare(d[a], d[b]) < 0 + ? (Double.compare(d[b], d[c]) < 0 ? b + : Double.compare(d[a], d[c]) < 0 ? c : a) + : (Double.compare(d[b], d[c]) > 0 ? b + : Double.compare(d[a], d[c]) > 0 ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, double[] a) + { + double c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, double[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(double[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; + j > from && Double.compare(array[j - 1], array[j]) > 0; + j--) + { + swap(j, j - 1, array); + } + return; + } + + // Determine a good median element. + int mid = count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = Double.compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = Double.compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Sort an array of Objects according to their natural ordering. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @throws ClassCastException if any two elements are not mutually + * comparable + * @throws NullPointerException if an element is null (since + * null.compareTo cannot work) + * @see Comparable + */ + public static void sort(Object[] a) + { + sort(a, 0, a.length, null); + } + + /** + * Sort an array of Objects according to a Comparator. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param c a Comparator to use in sorting the array; or null to indicate + * the elements' natural order + * @throws ClassCastException if any two elements are not mutually + * comparable by the Comparator provided + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static void sort(Object[] a, Comparator c) + { + sort(a, 0, a.length, c); + } + + /** + * Sort an array of Objects according to their natural ordering. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element to be sorted + * @param toIndex the index of the last element to be sorted plus one + * @throws ClassCastException if any two elements are not mutually + * comparable + * @throws NullPointerException if an element is null (since + * null.compareTo cannot work) + * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex + * are not in range. + * @throws IllegalArgumentException if fromIndex > toIndex + */ + public static void sort(Object[] a, int fromIndex, int toIndex) + { + sort(a, fromIndex, toIndex, null); + } + + /** + * Sort an array of Objects according to a Comparator. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element to be sorted + * @param toIndex the index of the last element to be sorted plus one + * @param c a Comparator to use in sorting the array; or null to indicate + * the elements' natural order + * @throws ClassCastException if any two elements are not mutually + * comparable by the Comparator provided + * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex + * are not in range. + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static void sort(Object[] a, int fromIndex, int toIndex, Comparator c) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException("fromIndex " + fromIndex + + " > toIndex " + toIndex); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + + // In general, the code attempts to be simple rather than fast, the + // idea being that a good optimising JIT will be able to optimise it + // better than I can, and if I try it will make it more confusing for + // the JIT. First presort the array in chunks of length 6 with insertion + // sort. A mergesort would give too much overhead for this length. + for (int chunk = fromIndex; chunk < toIndex; chunk += 6) + { + int end = Math.min(chunk + 6, toIndex); + for (int i = chunk + 1; i < end; i++) + { + if (Collections.compare(a[i - 1], a[i], c) > 0) + { + // not already sorted + int j = i; + Object elem = a[j]; + do + { + a[j] = a[j - 1]; + j--; + } + while (j > chunk + && Collections.compare(a[j - 1], elem, c) > 0); + a[j] = elem; + } + } + } + + int len = toIndex - fromIndex; + // If length is smaller or equal 6 we are done. + if (len <= 6) + return; + + Object[] src = a; + Object[] dest = new Object[len]; + Object[] t = null; // t is used for swapping src and dest + + // The difference of the fromIndex of the src and dest array. + int srcDestDiff = -fromIndex; + + // The merges are done in this loop + for (int size = 6; size < len; size <<= 1) + { + for (int start = fromIndex; start < toIndex; start += size << 1) + { + // mid is the start of the second sublist; + // end the start of the next sublist (or end of array). + int mid = start + size; + int end = Math.min(toIndex, mid + size); + + // The second list is empty or the elements are already in + // order - no need to merge + if (mid >= end + || Collections.compare(src[mid - 1], src[mid], c) <= 0) + { + System.arraycopy(src, start, + dest, start + srcDestDiff, end - start); + + // The two halves just need swapping - no need to merge + } + else if (Collections.compare(src[start], src[end - 1], c) > 0) + { + System.arraycopy(src, start, + dest, end - size + srcDestDiff, size); + System.arraycopy(src, mid, + dest, start + srcDestDiff, end - mid); + + } + else + { + // Declare a lot of variables to save repeating + // calculations. Hopefully a decent JIT will put these + // in registers and make this fast + int p1 = start; + int p2 = mid; + int i = start + srcDestDiff; + + // The main merge loop; terminates as soon as either + // half is ended + while (p1 < mid && p2 < end) + { + dest[i++] = + src[(Collections.compare(src[p1], src[p2], c) <= 0 + ? p1++ : p2++)]; + } + + // Finish up by copying the remainder of whichever half + // wasn't finished. + if (p1 < mid) + System.arraycopy(src, p1, dest, i, mid - p1); + else + System.arraycopy(src, p2, dest, i, end - p2); + } + } + // swap src and dest ready for the next merge + t = src; + src = dest; + dest = t; + fromIndex += srcDestDiff; + toIndex += srcDestDiff; + srcDestDiff = -srcDestDiff; + } + + // make sure the result ends up back in the right place. Note + // that src and dest may have been swapped above, so src + // contains the sorted array. + if (src != a) + { + // Note that fromIndex == 0. + System.arraycopy(src, 0, a, srcDestDiff, toIndex); + } + } + + /** + * Returns a list "view" of the specified array. This method is intended to + * make it easy to use the Collections API with existing array-based APIs and + * programs. Changes in the list or the array show up in both places. The + * list does not support element addition or removal, but does permit + * value modification. The returned list implements both Serializable and + * RandomAccess. + * + * @param a the array to return a view of + * @return a fixed-size list, changes to which "write through" to the array + * @see Serializable + * @see RandomAccess + * @see Arrays.ArrayList + */ + public static List asList(final Object[] a) + { + return new Arrays.ArrayList(a); + } + + /** + * Inner class used by {@link #asList(Object[])} to provide a list interface + * to an array. The name, though it clashes with java.util.ArrayList, is + * Sun's choice for Serialization purposes. Element addition and removal + * is prohibited, but values can be modified. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ + private static final class ArrayList extends AbstractList + implements Serializable, RandomAccess + { + // We override the necessary methods, plus others which will be much + // more efficient with direct iteration rather than relying on iterator(). + + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2764017481108945198L; + + /** + * The array we are viewing. + * @serial the array + */ + private final Object[] a; + + /** + * Construct a list view of the array. + * @param a the array to view + * @throws NullPointerException if a is null + */ + ArrayList(Object[] a) + { + // We have to explicitly check. + if (a == null) + throw new NullPointerException(); + this.a = a; + } + + /** + * Returns the object at the specified index in + * the array. + * + * @param index The index to retrieve an object from. + * @return The object at the array index specified. + */ + public Object get(int index) + { + return a[index]; + } + + /** + * Returns the size of the array. + * + * @return The size. + */ + public int size() + { + return a.length; + } + + /** + * Replaces the object at the specified index + * with the supplied element. + * + * @param index The index at which to place the new object. + * @param element The new object. + * @return The object replaced by this operation. + */ + public Object set(int index, Object element) + { + Object old = a[index]; + a[index] = element; + return old; + } + + /** + * Returns true if the array contains the + * supplied object. + * + * @param o The object to look for. + * @return True if the object was found. + */ + public boolean contains(Object o) + { + return lastIndexOf(o) >= 0; + } + + /** + * Returns the first index at which the + * object, o, occurs in the array. + * + * @param o The object to search for. + * @return The first relevant index. + */ + public int indexOf(Object o) + { + int size = a.length; + for (int i = 0; i < size; i++) + if (ArrayList.equals(o, a[i])) + return i; + return -1; + } + + /** + * Returns the last index at which the + * object, o, occurs in the array. + * + * @param o The object to search for. + * @return The last relevant index. + */ + public int lastIndexOf(Object o) + { + int i = a.length; + while (--i >= 0) + if (ArrayList.equals(o, a[i])) + return i; + return -1; + } + + /** + * Transforms the list into an array of + * objects, by simplying cloning the array + * wrapped by this list. + * + * @return A clone of the internal array. + */ + public Object[] toArray() + { + return (Object[]) a.clone(); + } + + /** + * Copies the objects from this list into + * the supplied array. The supplied array + * is shrunk or enlarged to the size of the + * internal array, and filled with its objects. + * + * @param array The array to fill with the objects in this list. + * @return The array containing the objects in this list, + * which may or may not be == to array. + */ + public Object[] toArray(Object[] array) + { + int size = a.length; + if (array.length < size) + array = (Object[]) + Array.newInstance(array.getClass().getComponentType(), size); + else if (array.length > size) + array[size] = null; + + System.arraycopy(a, 0, array, 0, size); + return array; + } + } +} diff --git a/libjava/classpath/java/util/BitSet.java b/libjava/classpath/java/util/BitSet.java new file mode 100644 index 00000000000..ce88794f52b --- /dev/null +++ b/libjava/classpath/java/util/BitSet.java @@ -0,0 +1,744 @@ +/* BitSet.java -- A vector of bits. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; +import java.io.Serializable; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * hashCode algorithm taken from JDK 1.2 docs. + */ + +/** + * This class can be thought of in two ways. You can see it as a + * vector of bits or as a set of non-negative integers. The name + * <code>BitSet</code> is a bit misleading. + * + * It is implemented by a bit vector, but its equally possible to see + * it as set of non-negative integer; each integer in the set is + * represented by a set bit at the corresponding index. The size of + * this structure is determined by the highest integer in the set. + * + * You can union, intersect and build (symmetric) remainders, by + * invoking the logical operations and, or, andNot, resp. xor. + * + * This implementation is NOT synchronized against concurrent access from + * multiple threads. Specifically, if one thread is reading from a bitset + * while another thread is simultaneously modifying it, the results are + * undefined. + * + * @author Jochen Hoenicke + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class BitSet implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 7997698588986878753L; + + /** + * A common mask. + */ + private static final int LONG_MASK = 0x3f; + + /** + * The actual bits. + * @serial the i'th bit is in bits[i/64] at position i%64 (where position + * 0 is the least significant). + */ + private long[] bits; + + /** + * Create a new empty bit set. All bits are initially false. + */ + public BitSet() + { + this(64); + } + + /** + * Create a new empty bit set, with a given size. This + * constructor reserves enough space to represent the integers + * from <code>0</code> to <code>nbits-1</code>. + * + * @param nbits the initial size of the bit set + * @throws NegativeArraySizeException if nbits < 0 + */ + public BitSet(int nbits) + { + if (nbits < 0) + throw new NegativeArraySizeException(); + + int length = nbits >>> 6; + if ((nbits & LONG_MASK) != 0) + ++length; + bits = new long[length]; + } + + /** + * Performs the logical AND operation on this bit set and the + * given <code>set</code>. This means it builds the intersection + * of the two sets. The result is stored into this bit set. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void and(BitSet bs) + { + int max = Math.min(bits.length, bs.bits.length); + int i; + for (i = 0; i < max; ++i) + bits[i] &= bs.bits[i]; + while (i < bits.length) + bits[i++] = 0; + } + + /** + * Performs the logical AND operation on this bit set and the + * complement of the given <code>bs</code>. This means it + * selects every element in the first set, that isn't in the + * second set. The result is stored into this bit set and is + * effectively the set difference of the two. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + * @since 1.2 + */ + public void andNot(BitSet bs) + { + int i = Math.min(bits.length, bs.bits.length); + while (--i >= 0) + bits[i] &= ~bs.bits[i]; + } + + /** + * Returns the number of bits set to true. + * + * @return the number of true bits + * @since 1.4 + */ + public int cardinality() + { + int card = 0; + for (int i = bits.length - 1; i >= 0; i--) + { + long a = bits[i]; + // Take care of common cases. + if (a == 0) + continue; + if (a == -1) + { + card += 64; + continue; + } + + // Successively collapse alternating bit groups into a sum. + a = ((a >> 1) & 0x5555555555555555L) + (a & 0x5555555555555555L); + a = ((a >> 2) & 0x3333333333333333L) + (a & 0x3333333333333333L); + int b = (int) ((a >>> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + } + return card; + } + + /** + * Sets all bits in the set to false. + * + * @since 1.4 + */ + public void clear() + { + Arrays.fill(bits, 0); + } + + /** + * Removes the integer <code>pos</code> from this set. That is + * the corresponding bit is cleared. If the index is not in the set, + * this method does nothing. + * + * @param pos a non-negative integer + * @throws IndexOutOfBoundsException if pos < 0 + */ + public void clear(int pos) + { + int offset = pos >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] &= ~(1L << pos); + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to false. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @throws IndexOutOfBoundsException if from < 0 || to < 0 || + * from > to + * @since 1.4 + */ + public void clear(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] &= ((1L << from) - 1) | (-1L << to); + return; + } + + bits[lo_offset] &= (1L << from) - 1; + bits[hi_offset] &= -1L << to; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] = 0; + } + + /** + * Create a clone of this bit set, that is an instance of the same + * class and contains the same elements. But it doesn't change when + * this bit set changes. + * + * @return the clone of this object. + */ + public Object clone() + { + try + { + BitSet bs = (BitSet) super.clone(); + bs.bits = (long[]) bits.clone(); + return bs; + } + catch (CloneNotSupportedException e) + { + // Impossible to get here. + return null; + } + } + + /** + * Returns true if the <code>obj</code> is a bit set that contains + * exactly the same elements as this bit set, otherwise false. + * + * @param obj the object to compare to + * @return true if obj equals this bit set + */ + public boolean equals(Object obj) + { + if (!(obj instanceof BitSet)) + return false; + BitSet bs = (BitSet) obj; + int max = Math.min(bits.length, bs.bits.length); + int i; + for (i = 0; i < max; ++i) + if (bits[i] != bs.bits[i]) + return false; + // If one is larger, check to make sure all extra bits are 0. + for (int j = i; j < bits.length; ++j) + if (bits[j] != 0) + return false; + for (int j = i; j < bs.bits.length; ++j) + if (bs.bits[j] != 0) + return false; + return true; + } + + /** + * Sets the bit at the index to the opposite value. + * + * @param index the index of the bit + * @throws IndexOutOfBoundsException if index is negative + * @since 1.4 + */ + public void flip(int index) + { + int offset = index >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] ^= 1L << index; + } + + /** + * Sets a range of bits to the opposite value. + * + * @param from the low index (inclusive) + * @param to the high index (exclusive) + * @throws IndexOutOfBoundsException if from > to || from < 0 || + * to < 0 + * @since 1.4 + */ + public void flip(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] ^= (-1L << from) & ((1L << to) - 1); + return; + } + + bits[lo_offset] ^= -1L << from; + bits[hi_offset] ^= (1L << to) - 1; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] ^= -1; + } + + /** + * Returns true if the integer <code>bitIndex</code> is in this bit + * set, otherwise false. + * + * @param pos a non-negative integer + * @return the value of the bit at the specified position + * @throws IndexOutOfBoundsException if the pos is negative + */ + public boolean get(int pos) + { + int offset = pos >> 6; + if (offset >= bits.length) + return false; + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + return (bits[offset] & (1L << pos)) != 0; + } + + /** + * Returns a new <code>BitSet</code> composed of a range of bits from + * this one. + * + * @param from the low index (inclusive) + * @param to the high index (exclusive) + * @throws IndexOutOfBoundsException if from > to || from < 0 || + * to < 0 + * @since 1.4 + */ + public BitSet get(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + BitSet bs = new BitSet(to - from); + int lo_offset = from >>> 6; + if (lo_offset >= bits.length) + return bs; + + int lo_bit = from & LONG_MASK; + int hi_offset = to >>> 6; + if (lo_bit == 0) + { + int len = Math.min(hi_offset - lo_offset + 1, bits.length - lo_offset); + System.arraycopy(bits, lo_offset, bs.bits, 0, len); + if (hi_offset < bits.length) + bs.bits[hi_offset - lo_offset] &= (1L << to) - 1; + return bs; + } + + int len = Math.min(hi_offset, bits.length - 1); + int reverse = 64 - lo_bit; + int i; + for (i = 0; lo_offset < len; lo_offset++, i++) + bs.bits[i] = ((bits[lo_offset] >>> lo_bit) + | (bits[lo_offset + 1] << reverse)); + if ((to & LONG_MASK) > lo_bit) + bs.bits[i++] = bits[lo_offset] >>> lo_bit; + if (hi_offset < bits.length) + bs.bits[i - 1] &= (1L << (to - from)) - 1; + return bs; + } + + /** + * Returns a hash code value for this bit set. The hash code of + * two bit sets containing the same integers is identical. The algorithm + * used to compute it is as follows: + * + * Suppose the bits in the BitSet were to be stored in an array of + * long integers called <code>bits</code>, in such a manner that + * bit <code>k</code> is set in the BitSet (for non-negative values + * of <code>k</code>) if and only if + * + * <code>((k/64) < bits.length) + * && ((bits[k/64] & (1L << (bit % 64))) != 0) + * </code> + * + * Then the following definition of the hashCode method + * would be a correct implementation of the actual algorithm: + * + * +<pre>public int hashCode() +{ + long h = 1234; + for (int i = bits.length-1; i >= 0; i--) + { + h ^= bits[i] * (i + 1); + } + + return (int)((h >> 32) ^ h); +}</pre> + * + * Note that the hash code values changes, if the set is changed. + * + * @return the hash code value for this bit set. + */ + public int hashCode() + { + long h = 1234; + for (int i = bits.length; i > 0; ) + h ^= i * bits[--i]; + return (int) ((h >> 32) ^ h); + } + + /** + * Returns true if the specified BitSet and this one share at least one + * common true bit. + * + * @param set the set to check for intersection + * @return true if the sets intersect + * @throws NullPointerException if set is null + * @since 1.4 + */ + public boolean intersects(BitSet set) + { + int i = Math.min(bits.length, set.bits.length); + while (--i >= 0) + if ((bits[i] & set.bits[i]) != 0) + return true; + return false; + } + + /** + * Returns true if this set contains no true bits. + * + * @return true if all bits are false + * @since 1.4 + */ + public boolean isEmpty() + { + for (int i = bits.length - 1; i >= 0; i--) + if (bits[i] != 0) + return false; + return true; + } + + /** + * Returns the logical number of bits actually used by this bit + * set. It returns the index of the highest set bit plus one. + * Note that this method doesn't return the number of set bits. + * + * @return the index of the highest set bit plus one. + */ + public int length() + { + // Set i to highest index that contains a non-zero value. + int i; + for (i = bits.length - 1; i >= 0 && bits[i] == 0; --i) + ; + + // if i < 0 all bits are cleared. + if (i < 0) + return 0; + + // Now determine the exact length. + long b = bits[i]; + int len = (i + 1) * 64; + // b >= 0 checks if the highest bit is zero. + while (b >= 0) + { + --len; + b <<= 1; + } + + return len; + } + + /** + * Returns the index of the next false bit, from the specified bit + * (inclusive). + * + * @param from the start location + * @return the first false bit + * @throws IndexOutOfBoundsException if from is negative + * @since 1.4 + */ + public int nextClearBit(int from) + { + int offset = from >> 6; + long mask = 1L << from; + while (offset < bits.length) + { + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + long h = bits[offset]; + do + { + if ((h & mask) == 0) + return from; + mask <<= 1; + from++; + } + while (mask != 0); + mask = 1; + offset++; + } + return from; + } + + /** + * Returns the index of the next true bit, from the specified bit + * (inclusive). If there is none, -1 is returned. You can iterate over + * all true bits with this loop:<br> + * +<pre>for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) +{ + // operate on i here +}</pre> + * + * @param from the start location + * @return the first true bit, or -1 + * @throws IndexOutOfBoundsException if from is negative + * @since 1.4 + */ + public int nextSetBit(int from) + { + int offset = from >> 6; + long mask = 1L << from; + while (offset < bits.length) + { + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + long h = bits[offset]; + do + { + if ((h & mask) != 0) + return from; + mask <<= 1; + from++; + } + while (mask != 0); + mask = 1; + offset++; + } + return -1; + } + + /** + * Performs the logical OR operation on this bit set and the + * given <code>set</code>. This means it builds the union + * of the two sets. The result is stored into this bit set, which + * grows as necessary. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void or(BitSet bs) + { + ensure(bs.bits.length - 1); + for (int i = bs.bits.length - 1; i >= 0; i--) + bits[i] |= bs.bits[i]; + } + + /** + * Add the integer <code>bitIndex</code> to this set. That is + * the corresponding bit is set to true. If the index was already in + * the set, this method does nothing. The size of this structure + * is automatically increased as necessary. + * + * @param pos a non-negative integer. + * @throws IndexOutOfBoundsException if pos is negative + */ + public void set(int pos) + { + int offset = pos >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] |= 1L << pos; + } + + /** + * Sets the bit at the given index to the specified value. The size of + * this structure is automatically increased as necessary. + * + * @param index the position to set + * @param value the value to set it to + * @throws IndexOutOfBoundsException if index is negative + * @since 1.4 + */ + public void set(int index, boolean value) + { + if (value) + set(index); + else + clear(index); + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to true. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @throws IndexOutOfBoundsException if from < 0 || from > to || + * to < 0 + * @since 1.4 + */ + public void set(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] |= (-1L << from) & ((1L << to) - 1); + return; + } + + bits[lo_offset] |= -1L << from; + bits[hi_offset] |= (1L << to) - 1; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] = -1; + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to the + * specified value. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @param value the value to set it to + * @throws IndexOutOfBoundsException if from < 0 || from > to || + * to < 0 + * @since 1.4 + */ + public void set(int from, int to, boolean value) + { + if (value) + set(from, to); + else + clear(from, to); + } + + /** + * Returns the number of bits actually used by this bit set. Note + * that this method doesn't return the number of set bits, and that + * future requests for larger bits will make this automatically grow. + * + * @return the number of bits currently used. + */ + public int size() + { + return bits.length * 64; + } + + /** + * Returns the string representation of this bit set. This + * consists of a comma separated list of the integers in this set + * surrounded by curly braces. There is a space after each comma. + * A sample string is thus "{1, 3, 53}". + * @return the string representation. + */ + public String toString() + { + StringBuffer r = new StringBuffer("{"); + boolean first = true; + for (int i = 0; i < bits.length; ++i) + { + long bit = 1; + long word = bits[i]; + if (word == 0) + continue; + for (int j = 0; j < 64; ++j) + { + if ((word & bit) != 0) + { + if (! first) + r.append(", "); + r.append(64 * i + j); + first = false; + } + bit <<= 1; + } + } + return r.append("}").toString(); + } + + /** + * Performs the logical XOR operation on this bit set and the + * given <code>set</code>. This means it builds the symmetric + * remainder of the two sets (the elements that are in one set, + * but not in the other). The result is stored into this bit set, + * which grows as necessary. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void xor(BitSet bs) + { + ensure(bs.bits.length - 1); + for (int i = bs.bits.length - 1; i >= 0; i--) + bits[i] ^= bs.bits[i]; + } + + /** + * Make sure the vector is big enough. + * + * @param lastElt the size needed for the bits array + */ + private void ensure(int lastElt) + { + if (lastElt >= bits.length) + { + long[] nd = new long[lastElt + 1]; + System.arraycopy(bits, 0, nd, 0, bits.length); + bits = nd; + } + } +} diff --git a/libjava/classpath/java/util/Calendar.java b/libjava/classpath/java/util/Calendar.java new file mode 100644 index 00000000000..88bd76bcadc --- /dev/null +++ b/libjava/classpath/java/util/Calendar.java @@ -0,0 +1,1277 @@ +/* Calendar.java -- + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * This class is an abstract base class for Calendars, which can be + * used to convert between <code>Date</code> objects and a set of + * integer fields which represent <code>YEAR</code>, + * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code> + * object represents a time in milliseconds since the Epoch. <br> + * + * This class is locale sensitive. To get the Object matching the + * current locale you can use <code>getInstance</code>. You can even provide + * a locale or a timezone. <code>getInstance</code> returns currently + * a <code>GregorianCalendar</code> for the current date. <br> + * + * If you want to convert a date from the Year, Month, Day, DayOfWeek, + * etc. Representation to a <code>Date</code>-Object, you can create + * a new Calendar with <code>getInstance()</code>, + * <code>clear()</code> all fields, <code>set(int,int)</code> the + * fields you need and convert it with <code>getTime()</code>. <br> + * + * If you want to convert a <code>Date</code>-object to the Calendar + * representation, create a new Calendar, assign the + * <code>Date</code>-Object with <code>setTime()</code>, and read the + * fields with <code>get(int)</code>. <br> + * + * When computing the date from time fields, it may happen, that there + * are either two few fields set, or some fields are inconsistent. This + * cases will handled in a calendar specific way. Missing fields are + * replaced by the fields of the epoch: 1970 January 1 00:00. <br> + * + * To understand, how the day of year is computed out of the fields + * look at the following table. It is traversed from top to bottom, + * and for the first line all fields are set, that line is used to + * compute the day. <br> + * + * +<pre>month + day_of_month +month + week_of_month + day_of_week +month + day_of_week_of_month + day_of_week +day_of_year +day_of_week + week_of_year</pre> + * + * The hour_of_day-field takes precedence over the ampm and + * hour_of_ampm fields. <br> + * + * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br> + * + * To convert a calendar to a human readable form and vice versa, use + * the <code>java.text.DateFormat</code> class. <br> + * + * Other useful things you can do with an calendar, is + * <code>roll</code>ing fields (that means increase/decrease a + * specific field by one, propagating overflows), or + * <code>add</code>ing/substracting a fixed amount to a field. + * + * @see Date + * @see GregorianCalendar + * @see TimeZone + * @see java.text.DateFormat + */ +public abstract class Calendar implements Serializable, Cloneable +{ + /** + * Constant representing the era time field. + */ + public static final int ERA = 0; + + /** + * Constant representing the year time field. + */ + public static final int YEAR = 1; + + /** + * Constant representing the month time field. This field + * should contain one of the JANUARY,...,DECEMBER constants below. + */ + public static final int MONTH = 2; + + /** + * Constant representing the week of the year field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_YEAR = 3; + + /** + * Constant representing the week of the month time field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_MONTH = 4; + + /** + * Constant representing the day time field, synonym for DAY_OF_MONTH. + */ + public static final int DATE = 5; + + /** + * Constant representing the day time field. + */ + public static final int DAY_OF_MONTH = 5; + + /** + * Constant representing the day of year time field. This is + * 1 for the first day in month. + */ + public static final int DAY_OF_YEAR = 6; + + /** + * Constant representing the day of week time field. This field + * should contain one of the SUNDAY,...,SATURDAY constants below. + */ + public static final int DAY_OF_WEEK = 7; + + /** + * Constant representing the day-of-week-in-month field. For + * instance this field contains 2 for the second thursday in a + * month. If you give a negative number here, the day will count + * from the end of the month. + */ + public static final int DAY_OF_WEEK_IN_MONTH = 8; + + /** + * Constant representing the part of the day for 12-hour clock. This + * should be one of AM or PM. + */ + public static final int AM_PM = 9; + + /** + * Constant representing the hour time field for 12-hour clock. + */ + public static final int HOUR = 10; + + /** + * Constant representing the hour of day time field for 24-hour clock. + */ + public static final int HOUR_OF_DAY = 11; + + /** + * Constant representing the minute of hour time field. + */ + public static final int MINUTE = 12; + + /** + * Constant representing the second time field. + */ + public static final int SECOND = 13; + + /** + * Constant representing the millisecond time field. + */ + public static final int MILLISECOND = 14; + + /** + * Constant representing the time zone offset time field for the + * time given in the other fields. It is measured in + * milliseconds. The default is the offset of the time zone. + */ + public static final int ZONE_OFFSET = 15; + + /** + * Constant representing the daylight saving time offset in + * milliseconds. The default is the value given by the time zone. + */ + public static final int DST_OFFSET = 16; + + /** + * Number of time fields. + */ + public static final int FIELD_COUNT = 17; + + /** + * Constant representing Sunday. + */ + public static final int SUNDAY = 1; + + /** + * Constant representing Monday. + */ + public static final int MONDAY = 2; + + /** + * Constant representing Tuesday. + */ + public static final int TUESDAY = 3; + + /** + * Constant representing Wednesday. + */ + public static final int WEDNESDAY = 4; + + /** + * Constant representing Thursday. + */ + public static final int THURSDAY = 5; + + /** + * Constant representing Friday. + */ + public static final int FRIDAY = 6; + + /** + * Constant representing Saturday. + */ + public static final int SATURDAY = 7; + + /** + * Constant representing January. + */ + public static final int JANUARY = 0; + + /** + * Constant representing February. + */ + public static final int FEBRUARY = 1; + + /** + * Constant representing March. + */ + public static final int MARCH = 2; + + /** + * Constant representing April. + */ + public static final int APRIL = 3; + + /** + * Constant representing May. + */ + public static final int MAY = 4; + + /** + * Constant representing June. + */ + public static final int JUNE = 5; + + /** + * Constant representing July. + */ + public static final int JULY = 6; + + /** + * Constant representing August. + */ + public static final int AUGUST = 7; + + /** + * Constant representing September. + */ + public static final int SEPTEMBER = 8; + + /** + * Constant representing October. + */ + public static final int OCTOBER = 9; + + /** + * Constant representing November. + */ + public static final int NOVEMBER = 10; + + /** + * Constant representing December. + */ + public static final int DECEMBER = 11; + + /** + * Constant representing Undecimber. This is an artificial name useful + * for lunar calendars. + */ + public static final int UNDECIMBER = 12; + + /** + * Useful constant for 12-hour clock. + */ + public static final int AM = 0; + + /** + * Useful constant for 12-hour clock. + */ + public static final int PM = 1; + + /** + * The time fields. The array is indexed by the constants YEAR to + * DST_OFFSET. + * @serial + */ + protected int[] fields = new int[FIELD_COUNT]; + + /** + * The flags which tell if the fields above have a value. + * @serial + */ + protected boolean[] isSet = new boolean[FIELD_COUNT]; + + /** + * The time in milliseconds since the epoch. + * @serial + */ + protected long time; + + /** + * Tells if the above field has a valid value. + * @serial + */ + protected boolean isTimeSet; + + /** + * Tells if the fields have a valid value. This superseeds the isSet + * array. + * @serial + */ + protected boolean areFieldsSet; + + /** + * The time zone of this calendar. Used by sub classes to do UTC / local + * time conversion. Sub classes can access this field with getTimeZone(). + * @serial + */ + private TimeZone zone; + + /** + * This is the default calendar class, that is returned on + * java.util.Calendar.getInstance(). + * XXX - this isn't localized anywhere, is it? + * @see java.util.Calendar#getInstance() + */ + private static final String calendarClassName = "java.util.GregorianCalendar"; + + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @serial + */ + private boolean lenient; + + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @serial + */ + private int firstDayOfWeek; + + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @serial + */ + private int minimalDaysInFirstWeek; + + /** + * Is set to true if DST_OFFSET is explicitly set. In that case + * it's value overrides the value computed from the current + * time and the timezone. + */ + private boolean explicitDSTOffset = false; + + /** + * The version of the serialized data on the stream. + * <dl><dt>0 or not present</dt> + * <dd> JDK 1.1.5 or later.</dd> + * <dt>1</dt> + * <dd>JDK 1.1.6 or later. This always writes a correct `time' value + * on the stream, as well as the other fields, to be compatible with + * earlier versions</dd></dl> + * @since JDK1.1.6 + * @serial + */ + private int serialVersionOnStream = 1; + + /** + * XXX - I have not checked the compatibility. The documentation of + * the serialized-form is quite hairy... + */ + static final long serialVersionUID = -1807547505821590642L; + + /** + * The name of the resource bundle. Used only by getBundle() + */ + private static final String bundleName = "gnu.java.locale.LocaleInformation"; + + /** + * get resource bundle: + * The resources should be loaded via this method only. Iff an application + * uses this method, the resourcebundle is required. + */ + private static ResourceBundle getBundle(Locale locale) + { + return ResourceBundle.getBundle(bundleName, locale, + ClassLoader.getSystemClassLoader()); + } + + /** + * Constructs a new Calendar with the default time zone and the default + * locale. + */ + protected Calendar() + { + this(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Constructs a new Calendar with the given time zone and the given + * locale. + * @param zone a time zone. + * @param locale a locale. + */ + protected Calendar(TimeZone zone, Locale locale) + { + this.zone = zone; + lenient = true; + String[] days = { "", "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; + + ResourceBundle rb = getBundle(locale); + String min = (String) rb.getObject("minNumberOfDaysInFirstWeek"); + String first = (String) rb.getObject("firstDayOfWeek"); + try + { + if (min != null) + minimalDaysInFirstWeek = Integer.parseInt(min); + } + catch (NumberFormatException ex) + { + minimalDaysInFirstWeek = 1; + } + + firstDayOfWeek = 1; + if (first != null) + for (int i = 0; i < 8; i++) + if (days[i].equals(first)) + firstDayOfWeek = i; + + clear(); + } + + /** + * Creates a calendar representing the actual time, using the default + * time zone and locale. + */ + public static synchronized Calendar getInstance() + { + return getInstance(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Creates a calendar representing the actual time, using the given + * time zone and the default locale. + * @param zone a time zone. + */ + public static synchronized Calendar getInstance(TimeZone zone) + { + return getInstance(zone, Locale.getDefault()); + } + + /** + * Creates a calendar representing the actual time, using the default + * time zone and the given locale. + * @param locale a locale. + */ + public static synchronized Calendar getInstance(Locale locale) + { + return getInstance(TimeZone.getDefault(), locale); + } + + /** + * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle + * lookup for every getInstance call. + */ + private static HashMap cache = new HashMap(); + + /** Preset argument types for calendar-class constructor lookup. */ + private static Class[] ctorArgTypes = new Class[] + { + TimeZone.class, Locale.class + }; + + /** + * Creates a calendar representing the actual time, using the given + * time zone and locale. + * @param zone a time zone. + * @param locale a locale. + */ + public static synchronized Calendar getInstance(TimeZone zone, Locale locale) + { + Class calendarClass = (Class) cache.get(locale); + Throwable exception = null; + + try + { + if (calendarClass == null) + { + calendarClass = Class.forName(calendarClassName); + if (Calendar.class.isAssignableFrom(calendarClass)) + cache.put(locale, calendarClass); + } + + // GregorianCalendar is by far the most common case. Optimize by + // avoiding reflection. + if (calendarClass == GregorianCalendar.class) + return new GregorianCalendar(zone, locale); + + if (Calendar.class.isAssignableFrom(calendarClass)) + { + Constructor ctor = calendarClass.getConstructor(ctorArgTypes); + return (Calendar) ctor.newInstance(new Object[] { zone, locale }); + } + } + catch (ClassNotFoundException ex) + { + exception = ex; + } + catch (IllegalAccessException ex) + { + exception = ex; + } + catch (NoSuchMethodException ex) + { + exception = ex; + } + catch (InstantiationException ex) + { + exception = ex; + } + catch (InvocationTargetException ex) + { + exception = ex; + } + + throw new RuntimeException("Error instantiating calendar for locale " + + locale, exception); + } + + /** + * Gets the set of locales for which a Calendar is available. + * @exception MissingResourceException if locale data couldn't be found. + * @return the set of locales. + */ + public static synchronized Locale[] getAvailableLocales() + { + ResourceBundle rb = getBundle(new Locale("", "")); + return (Locale[]) rb.getObject("availableLocales"); + } + + /** + * Converts the time field values (<code>fields</code>) to + * milliseconds since the epoch UTC (<code>time</code>). Override + * this method if you write your own Calendar. */ + protected abstract void computeTime(); + + /** + * Converts the milliseconds since the epoch UTC + * (<code>time</code>) to time fields + * (<code>fields</code>). Override this method if you write your + * own Calendar. + */ + protected abstract void computeFields(); + + /** + * Converts the time represented by this object to a + * <code>Date</code>-Object. + * @return the Date. + */ + public final Date getTime() + { + if (! isTimeSet) + computeTime(); + return new Date(time); + } + + /** + * Sets this Calendar's time to the given Date. All time fields + * are invalidated by this method. + */ + public final void setTime(Date date) + { + setTimeInMillis(date.getTime()); + } + + /** + * Returns the time represented by this Calendar. + * @return the time in milliseconds since the epoch. + * @specnote This was made public in 1.4. + */ + public long getTimeInMillis() + { + if (! isTimeSet) + computeTime(); + return time; + } + + /** + * Sets this Calendar's time to the given Time. All time fields + * are invalidated by this method. + * @param time the time in milliseconds since the epoch + * @specnote This was made public in 1.4. + */ + public void setTimeInMillis(long time) + { + clear(); + this.time = time; + isTimeSet = true; + computeFields(); + } + + /** + * Gets the value of the specified field. They are recomputed + * if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + * @specnote Not final since JDK 1.4 + */ + public int get(int field) + { + // If the requested field is invalid, force all fields to be recomputed. + if (! isSet[field]) + areFieldsSet = false; + complete(); + return fields[field]; + } + + /** + * Gets the value of the specified field. This method doesn't + * recompute the fields, if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field, undefined if + * <code>areFieldsSet</code> or <code>isSet[field]</code> is false. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + */ + protected final int internalGet(int field) + { + return fields[field]; + } + + /** + * Sets the time field with the given value. This does invalidate + * the time in milliseconds. + * @param field the time field. One of the time field constants + * @param value the value to be set. + * @throws ArrayIndexOutOfBoundsException if field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + * @specnote Not final since JDK 1.4 + */ + public void set(int field, int value) + { + if (isTimeSet) + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; + isTimeSet = false; + fields[field] = value; + isSet[field] = true; + + // The five valid date patterns, in order of priority + // 1 YEAR + MONTH + DAY_OF_MONTH + // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK + // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK + // 4 YEAR + DAY_OF_YEAR + // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + switch (field) + { + case MONTH: // pattern 1,2 or 3 + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_MONTH: // pattern 1 + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[WEEK_OF_MONTH] = true; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case WEEK_OF_MONTH: // pattern 2 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[DAY_OF_WEEK] = true; + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_WEEK_IN_MONTH: // pattern 3 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[DAY_OF_WEEK] = true; + isSet[DAY_OF_YEAR] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_YEAR: // pattern 4 + isSet[YEAR] = true; + isSet[MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[WEEK_OF_YEAR] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + break; + case WEEK_OF_YEAR: // pattern 5 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[DAY_OF_WEEK] = true; + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + break; + case AM_PM: + isSet[HOUR] = true; + isSet[HOUR_OF_DAY] = false; + break; + case HOUR_OF_DAY: + isSet[AM_PM] = false; + isSet[HOUR] = false; + break; + case HOUR: + isSet[AM_PM] = true; + isSet[HOUR_OF_DAY] = false; + break; + case DST_OFFSET: + explicitDSTOffset = true; + } + + // May have crossed over a DST boundary. + if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET)) + isSet[DST_OFFSET] = false; + } + + /** + * Sets the fields for year, month, and date + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + */ + public final void set(int year, int month, int date) + { + isTimeSet = false; + fields[YEAR] = year; + fields[MONTH] = month; + fields[DATE] = date; + isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; + isSet[WEEK_OF_YEAR] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[ERA] = false; + + if (! explicitDSTOffset) + isSet[DST_OFFSET] = false; // May have crossed a DST boundary. + } + + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + */ + public final void set(int year, int month, int date, int hour, int minute) + { + set(year, month, date); + fields[HOUR_OF_DAY] = hour; + fields[MINUTE] = minute; + isSet[HOUR_OF_DAY] = isSet[MINUTE] = true; + isSet[AM_PM] = false; + isSet[HOUR] = false; + } + + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + * @param second the second. + */ + public final void set(int year, int month, int date, int hour, int minute, + int second) + { + set(year, month, date, hour, minute); + fields[SECOND] = second; + isSet[SECOND] = true; + } + + /** + * Clears the values of all the time fields. + */ + public final void clear() + { + isTimeSet = false; + areFieldsSet = false; + int zoneOffs = zone.getRawOffset(); + int[] tempFields = + { + 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, + 0, 0, zoneOffs, 0 + }; + fields = tempFields; + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; + } + + /** + * Clears the values of the specified time field. + * @param field the time field. One of the time field constants. + * @throws ArrayIndexOutOfBoundsException if field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + */ + public final void clear(int field) + { + int[] tempFields = + { + 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, + 0, 0, zone.getRawOffset(), 0 + }; + isTimeSet = false; + areFieldsSet = false; + isSet[field] = false; + fields[field] = tempFields[field]; + } + + /** + * Determines if the specified field has a valid value. + * @return true if the specified field has a value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + */ + public final boolean isSet(int field) + { + return isSet[field]; + } + + /** + * Fills any unset fields in the time field list + */ + protected void complete() + { + if (! isTimeSet) + computeTime(); + if (! areFieldsSet) + computeFields(); + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, that represents + * the same time (but doesn't necessary have the same fields). + */ + public boolean equals(Object o) + { + return (o instanceof Calendar) + && getTimeInMillis() == ((Calendar) o).getTimeInMillis(); + } + + /** + * Returns a hash code for this calendar. + * @return a hash code, which fullfits the general contract of + * <code>hashCode()</code> + */ + public int hashCode() + { + long time = getTimeInMillis(); + return (int) ((time & 0xffffffffL) ^ (time >> 32)); + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a smaller time than the calendar o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean before(Object o) + { + return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a bigger time than the calendar o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean after(Object o) + { + return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); + } + + /** + * Adds the specified amount of time to the given time field. The + * amount may be negative to subtract the time. If the field overflows + * it does what you expect: Jan, 25 + 10 Days is Feb, 4. + * @param field the time field. One of the time field constants. + * @param amount the amount of time. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + */ + public abstract void add(int field, int amount); + + /** + * Rolls the specified time field up or down. This means add one + * to the specified field, but don't change the other fields. If + * the maximum for this field is reached, start over with the + * minimum value. <br> + * + * <strong>Note:</strong> There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. + * @param field the time field. One of the time field constants. + * @param up the direction, true for up, false for down. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + */ + public abstract void roll(int field, boolean up); + + /** + * Rolls up or down the specified time field by the given amount. + * A negative amount rolls down. The default implementation is + * call <code>roll(int, boolean)</code> for the specified amount. + * + * Subclasses should override this method to do more intuitiv things. + * + * @param field the time field. One of the time field constants. + * @param amount the amount to roll by, positive for rolling up, + * negative for rolling down. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + * @since JDK1.2 + */ + public void roll(int field, int amount) + { + while (amount > 0) + { + roll(field, true); + amount--; + } + while (amount < 0) + { + roll(field, false); + amount++; + } + } + + /** + * Sets the time zone to the specified value. + * @param zone the new time zone + */ + public void setTimeZone(TimeZone zone) + { + this.zone = zone; + } + + /** + * Gets the time zone of this calendar + * @return the current time zone. + */ + public TimeZone getTimeZone() + { + return zone; + } + + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @param lenient true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public void setLenient(boolean lenient) + { + this.lenient = lenient; + } + + /** + * Tells if the date/time interpretation is lenient. + * @return true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public boolean isLenient() + { + return lenient; + } + + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @param value the first day of week. One of SUNDAY to SATURDAY. + */ + public void setFirstDayOfWeek(int value) + { + firstDayOfWeek = value; + } + + /** + * Gets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @return the first day of week. One of SUNDAY to SATURDAY. + */ + public int getFirstDayOfWeek() + { + return firstDayOfWeek; + } + + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @param value the minimal days required in the first week. + */ + public void setMinimalDaysInFirstWeek(int value) + { + minimalDaysInFirstWeek = value; + } + + /** + * Gets how many days are required in the first week of the year. + * @return the minimal days required in the first week. + * @see #setMinimalDaysInFirstWeek + */ + public int getMinimalDaysInFirstWeek() + { + return minimalDaysInFirstWeek; + } + + /** + * Gets the smallest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the smallest value. + */ + public abstract int getMinimum(int field); + + /** + * Gets the biggest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the biggest value. + */ + public abstract int getMaximum(int field); + + /** + * Gets the greatest minimum value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the greatest minimum value. + */ + public abstract int getGreatestMinimum(int field); + + /** + * Gets the smallest maximum value that is allowed for the + * specified field. For example this is 28 for DAY_OF_MONTH. + * @param field the time field. One of the time field constants. + * @return the least maximum value. + */ + public abstract int getLeastMaximum(int field); + + /** + * Gets the actual minimum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual minimum value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + * @since jdk1.2 + */ + public int getActualMinimum(int field) + { + Calendar tmp = (Calendar) clone(); // To avoid restoring state + int min = tmp.getGreatestMinimum(field); + int end = tmp.getMinimum(field); + tmp.set(field, min); + for (; min > end; min--) + { + tmp.add(field, -1); // Try to get smaller + if (tmp.get(field) != min - 1) + break; // Done if not successful + } + return min; + } + + /** + * Gets the actual maximum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual maximum value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= <code>FIELD_COUNT</code>. + * @since jdk1.2 + */ + public int getActualMaximum(int field) + { + Calendar tmp = (Calendar) clone(); // To avoid restoring state + int max = tmp.getLeastMaximum(field); + int end = tmp.getMaximum(field); + tmp.set(field, max); + for (; max < end; max++) + { + tmp.add(field, 1); + if (tmp.get(field) != max + 1) + break; + } + return max; + } + + /** + * Return a clone of this object. + */ + public Object clone() + { + try + { + Calendar cal = (Calendar) super.clone(); + cal.fields = (int[]) fields.clone(); + cal.isSet = (boolean[]) isSet.clone(); + return cal; + } + catch (CloneNotSupportedException ex) + { + return null; + } + } + + private static final String[] fieldNames = + { + ",ERA=", ",YEAR=", ",MONTH=", + ",WEEK_OF_YEAR=", + ",WEEK_OF_MONTH=", + ",DAY_OF_MONTH=", + ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", + ",DAY_OF_WEEK_IN_MONTH=", + ",AM_PM=", ",HOUR=", + ",HOUR_OF_DAY=", ",MINUTE=", + ",SECOND=", ",MILLISECOND=", + ",ZONE_OFFSET=", ",DST_OFFSET=" + }; + + /** + * Returns a string representation of this object. It is mainly + * for debugging purposes and its content is implementation + * specific. + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append('['); + sb.append("time="); + if (isTimeSet) + sb.append(time); + else + sb.append("?"); + sb.append(",zone=" + zone); + sb.append(",areFieldsSet=" + areFieldsSet); + for (int i = 0; i < FIELD_COUNT; i++) + { + sb.append(fieldNames[i]); + if (isSet[i]) + sb.append(fields[i]); + else + sb.append("?"); + } + sb.append(",lenient=").append(lenient); + sb.append(",firstDayOfWeek=").append(firstDayOfWeek); + sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); + sb.append("]"); + return sb.toString(); + } + + /** + * Saves the state of the object to the stream. Ideally we would + * only write the time field, but we need to be compatible with + * earlier versions. <br> + * + * This doesn't write the JDK1.1 field nextStamp to the stream, as + * I don't know what it is good for, and because the documentation + * says, that it could be omitted. */ + private void writeObject(ObjectOutputStream stream) throws IOException + { + if (! isTimeSet) + computeTime(); + stream.defaultWriteObject(); + } + + /** + * Reads the object back from stream (deserialization). + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (! isTimeSet) + computeTime(); + + if (serialVersionOnStream > 1) + { + // This is my interpretation of the serial number: + // Sun wants to remove all fields from the stream someday + // and will then increase the serialVersion number again. + // We prepare to be compatible. + fields = new int[FIELD_COUNT]; + isSet = new boolean[FIELD_COUNT]; + areFieldsSet = false; + } + } +} diff --git a/libjava/classpath/java/util/Collection.java b/libjava/classpath/java/util/Collection.java new file mode 100644 index 00000000000..29e1b3786b4 --- /dev/null +++ b/libjava/classpath/java/util/Collection.java @@ -0,0 +1,288 @@ +/* Collection.java -- Interface that represents a collection of objects + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface that represents a collection of objects. This interface is the + * root of the collection hierarchy, and does not provide any guarantees about + * the order of its elements or whether or not duplicate elements are + * permitted. + * <p> + * All methods of this interface that are defined to modify the collection are + * defined as <dfn>optional</dfn>. An optional operation may throw an + * UnsupportedOperationException if the data backing this collection does not + * support such a modification. This may mean that the data structure is + * immutable, or that it is read-only but may change ("unmodifiable"), or + * that it is modifiable but of fixed size (such as an array), or any number + * of other combinations. + * <p> + * A class that wishes to implement this interface should consider subclassing + * AbstractCollection, which provides basic implementations of most of the + * methods of this interface. Classes that are prepared to make guarantees + * about ordering or about absence of duplicate elements should consider + * implementing List or Set respectively, both of which are subinterfaces of + * Collection. + * <p> + * A general-purpose implementation of the Collection interface should in most + * cases provide at least two constructors: One which takes no arguments and + * creates an empty collection, and one which takes a Collection as an argument + * and returns a collection containing the same elements (that is, creates a + * copy of the argument using its own implementation). + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @see Set + * @see Map + * @see SortedSet + * @see SortedMap + * @see HashSet + * @see TreeSet + * @see ArrayList + * @see LinkedList + * @see Vector + * @see Collections + * @see Arrays + * @see AbstractCollection + * @since 1.2 + * @status updated to 1.4 + */ +public interface Collection +{ + /** + * Add an element to this collection. + * + * @param o the object to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException if this collection does not + * support the add operation. + * @throws ClassCastException if o cannot be added to this collection due + * to its type. + * @throws NullPointerException if o is null and this collection doesn't + * support the addition of null values. + * @throws IllegalArgumentException if o cannot be added to this + * collection for some other reason. + */ + boolean add(Object o); + + /** + * Add the contents of a given collection to this collection. + * + * @param c the collection to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException if this collection does not + * support the addAll operation. + * @throws ClassCastException if some element of c cannot be added to this + * collection due to its type. + * @throws NullPointerException if some element of c is null and this + * collection does not support the addition of null values. + * @throws NullPointerException if c itself is null. + * @throws IllegalArgumentException if some element of c cannot be added + * to this collection for some other reason. + */ + boolean addAll(Collection c); + + /** + * Clear the collection, such that a subsequent call to isEmpty() would + * return true. + * + * @throws UnsupportedOperationException if this collection does not + * support the clear operation. + */ + void clear(); + + /** + * Test whether this collection contains a given object as one of its + * elements. + * + * @param o the element to look for. + * @return true if this collection contains at least one element e such that + * <code>o == null ? e == null : o.equals(e)</code>. + * @throws ClassCastException if the type of o is not a valid type for this + * collection. + * @throws NullPointerException if o is null and this collection doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Test whether this collection contains every element in a given collection. + * + * @param c the collection to test for. + * @return true if for every element o in c, contains(o) would return true. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support null values. + * @throws NullPointerException if c itself is null. + */ + boolean containsAll(Collection c); + + /** + * Test whether this collection is equal to some object. The Collection + * interface does not explicitly require any behaviour from this method, and + * it may be left to the default implementation provided by Object. The Set + * and List interfaces do, however, require specific behaviour from this + * method. + * <p> + * If an implementation of Collection, which is not also an implementation of + * Set or List, should choose to implement this method, it should take care + * to obey the contract of the equals method of Object. In particular, care + * should be taken to return false when o is a Set or a List, in order to + * preserve the symmetry of the relation. + * + * @param o the object to compare to this collection. + * @return true if the o is equal to this collection. + */ + boolean equals(Object o); + + /** + * Obtain a hash code for this collection. The Collection interface does not + * explicitly require any behaviour from this method, and it may be left to + * the default implementation provided by Object. The Set and List interfaces + * do, however, require specific behaviour from this method. + * <p> + * If an implementation of Collection, which is not also an implementation of + * Set or List, should choose to implement this method, it should take care + * to obey the contract of the hashCode method of Object. Note that this + * method renders it impossible to correctly implement both Set and List, as + * the required implementations are mutually exclusive. + * + * @return a hash code for this collection. + */ + int hashCode(); + + /** + * Test whether this collection is empty, that is, if size() == 0. + * + * @return true if this collection contains no elements. + */ + boolean isEmpty(); + + /** + * Obtain an Iterator over this collection. + * + * @return an Iterator over the elements of this collection, in any order. + */ + Iterator iterator(); + + /** + * Remove a single occurrence of an object from this collection. That is, + * remove an element e, if one exists, such that <code>o == null ? e == null + * : o.equals(e)</code>. + * + * @param o the object to remove. + * @return true if the collection changed as a result of this call, that is, + * if the collection contained at least one occurrence of o. + * @throws UnsupportedOperationException if this collection does not + * support the remove operation. + * @throws ClassCastException if the type of o is not a valid type + * for this collection. + * @throws NullPointerException if o is null and the collection doesn't + * support null values. + */ + boolean remove(Object o); + + /** + * Remove all elements of a given collection from this collection. That is, + * remove every element e such that c.contains(e). + * + * @param c The collection of objects to be removed. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + boolean removeAll(Collection c); + + /** + * Remove all elements of this collection that are not contained in a given + * collection. That is, remove every element e such that !c.contains(e). + * + * @param c The collection of objects to be retained. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the retainAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support retaining null values. + * @throws NullPointerException if c itself is null. + */ + boolean retainAll(Collection c); + + /** + * Get the number of elements in this collection. + * + * @return the number of elements in the collection. + */ + int size(); + + /** + * Copy the current contents of this collection into an array. + * + * @return an array of type Object[] and length equal to the size of this + * collection, containing the elements currently in this collection, in + * any order. + */ + Object[] toArray(); + + /** + * Copy the current contents of this collection into an array. If the array + * passed as an argument has length less than the size of this collection, an + * array of the same run-time type as a, and length equal to the size of this + * collection, is allocated using Reflection. Otherwise, a itself is used. + * The elements of this collection are copied into it, and if there is space + * in the array, the following element is set to null. The resultant array is + * returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this collection does not contain any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in this collection, in + * any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + Object[] toArray(Object[] a); +} diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java new file mode 100644 index 00000000000..ed8a00500c6 --- /dev/null +++ b/libjava/classpath/java/util/Collections.java @@ -0,0 +1,5493 @@ +/* Collections.java -- Utility class with methods to operate on collections + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * Utility class consisting of static methods that operate on, or return + * Collections. Contains methods to sort, search, reverse, fill and shuffle + * Collections, methods to facilitate interoperability with legacy APIs that + * are unaware of collections, a method to return a list which consists of + * multiple copies of one element, and methods which "wrap" collections to give + * them extra properties, such as thread-safety and unmodifiability. + * <p> + * + * All methods which take a collection throw a {@link NullPointerException} if + * that collection is null. Algorithms which can change a collection may, but + * are not required, to throw the {@link UnsupportedOperationException} that + * the underlying collection would throw during an attempt at modification. + * For example, + * <code>Collections.singleton("").addAll(Collections.EMPTY_SET)</code> + * does not throw a exception, even though addAll is an unsupported operation + * on a singleton; the reason for this is that addAll did not attempt to + * modify the set. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see List + * @see Map + * @see Arrays + * @since 1.2 + * @status updated to 1.4 + */ +public class Collections +{ + /** + * Constant used to decide cutoff for when a non-RandomAccess list should + * be treated as sequential-access. Basically, quadratic behavior is + * acceptable for small lists when the overhead is so small in the first + * place. I arbitrarily set it to 16, so it may need some tuning. + */ + private static final int LARGE_LIST_SIZE = 16; + + /** + * Determines if a list should be treated as a sequential-access one. + * Rather than the old method of JDK 1.3 of assuming only instanceof + * AbstractSequentialList should be sequential, this uses the new method + * of JDK 1.4 of assuming anything that does NOT implement RandomAccess + * and exceeds a large (unspecified) size should be sequential. + * + * @param l the list to check + * @return <code>true</code> if it should be treated as sequential-access + */ + private static boolean isSequential(List l) + { + return ! (l instanceof RandomAccess) && l.size() > LARGE_LIST_SIZE; + } + + /** + * This class is non-instantiable. + */ + private Collections() + { + } + + /** + * An immutable, serializable, empty Set. + * @see Serializable + */ + public static final Set EMPTY_SET = new EmptySet(); + + /** + * The implementation of {@link #EMPTY_SET}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptySet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1582296315990362920L; + + /** + * A private constructor adds overhead. + */ + EmptySet() + { + } + + /** + * The size: always 0! + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * Returns an iterator that does not iterate. + * @return A non-iterating iterator. + */ + // This is really cheating! I think it's perfectly valid, though. + public Iterator iterator() + { + return EMPTY_LIST.iterator(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The empty set never contains anything. + * @param o The object to search for. + * @return <code>false</code>. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects which are to be compared + * against the members of this set. + * @return <code>true</code> if c is empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other set is empty. + * @param o The object to compare with this set. + * @return <code>true</code> if o is an empty instance of <code>Set</code>. + */ + public boolean equals(Object o) + { + return o instanceof Set && ((Set) o).isEmpty(); + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param o The object to remove. + * @return <code>false</code>. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param c The collection of objects which should + * all be removed from this set. + * @return <code>false</code>. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param c The collection of objects which should + * all be retained within this set. + * @return <code>false</code>. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public Object[] toArray(Object[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptySet + + /** + * An immutable, serializable, empty List, which implements RandomAccess. + * @see Serializable + * @see RandomAccess + */ + public static final List EMPTY_LIST = new EmptyList(); + + /** + * The implementation of {@link #EMPTY_LIST}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8842843931221139166L; + + /** + * A private constructor adds overhead. + */ + EmptyList() + { + } + + /** + * The size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No matter the index, it is out of bounds. This + * method never returns, throwing an exception instead. + * + * @param index The index of the element to retrieve. + * @return the object at the specified index. + * @throws IndexOutOfBoundsException as any given index + * is outside the bounds of an empty array. + */ + public Object get(int index) + { + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * Never contains anything. + * @param o The object to search for. + * @return <code>false</code>. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects, which should be compared + * against the members of this list. + * @return <code>true</code> if c is also empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other list is empty. + * @param o The object to compare against this list. + * @return <code>true</code> if o is also an empty instance of + * <code>List</code>. + */ + public boolean equals(Object o) + { + return o instanceof List && ((List) o).isEmpty(); + } + + /** + * The hashcode is always 1. + * @return 1. + */ + public int hashCode() + { + return 1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int indexOf(Object o) + { + return -1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int lastIndexOf(Object o) + { + return -1; + } + + /** + * Always succeeds with <code>false</code> result. + * @param o The object to remove. + * @return -1. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with <code>false</code> result. + * @param c The collection of objects which should + * all be removed from this list. + * @return <code>false</code>. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with <code>false</code> result. + * @param c The collection of objects which should + * all be retained within this list. + * @return <code>false</code>. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public Object[] toArray(Object[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyList + + /** + * An immutable, serializable, empty Map. + * @see Serializable + */ + public static final Map EMPTY_MAP = new EmptyMap(); + + /** + * The implementation of {@link #EMPTY_MAP}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 6428348081105594320L; + + /** + * A private constructor adds overhead. + */ + EmptyMap() + { + } + + /** + * There are no entries. + * @return The empty set. + */ + public Set entrySet() + { + return EMPTY_SET; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * No entries! + * @param key The key to search for. + * @return <code>false</code>. + */ + public boolean containsKey(Object key) + { + return false; + } + + /** + * No entries! + * @param value The value to search for. + * @return <code>false</code>. + */ + public boolean containsValue(Object value) + { + return false; + } + + /** + * Equal to all empty maps. + * @param o The object o to compare against this map. + * @return <code>true</code> if o is also an empty instance of + * <code>Map</code>. + */ + public boolean equals(Object o) + { + return o instanceof Map && ((Map) o).isEmpty(); + } + + /** + * No mappings, so this returns null. + * @param o The key of the object to retrieve. + * @return null. + */ + public Object get(Object o) + { + return null; + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * No entries. + * @return The empty set. + */ + public Set keySet() + { + return EMPTY_SET; + } + + /** + * Remove always succeeds, with null result. + * @param o The key of the mapping to remove. + * @return null, as there is never a mapping for o. + */ + public Object remove(Object o) + { + return null; + } + + /** + * Size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No entries. Technically, EMPTY_SET, while more specific than a general + * Collection, will work. Besides, that's what the JDK uses! + * @return The empty set. + */ + public Collection values() + { + return EMPTY_SET; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyMap + + + /** + * Compare two objects with or without a Comparator. If c is null, uses the + * natural ordering. Slightly slower than doing it inline if the JVM isn't + * clever, but worth it for removing a duplicate of the search code. + * Note: This code is also used in Arrays (for sort as well as search). + */ + static final int compare(Object o1, Object o2, Comparator c) + { + return c == null ? ((Comparable) o1).compareTo(o2) : c.compare(o1, o2); + } + + /** + * Perform a binary search of a List for a key, using the natural ordering of + * the elements. The list must be sorted (as by the sort() method) - if it is + * not, the behavior of this method is undefined, and may be an infinite + * loop. Further, the key must be comparable with every item in the list. If + * the list contains the key more than once, any one of them may be found. + * <p> + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element has compareTo called + * @see #sort(List) + */ + public static int binarySearch(List l, Object key) + { + return binarySearch(l, key, null); + } + + /** + * Perform a binary search of a List for a key, using a supplied Comparator. + * The list must be sorted (as by the sort() method with the same Comparator) + * - if it is not, the behavior of this method is undefined, and may be an + * infinite loop. Further, the key must be comparable with every item in the + * list. If the list contains the key more than once, any one of them may be + * found. If the comparator is null, the elements' natural ordering is used. + * <p> + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @param c the comparator by which the list is sorted + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + * @see #sort(List, Comparator) + */ + public static int binarySearch(List l, Object key, Comparator c) + { + int pos = 0; + int low = 0; + int hi = l.size() - 1; + + // We use a linear search with log(n) comparisons using an iterator + // if the list is sequential-access. + if (isSequential(l)) + { + ListIterator itr = l.listIterator(); + int i = 0; + Object o = itr.next(); // Assumes list is not empty (see isSequential) + boolean forward = true; + while (low <= hi) + { + pos = (low + hi) >> 1; + if (i < pos) + { + if (!forward) + itr.next(); // Changing direction first. + for ( ; i != pos; i++, o = itr.next()); + forward = true; + } + else + { + if (forward) + itr.previous(); // Changing direction first. + for ( ; i != pos; i--, o = itr.previous()); + forward = false; + } + final int d = compare(key, o, c); + if (d == 0) + return pos; + else if (d < 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + else + { + while (low <= hi) + { + pos = (low + hi) >> 1; + final int d = compare(key, l.get(pos), c); + if (d == 0) + return pos; + else if (d < 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + + // If we failed to find it, we do the same whichever search we did. + return -pos - 1; + } + + /** + * Copy one list to another. If the destination list is longer than the + * source list, the remaining elements are unaffected. This method runs in + * linear time. + * + * @param dest the destination list + * @param source the source list + * @throws IndexOutOfBoundsException if the destination list is shorter + * than the source list (the destination will be unmodified) + * @throws UnsupportedOperationException if dest.listIterator() does not + * support the set operation + */ + public static void copy(List dest, List source) + { + int pos = source.size(); + if (dest.size() < pos) + throw new IndexOutOfBoundsException("Source does not fit in dest"); + + Iterator i1 = source.iterator(); + ListIterator i2 = dest.listIterator(); + + while (--pos >= 0) + { + i2.next(); + i2.set(i1.next()); + } + } + + /** + * Returns an Enumeration over a collection. This allows interoperability + * with legacy APIs that require an Enumeration as input. + * + * @param c the Collection to iterate over + * @return an Enumeration backed by an Iterator over c + */ + public static Enumeration enumeration(Collection c) + { + final Iterator i = c.iterator(); + return new Enumeration() + { + /** + * Returns <code>true</code> if there are more elements to + * be enumerated. + * + * @return The result of <code>hasNext()</code> + * called on the underlying iterator. + */ + public final boolean hasMoreElements() + { + return i.hasNext(); + } + + /** + * Returns the next element to be enumerated. + * + * @return The result of <code>next()</code> + * called on the underlying iterator. + */ + public final Object nextElement() + { + return i.next(); + } + }; + } + + /** + * Replace every element of a list with a given value. This method runs in + * linear time. + * + * @param l the list to fill. + * @param val the object to vill the list with. + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation. + */ + public static void fill(List l, Object val) + { + ListIterator itr = l.listIterator(); + for (int i = l.size() - 1; i >= 0; --i) + { + itr.next(); + itr.set(val); + } + } + + /** + * Returns the starting index where the specified sublist first occurs + * in a larger list, or -1 if there is no matching position. If + * <code>target.size() > source.size()</code>, this returns -1, + * otherwise this implementation uses brute force, checking for + * <code>source.sublist(i, i + target.size()).equals(target)</code> + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int indexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = 0, j = target.size(); j <= ssize; i++, j++) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns the starting index where the specified sublist last occurs + * in a larger list, or -1 if there is no matching position. If + * <code>target.size() > source.size()</code>, this returns -1, + * otherwise this implementation uses brute force, checking for + * <code>source.sublist(i, i + target.size()).equals(target)</code> + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int lastIndexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = ssize - target.size(), j = ssize; i >= 0; i--, j--) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns an ArrayList holding the elements visited by a given + * Enumeration. This method exists for interoperability between legacy + * APIs and the new Collection API. + * + * @param e the enumeration to put in a list + * @return a list containing the enumeration elements + * @see ArrayList + * @since 1.4 + */ + public static ArrayList list(Enumeration e) + { + ArrayList l = new ArrayList(); + while (e.hasMoreElements()) + l.add(e.nextElement()); + return l; + } + + /** + * Find the maximum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the maximum element of + * @return the maximum element of c + * @exception NoSuchElementException if c is empty + * @exception ClassCastException if elements in c are not mutually comparable + * @exception NullPointerException if null.compareTo is called + */ + public static Object max(Collection c) + { + return max(c, null); + } + + /** + * Find the maximum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the maximum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the maximum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static Object max(Collection c, Comparator order) + { + Iterator itr = c.iterator(); + Object max = itr.next(); // throws NoSuchElementException + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + Object o = itr.next(); + if (compare(max, o, order) < 0) + max = o; + } + return max; + } + + /** + * Find the minimum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the minimum element of + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null.compareTo is called + */ + public static Object min(Collection c) + { + return min(c, null); + } + + /** + * Find the minimum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the minimum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static Object min(Collection c, Comparator order) + { + Iterator itr = c.iterator(); + Object min = itr.next(); // throws NoSuchElementExcception + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + Object o = itr.next(); + if (compare(min, o, order) > 0) + min = o; + } + return min; + } + + /** + * Creates an immutable list consisting of the same object repeated n times. + * The returned object is tiny, consisting of only a single reference to the + * object and a count of the number of elements. It is Serializable, and + * implements RandomAccess. You can use it in tandem with List.addAll for + * fast list construction. + * + * @param n the number of times to repeat the object + * @param o the object to repeat + * @return a List consisting of n copies of o + * @throws IllegalArgumentException if n < 0 + * @see List#addAll(Collection) + * @see Serializable + * @see RandomAccess + */ + public static List nCopies(final int n, final Object o) + { + return new CopiesList(n, o); + } + + /** + * The implementation of {@link #nCopies(int, Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class CopiesList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 2739099268398711800L; + + /** + * The count of elements in this list. + * @serial the list size + */ + private final int n; + + /** + * The repeated list element. + * @serial the list contents + */ + private final Object element; + + /** + * Constructs the list. + * + * @param n the count + * @param o the object + * @throws IllegalArgumentException if n < 0 + */ + CopiesList(int n, Object o) + { + if (n < 0) + throw new IllegalArgumentException(); + this.n = n; + element = o; + } + + /** + * The size is fixed. + * @return The size of the list. + */ + public int size() + { + return n; + } + + /** + * The same element is returned. + * @param index The index of the element to be returned (irrelevant + * as the list contains only copies of <code>element</code>). + * @return The element used by this list. + */ + public Object get(int index) + { + if (index < 0 || index >= n) + throw new IndexOutOfBoundsException(); + return element; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * This list only contains one element. + * @param o The object to search for. + * @return <code>true</code> if o is the element used by this list. + */ + public boolean contains(Object o) + { + return n > 0 && equals(o, element); + } + + /** + * The index is either 0 or -1. + * @param o The object to find the index of. + * @return 0 if <code>o == element</code>, -1 if not. + */ + public int indexOf(Object o) + { + return (n > 0 && equals(o, element)) ? 0 : -1; + } + + /** + * The index is either n-1 or -1. + * @param o The object to find the last index of. + * @return The last index in the list if <code>o == element</code>, + * -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? n - 1 : -1; + } + + /** + * A subList is just another CopiesList. + * @param from The starting bound of the sublist. + * @param to The ending bound of the sublist. + * @return A list of copies containing <code>from - to</code> + * elements, all of which are equal to the element + * used by this list. + */ + public List subList(int from, int to) + { + if (from < 0 || to > n) + throw new IndexOutOfBoundsException(); + return new CopiesList(to - from, element); + } + + /** + * The array is easy. + * @return An array of size n filled with copies of + * the element used by this list. + */ + public Object[] toArray() + { + Object[] a = new Object[n]; + Arrays.fill(a, element); + return a; + } + + /** + * The string is easy to generate. + * @return A string representation of the list. + */ + public String toString() + { + StringBuffer r = new StringBuffer("{"); + for (int i = n - 1; --i > 0; ) + r.append(element).append(", "); + r.append(element).append("}"); + return r.toString(); + } + } // class CopiesList + + /** + * Replace all instances of one object with another in the specified list. + * The list does not change size. An element e is replaced if + * <code>oldval == null ? e == null : oldval.equals(e)</code>. + * + * @param list the list to iterate over + * @param oldval the element to replace + * @param newval the new value for the element + * @return <code>true</code> if a replacement occurred. + * @throws UnsupportedOperationException if the list iterator does not allow + * for the set operation + * @throws ClassCastException if newval is of a type which cannot be added + * to the list + * @throws IllegalArgumentException if some other aspect of newval stops + * it being added to the list + * @since 1.4 + */ + public static boolean replaceAll(List list, Object oldval, Object newval) + { + ListIterator itr = list.listIterator(); + boolean replace_occured = false; + for (int i = list.size(); --i >= 0; ) + if (AbstractCollection.equals(oldval, itr.next())) + { + itr.set(newval); + replace_occured = true; + } + return replace_occured; + } + + /** + * Reverse a given list. This method works in linear time. + * + * @param l the list to reverse + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void reverse(List l) + { + ListIterator i1 = l.listIterator(); + int pos1 = 1; + int pos2 = l.size(); + ListIterator i2 = l.listIterator(pos2); + while (pos1 < pos2) + { + Object o = i1.next(); + i1.set(i2.previous()); + i2.set(o); + ++pos1; + --pos2; + } + } + + /** + * Get a comparator that implements the reverse of natural ordering. In + * other words, this sorts Comparable objects opposite of how their + * compareTo method would sort. This makes it easy to sort into reverse + * order, by simply passing Collections.reverseOrder() to the sort method. + * The return value of this method is Serializable. + * + * @return a comparator that imposes reverse natural ordering + * @see Comparable + * @see Serializable + */ + public static Comparator reverseOrder() + { + return rcInstance; + } + + /** + * The object for {@link #reverseOrder()}. + */ + private static final ReverseComparator rcInstance = new ReverseComparator(); + + /** + * The implementation of {@link #reverseOrder()}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ReverseComparator + implements Comparator, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7207038068494060240L; + + /** + * A private constructor adds overhead. + */ + ReverseComparator() + { + } + + /** + * Compare two objects in reverse natural order. + * + * @param a the first object + * @param b the second object + * @return <, ==, or > 0 according to b.compareTo(a) + */ + public int compare(Object a, Object b) + { + return ((Comparable) b).compareTo(a); + } + } + + /** + * Rotate the elements in a list by a specified distance. After calling this + * method, the element now at index <code>i</code> was formerly at index + * <code>(i - distance) mod list.size()</code>. The list size is unchanged. + * <p> + * + * For example, suppose a list contains <code>[t, a, n, k, s]</code>. After + * either <code>Collections.rotate(l, 4)</code> or + * <code>Collections.rotate(l, -1)</code>, the new contents are + * <code>[s, t, a, n, k]</code>. This can be applied to sublists to rotate + * just a portion of the list. For example, to move element <code>a</code> + * forward two positions in the original example, use + * <code>Collections.rotate(l.subList(1, 3+1), -1)</code>, which will + * result in <code>[t, n, k, a, s]</code>. + * <p> + * + * If the list is small or implements {@link RandomAccess}, the + * implementation exchanges the first element to its destination, then the + * displaced element, and so on until a circuit has been completed. The + * process is repeated if needed on the second element, and so forth, until + * all elements have been swapped. For large non-random lists, the + * implementation breaks the list into two sublists at index + * <code>-distance mod size</code>, calls {@link #reverse(List)} on the + * pieces, then reverses the overall list. + * + * @param list the list to rotate + * @param distance the distance to rotate by; unrestricted in value + * @throws UnsupportedOperationException if the list does not support set + * @since 1.4 + */ + public static void rotate(List list, int distance) + { + int size = list.size(); + if (size == 0) + return; + distance %= size; + if (distance == 0) + return; + if (distance < 0) + distance += size; + + if (isSequential(list)) + { + reverse(list); + reverse(list.subList(0, distance)); + reverse(list.subList(distance, size)); + } + else + { + // Determine the least common multiple of distance and size, as there + // are (distance / LCM) loops to cycle through. + int a = size; + int lcm = distance; + int b = a % lcm; + while (b != 0) + { + a = lcm; + lcm = b; + b = a % lcm; + } + + // Now, make the swaps. We must take the remainder every time through + // the inner loop so that we don't overflow i to negative values. + while (--lcm >= 0) + { + Object o = list.get(lcm); + for (int i = lcm + distance; i != lcm; i = (i + distance) % size) + o = list.set(i, o); + list.set(lcm, o); + } + } + } + + /** + * Shuffle a list according to a default source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + * <p> + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practice the results are merely very + * close to perfect. + * <p> + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l) + { + if (defaultRandom == null) + { + synchronized (Collections.class) + { + if (defaultRandom == null) + defaultRandom = new Random(); + } + } + shuffle(l, defaultRandom); + } + + /** + * Cache a single Random object for use by shuffle(List). This improves + * performance as well as ensuring that sequential calls to shuffle() will + * not result in the same shuffle order occurring: the resolution of + * System.currentTimeMillis() is not sufficient to guarantee a unique seed. + */ + private static Random defaultRandom = null; + + /** + * Shuffle a list according to a given source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + * <p> + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practise (eg if r = new Random()) the + * results are merely very close to perfect. + * <p> + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @param r the source of randomness to use for the shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l, Random r) + { + int lsize = l.size(); + ListIterator i = l.listIterator(lsize); + boolean sequential = isSequential(l); + Object[] a = null; // stores a copy of the list for the sequential case + + if (sequential) + a = l.toArray(); + + for (int pos = lsize - 1; pos > 0; --pos) + { + // Obtain a random position to swap with. pos + 1 is used so that the + // range of the random number includes the current position. + int swap = r.nextInt(pos + 1); + + // Swap the desired element. + Object o; + if (sequential) + { + o = a[swap]; + a[swap] = i.previous(); + } + else + o = l.set(swap, i.previous()); + + i.set(o); + } + } + + + /** + * Obtain an immutable Set consisting of a single element. The return value + * of this method is Serializable. + * + * @param o the single element + * @return an immutable Set containing only o + * @see Serializable + */ + public static Set singleton(Object o) + { + return new SingletonSet(o); + } + + /** + * The implementation of {@link #singleton(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonSet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3193687207550431679L; + + + /** + * The single element; package visible for use in nested class. + * @serial the singleton + */ + final Object element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonSet(Object o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Returns an iterator over the lone element. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * Flag to indicate whether or not the element has + * been retrieved. + */ + private boolean hasNext = true; + + /** + * Returns <code>true</code> if elements still remain to be + * iterated through. + * + * @return <code>true</code> if the element has not yet been returned. + */ + public boolean hasNext() + { + return hasNext; + } + + /** + * Returns the element. + * + * @return The element used by this singleton. + * @throws NoSuchElementException if the object + * has already been retrieved. + */ + public Object next() + { + if (hasNext) + { + hasNext = false; + return element; + } + else + throw new NoSuchElementException(); + } + + /** + * Removes the element from the singleton. + * As this set is immutable, this will always + * throw an exception. + * + * @throws UnsupportedOperationException as the + * singleton set doesn't support + * <code>remove()</code>. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return <code>true</code> if o == the element of the singleton. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return <code>true</code> if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * The hash is just that of the element. + * + * @return The hashcode of the element. + */ + public int hashCode() + { + return hashCode(element); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonSet + + /** + * Obtain an immutable List consisting of a single element. The return value + * of this method is Serializable, and implements RandomAccess. + * + * @param o the single element + * @return an immutable List containing only o + * @see Serializable + * @see RandomAccess + * @since 1.3 + */ + public static List singletonList(Object o) + { + return new SingletonList(o); + } + + /** + * The implementation of {@link #singletonList(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3093736618740652951L; + + /** + * The single element. + * @serial the singleton + */ + private final Object element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonList(Object o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Only index 0 is valid. + * @param index The index of the element + * to retrieve. + * @return The singleton's element if the + * index is 0. + * @throws IndexOutOfBoundsException if + * index is not 0. + */ + public Object get(int index) + { + if (index == 0) + return element; + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return <code>true</code> if o == the singleton element. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return <code>true</code> if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * Speed up the hashcode computation. + * + * @return The hashcode of the list, based + * on the hashcode of the singleton element. + */ + public int hashCode() + { + return 31 + hashCode(element); + } + + /** + * Either the list has it or not. + * + * @param o The object to find the first index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int indexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Either the list has it or not. + * + * @param o The object to find the last index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Sublists are limited in scope. + * + * @param from The starting bound for the sublist. + * @param to The ending bound for the sublist. + * @return Either an empty list if both bounds are + * 0 or 1, or this list if the bounds are 0 and 1. + * @throws IllegalArgumentException if <code>from > to</code> + * @throws IndexOutOfBoundsException if either bound is greater + * than 1. + */ + public List subList(int from, int to) + { + if (from == to && (to == 0 || to == 1)) + return EMPTY_LIST; + if (from == 0 && to == 1) + return this; + if (from > to) + throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException(); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonList + + /** + * Obtain an immutable Map consisting of a single key-value pair. + * The return value of this method is Serializable. + * + * @param key the single key + * @param value the single value + * @return an immutable Map containing only the single key-value pair + * @see Serializable + * @since 1.3 + */ + public static Map singletonMap(Object key, Object value) + { + return new SingletonMap(key, value); + } + + /** + * The implementation of {@link #singletonMap(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -6979724477215052911L; + + /** + * The single key. + * @serial the singleton key + */ + private final Object k; + + /** + * The corresponding value. + * @serial the singleton value + */ + private final Object v; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Construct a singleton. + * @param key the key + * @param value the value + */ + SingletonMap(Object key, Object value) + { + k = key; + v = value; + } + + /** + * There is a single immutable entry. + * + * @return A singleton containing the map entry. + */ + public Set entrySet() + { + if (entries == null) + entries = singleton(new AbstractMap.BasicMapEntry(k, v) + { + /** + * Sets the value of the map entry to the supplied value. + * An exception is always thrown, as the map is immutable. + * + * @param o The new value. + * @return The old value. + * @throws UnsupportedOperationException as setting the value + * is not supported. + */ + public Object setValue(Object o) + { + throw new UnsupportedOperationException(); + } + }); + return entries; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * Single entry. + * + * @param key The key to look for. + * @return <code>true</code> if the key is the same as the one used by + * this map. + */ + public boolean containsKey(Object key) + { + return equals(key, k); + } + + /** + * Single entry. + * + * @param value The value to look for. + * @return <code>true</code> if the value is the same as the one used by + * this map. + */ + public boolean containsValue(Object value) + { + return equals(value, v); + } + + /** + * Single entry. + * + * @param key The key of the value to be retrieved. + * @return The singleton value if the key is the same as the + * singleton key, null otherwise. + */ + public Object get(Object key) + { + return equals(key, k) ? v : null; + } + + /** + * Calculate the hashcode directly. + * + * @return The hashcode computed from the singleton key + * and the singleton value. + */ + public int hashCode() + { + return hashCode(k) ^ hashCode(v); + } + + /** + * Return the keyset. + * + * @return A singleton containing the key. + */ + public Set keySet() + { + if (keys == null) + keys = singleton(k); + return keys; + } + + /** + * The size: always 1! + * + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Return the values. Technically, a singleton, while more specific than + * a general Collection, will work. Besides, that's what the JDK uses! + * + * @return A singleton containing the value. + */ + public Collection values() + { + if (values == null) + values = singleton(v); + return values; + } + + /** + * Obvious string. + * + * @return A string containing the string representations of the key + * and its associated value. + */ + public String toString() + { + return "{" + k + "=" + v + "}"; + } + } // class SingletonMap + + /** + * Sort a list according to the natural ordering of its elements. The list + * must be modifiable, but can be of fixed size. The sort algorithm is + * precisely that used by Arrays.sort(Object[]), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort + * @throws ClassCastException if some items are not mutually comparable + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if some element is null + * @see Arrays#sort(Object[]) + */ + public static void sort(List l) + { + sort(l, null); + } + + /** + * Sort a list according to a specified Comparator. The list must be + * modifiable, but can be of fixed size. The sort algorithm is precisely that + * used by Arrays.sort(Object[], Comparator), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort + * @param c the Comparator specifying the ordering for the elements, or + * null for natural ordering + * @throws ClassCastException if c will not compare some pair of items + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when c is null) + * @see Arrays#sort(Object[], Comparator) + */ + public static void sort(List l, Comparator c) + { + Object[] a = l.toArray(); + Arrays.sort(a, c); + ListIterator i = l.listIterator(); + for (int pos = 0, alen = a.length; pos < alen; pos++) + { + i.next(); + i.set(a[pos]); + } + } + + /** + * Swaps the elements at the specified positions within the list. Equal + * positions have no effect. + * + * @param l the list to work on + * @param i the first index to swap + * @param j the second index + * @throws UnsupportedOperationException if list.set is not supported + * @throws IndexOutOfBoundsException if either i or j is < 0 or >= + * list.size() + * @since 1.4 + */ + public static void swap(List l, int i, int j) + { + l.set(i, l.set(j, l.get(i))); + } + + + /** + * Returns a synchronized (thread-safe) collection wrapper backed by the + * given collection. Notice that element access through the iterators + * is thread-safe, but if the collection can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * Collection c = Collections.synchronizedCollection(new Collection(...)); + * ... + * synchronized (c) + * { + * Iterator i = c.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a synchronized view of the collection + * @see Serializable + */ + public static Collection synchronizedCollection(Collection c) + { + return new SynchronizedCollection(c); + } + + /** + * The implementation of {@link #synchronizedCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * Package visible, so that collections such as the one for + * Hashtable.values() can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3053995032091335093L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like SynchronizedMap.values() + * must specify another mutex. Package visible for use by subclasses. + * @serial the lock + */ + final Object mutex; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + SynchronizedCollection(Collection c) + { + this.c = c; + mutex = this; + if (c == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param c the collection + */ + SynchronizedCollection(Object sync, Collection c) + { + this.c = c; + mutex = sync; + } + + /** + * Adds the object to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param o The object to add. + * @return <code>true</code> if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the add operation. + * @throws ClassCastException if o cannot be added to this collection due + * to its type. + * @throws NullPointerException if o is null and this collection doesn't + * support the addition of null values. + * @throws IllegalArgumentException if o cannot be added to this + * collection for some other reason. + */ + public boolean add(Object o) + { + synchronized (mutex) + { + return c.add(o); + } + } + + /** + * Adds the objects in col to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param col The collection to take the new objects from. + * @return <code>true</code> if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the addAll operation. + * @throws ClassCastException if some element of col cannot be added to this + * collection due to its type. + * @throws NullPointerException if some element of col is null and this + * collection does not support the addition of null values. + * @throws NullPointerException if col itself is null. + * @throws IllegalArgumentException if some element of col cannot be added + * to this collection for some other reason. + */ + public boolean addAll(Collection col) + { + synchronized (mutex) + { + return c.addAll(col); + } + } + + /** + * Removes all objects from the underlying collection, + * first obtaining a lock on the mutex. + * + * @throws UnsupportedOperationException if this collection does not + * support the clear operation. + */ + public void clear() + { + synchronized (mutex) + { + c.clear(); + } + } + + /** + * Checks for the existence of o within the underlying + * collection, first obtaining a lock on the mutex. + * + * @param o the element to look for. + * @return <code>true</code> if this collection contains at least one + * element e such that <code>o == null ? e == null : o.equals(e)</code>. + * @throws ClassCastException if the type of o is not a valid type for this + * collection. + * @throws NullPointerException if o is null and this collection doesn't + * support null values. + */ + public boolean contains(Object o) + { + synchronized (mutex) + { + return c.contains(o); + } + } + + /** + * Checks for the existence of each object in cl + * within the underlying collection, first obtaining + * a lock on the mutex. + * + * @param c1 the collection to test for. + * @return <code>true</code> if for every element o in c, contains(o) + * would return <code>true</code>. + * @throws ClassCastException if the type of any element in cl is not a valid + * type for this collection. + * @throws NullPointerException if some element of cl is null and this + * collection does not support null values. + * @throws NullPointerException if cl itself is null. + */ + public boolean containsAll(Collection c1) + { + synchronized (mutex) + { + return c.containsAll(c1); + } + } + + /** + * Returns <code>true</code> if there are no objects in the underlying + * collection. A lock on the mutex is obtained before the + * check is performed. + * + * @return <code>true</code> if this collection contains no elements. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return c.isEmpty(); + } + } + + /** + * Returns a synchronized iterator wrapper around the underlying + * collection's iterator. A lock on the mutex is obtained before + * retrieving the collection's iterator. + * + * @return An iterator over the elements in the underlying collection, + * which returns each element in any order. + */ + public Iterator iterator() + { + synchronized (mutex) + { + return new SynchronizedIterator(mutex, c.iterator()); + } + } + + /** + * Removes the specified object from the underlying collection, + * first obtaining a lock on the mutex. + * + * @param o The object to remove. + * @return <code>true</code> if the collection changed as a result of this call, that is, + * if the collection contained at least one occurrence of o. + * @throws UnsupportedOperationException if this collection does not + * support the remove operation. + * @throws ClassCastException if the type of o is not a valid type + * for this collection. + * @throws NullPointerException if o is null and the collection doesn't + * support null values. + */ + public boolean remove(Object o) + { + synchronized (mutex) + { + return c.remove(o); + } + } + + /** + * Removes all elements, e, of the underlying + * collection for which <code>col.contains(e)</code> + * returns <code>true</code>. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return <code>true</code> if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean removeAll(Collection col) + { + synchronized (mutex) + { + return c.removeAll(col); + } + } + + /** + * Retains all elements, e, of the underlying + * collection for which <code>col.contains(e)</code> + * returns <code>true</code>. That is, every element that doesn't + * exist in col is removed. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return <code>true</code> if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean retainAll(Collection col) + { + synchronized (mutex) + { + return c.retainAll(col); + } + } + + /** + * Retrieves the size of the underlying collection. + * A lock on the mutex is obtained before the collection + * is accessed. + * + * @return The size of the collection. + */ + public int size() + { + synchronized (mutex) + { + return c.size(); + } + } + + /** + * Returns an array containing each object within the underlying + * collection. A lock is obtained on the mutex before the collection + * is accessed. + * + * @return An array of objects, matching the collection in size. The + * elements occur in any order. + */ + public Object[] toArray() + { + synchronized (mutex) + { + return c.toArray(); + } + } + + /** + * Copies the elements in the underlying collection to the supplied + * array. If <code>a.length < size()</code>, a new array of the + * same run-time type is created, with a size equal to that of + * the collection. If <code>a.length > size()</code>, then the + * elements from 0 to <code>size() - 1</code> contain the elements + * from this collection. The following element is set to null + * to indicate the end of the collection objects. However, this + * only makes a difference if null is not a permitted value within + * the collection. + * Before the copying takes place, a lock is obtained on the mutex. + * + * @param a An array to copy elements to. + * @return An array containing the elements of the underlying collection. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public Object[] toArray(Object[] a) + { + synchronized (mutex) + { + return c.toArray(a); + } + } + + /** + * Returns a string representation of the underlying collection. + * A lock is obtained on the mutex before the string is created. + * + * @return A string representation of the collection. + */ + public String toString() + { + synchronized (mutex) + { + return c.toString(); + } + } + } // class SynchronizedCollection + + /** + * The implementation of the various iterator methods in the + * synchronized classes. These iterators must "sync" on the same object + * as the collection they iterate over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedIterator implements Iterator + { + /** + * The object to synchronize on. Package visible for use by subclass. + */ + final Object mutex; + + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param i the wrapped iterator + */ + SynchronizedIterator(Object sync, Iterator i) + { + this.i = i; + mutex = sync; + } + + /** + * Retrieves the next object in the underlying collection. + * A lock is obtained on the mutex before the collection is accessed. + * + * @return The next object in the collection. + * @throws NoSuchElementException if there are no more elements + */ + public Object next() + { + synchronized (mutex) + { + return i.next(); + } + } + + /** + * Returns <code>true</code> if objects can still be retrieved from the iterator + * using <code>next()</code>. A lock is obtained on the mutex before + * the collection is accessed. + * + * @return <code>true</code> if at least one element is still to be returned by + * <code>next()</code>. + */ + public boolean hasNext() + { + synchronized (mutex) + { + return i.hasNext(); + } + } + + /** + * Removes the object that was last returned by <code>next()</code> + * from the underlying collection. Only one call to this method is + * allowed per call to the <code>next()</code> method, and it does + * not affect the value that will be returned by <code>next()</code>. + * Thus, if element n was retrieved from the collection by + * <code>next()</code>, it is this element that gets removed. + * Regardless of whether this takes place or not, element n+1 is + * still returned on the subsequent <code>next()</code> call. + * + * @throws IllegalStateException if next has not yet been called or remove + * has already been called since the last call to next. + * @throws UnsupportedOperationException if this Iterator does not support + * the remove operation. + */ + public void remove() + { + synchronized (mutex) + { + i.remove(); + } + } + } // class SynchronizedIterator + + /** + * Returns a synchronized (thread-safe) list wrapper backed by the + * given list. Notice that element access through the iterators + * is thread-safe, but if the list can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * List l = Collections.synchronizedList(new List(...)); + * ... + * synchronized (l) + * { + * Iterator i = l.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a synchronized view of the list + * @see Serializable + * @see RandomAccess + */ + public static List synchronizedList(List l) + { + if (l instanceof RandomAccess) + return new SynchronizedRandomAccessList(l); + return new SynchronizedList(l); + } + + /** + * The implementation of {@link #synchronizedList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. Package visible, so that lists such as Vector.subList() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedList extends SynchronizedCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -7754090372962971524L; + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedList(List l) + { + super(l); + list = l; + } + + /** + * Called only by trusted code to specify the mutex as well as the list. + * @param sync the mutex + * @param l the list + */ + SynchronizedList(Object sync, List l) + { + super(sync, l); + list = l; + } + + /** + * Insert an element into the underlying list at a given position (optional + * operation). This shifts all existing elements from that position to the + * end one index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. Before the + * addition takes place, a lock is obtained on the mutex. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public void add(int index, Object o) + { + synchronized (mutex) + { + list.add(index, o); + } + } + + /** + * Add an element to the end of the underlying list (optional operation). + * If the list imposes restraints on what can be inserted, such as no null + * elements, this should be documented. A lock is obtained on the mutex before + * any of the elements are added. + * + * @param o the object to add + * @return <code>true</code>, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public boolean addAll(int index, Collection c) + { + synchronized (mutex) + { + return list.addAll(index, c); + } + } + + /** + * Tests whether the underlying list is equal to the supplied object. + * The object is deemed to be equal if it is also a <code>List</code> + * of equal size and with the same elements (i.e. each element, e1, + * in list, l1, and each element, e2, in l2, must return <code>true</code> for + * <code>e1 == null ? e2 == null : e1.equals(e2)</code>. Before the + * comparison is made, a lock is obtained on the mutex. + * + * @param o The object to test for equality with the underlying list. + * @return <code>true</code> if o is equal to the underlying list under the above + * definition. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return list.equals(o); + } + } + + /** + * Retrieves the object at the specified index. A lock + * is obtained on the mutex before the list is accessed. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + synchronized (mutex) + { + return list.get(index); + } + } + + /** + * Obtains a hashcode for the underlying list, first obtaining + * a lock on the mutex. The calculation of the hashcode is + * detailed in the documentation for the <code>List</code> + * interface. + * + * @return The hashcode of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return list.hashCode(); + } + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. A lock is obtained on the mutex before the list is + * accessed. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + + public int indexOf(Object o) + { + synchronized (mutex) + { + return list.indexOf(o); + } + } + + /** + * Obtain the last index at which a given object is to be found in this + * underlying list. A lock is obtained on the mutex before the list + * is accessed. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + synchronized (mutex) + { + return list.lastIndexOf(o); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. + * + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + */ + public ListIterator listIterator() + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, list.listIterator()); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. The iterator starts at the + * index supplied, leading to the element at that index being + * the first one returned by <code>next()</code>. Calling + * <code>previous()</code> from this initial position returns + * index - 1. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, list.listIterator(index)); + } + } + + /** + * Remove the element at a given position in the underlying list (optional + * operation). All remaining elements are shifted to the left to fill the gap. + * A lock on the mutex is obtained before the element is removed. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object remove(int index) + { + synchronized (mutex) + { + return list.remove(index); + } + } + + /** + * Replace an element of the underlying list with another object (optional + * operation). A lock is obtained on the mutex before the element is + * replaced. + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation. + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public Object set(int index, Object o) + { + synchronized (mutex) + { + return list.set(index, o); + } + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedList(mutex, list.subList(fromIndex, toIndex)); + } + } + } // class SynchronizedList + + /** + * The implementation of {@link #synchronizedList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedRandomAccessList + extends SynchronizedList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1530674583602358482L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedRandomAccessList(List l) + { + super(l); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param l the list + */ + SynchronizedRandomAccessList(Object sync, List l) + { + super(sync, l); + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. Random accessibility + * is also extended to the new list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedRandomAccessList(mutex, + list.subList(fromIndex, + toIndex)); + } + } + } // class SynchronizedRandomAccessList + + /** + * The implementation of {@link SynchronizedList#listIterator()}. This + * iterator must "sync" on the same object as the list it iterates over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedListIterator + extends SynchronizedIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param li the wrapped iterator + */ + SynchronizedListIterator(Object sync, ListIterator li) + { + super(sync, li); + this.li = li; + } + + /** + * Insert an element into the underlying list at the current position of + * the iterator (optional operation). The element is inserted in between + * the element that would be returned by <code>previous()</code> and the + * element that would be returned by <code>next()</code>. After the + * insertion, a subsequent call to next is unaffected, but + * a call to previous returns the item that was added. The values returned + * by nextIndex() and previousIndex() are incremented. A lock is obtained + * on the mutex before the addition takes place. + * + * @param o the object to insert into the list + * @throws ClassCastException if the object is of a type which cannot be added + * to this list. + * @throws IllegalArgumentException if some other aspect of the object stops + * it being added to this list. + * @throws UnsupportedOperationException if this ListIterator does not + * support the add operation. + */ + public void add(Object o) + { + synchronized (mutex) + { + li.add(o); + } + } + + /** + * Tests whether there are elements remaining in the underlying list + * in the reverse direction. In other words, <code>previous()</code> + * will not fail with a NoSuchElementException. A lock is obtained + * on the mutex before the check takes place. + * + * @return <code>true</code> if the list continues in the reverse direction + */ + public boolean hasPrevious() + { + synchronized (mutex) + { + return li.hasPrevious(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * <code>next()</code>. If hasNext() returns <code>false</code>, this + * returns the list size. A lock is obtained on the mutex before the + * query takes place. + * + * @return the index of the element that would be returned by next() + */ + public int nextIndex() + { + synchronized (mutex) + { + return li.nextIndex(); + } + } + + /** + * Obtain the previous element from the underlying list. Repeated + * calls to previous may be used to iterate backwards over the entire list, + * or calls to next and previous may be used together to go forwards and + * backwards. Alternating calls to next and previous will return the same + * element. A lock is obtained on the mutex before the object is retrieved. + * + * @return the next element in the list in the reverse direction + * @throws NoSuchElementException if there are no more elements + */ + public Object previous() + { + synchronized (mutex) + { + return li.previous(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns <code>false</code>, this returns -1. + * A lock is obtained on the mutex before the query takes place. + * + * @return the index of the element that would be returned by previous() + */ + public int previousIndex() + { + synchronized (mutex) + { + return li.previousIndex(); + } + } + + /** + * Replace the element last returned by a call to <code>next()</code> or + * <code>previous()</code> with a given object (optional operation). This + * method may only be called if neither <code>add()</code> nor + * <code>remove()</code> have been called since the last call to + * <code>next()</code> or <code>previous</code>. A lock is obtained + * on the mutex before the list is modified. + * + * @param o the object to replace the element with + * @throws ClassCastException the object is of a type which cannot be added + * to this list + * @throws IllegalArgumentException some other aspect of the object stops + * it being added to this list + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the set operation + */ + public void set(Object o) + { + synchronized (mutex) + { + li.set(o); + } + } + } // class SynchronizedListIterator + + /** + * Returns a synchronized (thread-safe) map wrapper backed by the given + * map. Notice that element access through the collection views and their + * iterators are thread-safe, but if the map can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * Map m = Collections.synchronizedMap(new Map(...)); + * ... + * Set s = m.keySet(); // safe outside a synchronized block + * synchronized (m) // synch on m, not s + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a synchronized view of the map + * @see Serializable + */ + public static Map synchronizedMap(Map m) + { + return new SynchronizedMap(m); + } + + /** + * The implementation of {@link #synchronizedMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1978198479659022715L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like + * SynchronizedSortedMap.subMap() must specify another mutex. Package + * visible for use by subclass. + * @serial the lock + */ + final Object mutex; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + SynchronizedMap(Map m) + { + this.m = m; + mutex = this; + if (m == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param m the map + */ + SynchronizedMap(Object sync, Map m) + { + this.m = m; + mutex = sync; + } + + /** + * Clears all the entries from the underlying map. A lock is obtained + * on the mutex before the map is cleared. + * + * @throws UnsupportedOperationException if clear is not supported + */ + public void clear() + { + synchronized (mutex) + { + m.clear(); + } + } + + /** + * Returns <code>true</code> if the underlying map contains a entry for the given key. + * A lock is obtained on the mutex before the map is queried. + * + * @param key the key to search for. + * @return <code>true</code> if the underlying map contains the key. + * @throws ClassCastException if the key is of an inappropriate type. + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys. + */ + public boolean containsKey(Object key) + { + synchronized (mutex) + { + return m.containsKey(key); + } + } + + /** + * Returns <code>true</code> if the underlying map contains at least one entry with the + * given value. In other words, returns <code>true</code> if a value v exists where + * <code>(value == null ? v == null : value.equals(v))</code>. This usually + * requires linear time. A lock is obtained on the mutex before the map + * is queried. + * + * @param value the value to search for + * @return <code>true</code> if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + synchronized (mutex) + { + return m.containsValue(value); + } + } + + // This is one of the ickiest cases of nesting I've ever seen. It just + // means "return a SynchronizedSet, except that the iterator() method + // returns an SynchronizedIterator whose next() method returns a + // synchronized wrapper around its normal return value". + public Set entrySet() + { + // Define this here to spare some nesting. + class SynchronizedMapEntry implements Map.Entry + { + final Map.Entry e; + SynchronizedMapEntry(Object o) + { + e = (Map.Entry) o; + } + + /** + * Returns <code>true</code> if the object, o, implements <code>Map.Entry</code> + * with the same key and value as the underlying entry. A lock is + * obtained on the mutex before the comparison takes place. + * + * @param o The object to compare with this entry. + * @return <code>true</code> if o is equivalent to the underlying map entry. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return e.equals(o); + } + } + + /** + * Returns the key used in the underlying map entry. A lock is obtained + * on the mutex before the key is retrieved. + * + * @return The key of the underlying map entry. + */ + public Object getKey() + { + synchronized (mutex) + { + return e.getKey(); + } + } + + /** + * Returns the value used in the underlying map entry. A lock is obtained + * on the mutex before the value is retrieved. + * + * @return The value of the underlying map entry. + */ + public Object getValue() + { + synchronized (mutex) + { + return e.getValue(); + } + } + + /** + * Computes the hash code for the underlying map entry. + * This computation is described in the documentation for the + * <code>Map</code> interface. A lock is obtained on the mutex + * before the underlying map is accessed. + * + * @return The hash code of the underlying map entry. + * @see Map#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return e.hashCode(); + } + } + + /** + * Replaces the value in the underlying map entry with the specified + * object (optional operation). A lock is obtained on the mutex + * before the map is altered. The map entry, in turn, will alter + * the underlying map object. The operation is undefined if the + * <code>remove()</code> method of the iterator has been called + * beforehand. + * + * @param value the new value to store + * @return the old value + * @throws UnsupportedOperationException if the operation is not supported. + * @throws ClassCastException if the value is of the wrong type. + * @throws IllegalArgumentException if something about the value + * prevents it from existing in this map. + * @throws NullPointerException if the map forbids null values. + */ + public Object setValue(Object value) + { + synchronized (mutex) + { + return e.setValue(value); + } + } + + /** + * Returns a textual representation of the underlying map entry. + * A lock is obtained on the mutex before the entry is accessed. + * + * @return The contents of the map entry in <code>String</code> form. + */ + public String toString() + { + synchronized (mutex) + { + return e.toString(); + } + } + } // class SynchronizedMapEntry + + // Now the actual code. + if (entries == null) + synchronized (mutex) + { + entries = new SynchronizedSet(mutex, m.entrySet()) + { + /** + * Returns an iterator over the set. The iterator has no specific order, + * unless further specified. A lock is obtained on the set's mutex + * before the iterator is created. The created iterator is also + * thread-safe. + * + * @return A synchronized set iterator. + */ + public Iterator iterator() + { + synchronized (super.mutex) + { + return new SynchronizedIterator(super.mutex, c.iterator()) + { + /** + * Retrieves the next map entry from the iterator. + * A lock is obtained on the iterator's mutex before + * the entry is created. The new map entry is enclosed in + * a thread-safe wrapper. + * + * @return A synchronized map entry. + */ + public Object next() + { + synchronized (super.mutex) + { + return new SynchronizedMapEntry(super.next()); + } + } + }; + } + } + }; + } + return entries; + } + + /** + * Returns <code>true</code> if the object, o, is also an instance + * of <code>Map</code> and contains an equivalent + * entry set to that of the underlying map. A lock + * is obtained on the mutex before the objects are + * compared. + * + * @param o The object to compare. + * @return <code>true</code> if o and the underlying map are equivalent. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return m.equals(o); + } + } + + /** + * Returns the value associated with the given key, or null + * if no such mapping exists. An ambiguity exists with maps + * that accept null values as a return value of null could + * be due to a non-existent mapping or simply a null value + * for that key. To resolve this, <code>containsKey</code> + * should be used. A lock is obtained on the mutex before + * the value is retrieved from the underlying map. + * + * @param key The key of the required mapping. + * @return The value associated with the given key, or + * null if no such mapping exists. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + */ + public Object get(Object key) + { + synchronized (mutex) + { + return m.get(key); + } + } + + /** + * Calculates the hash code of the underlying map as the + * sum of the hash codes of all entries. A lock is obtained + * on the mutex before the hash code is computed. + * + * @return The hash code of the underlying map. + */ + public int hashCode() + { + synchronized (mutex) + { + return m.hashCode(); + } + } + + /** + * Returns <code>true</code> if the underlying map contains no entries. + * A lock is obtained on the mutex before the map is examined. + * + * @return <code>true</code> if the map is empty. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return m.isEmpty(); + } + } + + /** + * Returns a thread-safe set view of the keys in the underlying map. The + * set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. If the set supports removal, these methods remove the + * underlying mapping from the map: <code>Iterator.remove</code>, + * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>, + * and <code>clear</code>. Element addition, via <code>add</code> or + * <code>addAll</code>, is not supported via this set. A lock is obtained + * on the mutex before the set is created. + * + * @return A synchronized set containing the keys of the underlying map. + */ + public Set keySet() + { + if (keys == null) + synchronized (mutex) + { + keys = new SynchronizedSet(mutex, m.keySet()); + } + return keys; + } + + /** + * Associates the given key to the given value (optional operation). If the + * underlying map already contains the key, its value is replaced. Be aware + * that in a map that permits <code>null</code> values, a null return does not + * always imply that the mapping was created. A lock is obtained on the mutex + * before the modification is made. + * + * @param key the key to map. + * @param value the value to be mapped. + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if either the key or the value is null, + * and the map forbids null keys or values + * @see #containsKey(Object) + */ + public Object put(Object key, Object value) + { + synchronized (mutex) + { + return m.put(key, value); + } + } + + /** + * Copies all entries of the given map to the underlying one (optional + * operation). If the map already contains a key, its value is replaced. + * A lock is obtained on the mutex before the operation proceeds. + * + * @param map the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if a key or value is of the wrong type + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values, or + * if <code>m</code> is null. + * @see #put(Object, Object) + */ + public void putAll(Map map) + { + synchronized (mutex) + { + m.putAll(map); + } + } + + /** + * Removes the mapping for the key, o, if present (optional operation). If + * the key is not present, this returns null. Note that maps which permit + * null values may also return null if the key was removed. A prior + * <code>containsKey()</code> check is required to avoid this ambiguity. + * Before the mapping is removed, a lock is obtained on the mutex. + * + * @param o the key to remove + * @return the value the key mapped to, or null if not present + * @throws UnsupportedOperationException if deletion is unsupported + * @throws NullPointerException if the key is null and this map doesn't + * support null keys. + * @throws ClassCastException if the type of the key is not a valid type + * for this map. + */ + public Object remove(Object o) + { + synchronized (mutex) + { + return m.remove(o); + } + } + + /** + * Retrieves the size of the underlying map. A lock + * is obtained on the mutex before access takes place. + * Maps with a size greater than <code>Integer.MAX_VALUE</code> + * return <code>Integer.MAX_VALUE</code> instead. + * + * @return The size of the underlying map. + */ + public int size() + { + synchronized (mutex) + { + return m.size(); + } + } + + /** + * Returns a textual representation of the underlying + * map. A lock is obtained on the mutex before the map + * is accessed. + * + * @return The map in <code>String</code> form. + */ + public String toString() + { + synchronized (mutex) + { + return m.toString(); + } + } + + /** + * Returns a synchronized collection view of the values in the underlying + * map. The collection is backed by the map, so that changes in one show up in + * the other. Modifications made while an iterator is in progress cause + * undefined behavior. If the collection supports removal, these methods + * remove the underlying mapping from the map: <code>Iterator.remove</code>, + * <code>Collection.remove</code>, <code>removeAll</code>, + * <code>retainAll</code>, and <code>clear</code>. Element addition, via + * <code>add</code> or <code>addAll</code>, is not supported via this + * collection. A lock is obtained on the mutex before the collection + * is created. + * + * @return the collection of all values in the underlying map. + */ + public Collection values() + { + if (values == null) + synchronized (mutex) + { + values = new SynchronizedCollection(mutex, m.values()); + } + return values; + } + } // class SynchronizedMap + + /** + * Returns a synchronized (thread-safe) set wrapper backed by the given + * set. Notice that element access through the iterator is thread-safe, but + * if the set can be structurally modified (adding or removing elements) + * then you should synchronize around the iteration to avoid + * non-deterministic behavior:<br> + * <pre> + * Set s = Collections.synchronizedSet(new Set(...)); + * ... + * synchronized (s) + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a synchronized view of the set + * @see Serializable + */ + public static Set synchronizedSet(Set s) + { + return new SynchronizedSet(s); + } + + /** + * The implementation of {@link #synchronizedSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * Package visible, so that sets such as Hashtable.keySet() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedSet extends SynchronizedCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 487447009682186044L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + SynchronizedSet(Set s) + { + super(s); + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param s the set + */ + SynchronizedSet(Object sync, Set s) + { + super(sync, s); + } + + /** + * Returns <code>true</code> if the object, o, is a <code>Set</code> + * of the same size as the underlying set, and contains + * each element, e, which occurs in the underlying set. + * A lock is obtained on the mutex before the comparison + * takes place. + * + * @param o The object to compare against. + * @return <code>true</code> if o is an equivalent set. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return c.equals(o); + } + } + + /** + * Computes the hash code for the underlying set as the + * sum of the hash code of all elements within the set. + * A lock is obtained on the mutex before the computation + * occurs. + * + * @return The hash code for the underlying set. + */ + public int hashCode() + { + synchronized (mutex) + { + return c.hashCode(); + } + } + } // class SynchronizedSet + + /** + * Returns a synchronized (thread-safe) sorted map wrapper backed by the + * given map. Notice that element access through the collection views, + * subviews, and their iterators are thread-safe, but if the map can be + * structurally modified (adding or removing elements) then you should + * synchronize around the iteration to avoid non-deterministic behavior:<br> + * <pre> + * SortedMap m = Collections.synchronizedSortedMap(new SortedMap(...)); + * ... + * Set s = m.keySet(); // safe outside a synchronized block + * SortedMap m2 = m.headMap(foo); // safe outside a synchronized block + * Set s2 = m2.keySet(); // safe outside a synchronized block + * synchronized (m) // synch on m, not m2, s or s2 + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * i = s2.iterator(); + * while (i.hasNext()) + * bar(i.next()); + * } + * </pre><p> + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the sorted map to wrap + * @return a synchronized view of the sorted map + * @see Serializable + */ + public static SortedMap synchronizedSortedMap(SortedMap m) + { + return new SynchronizedSortedMap(m); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedMap extends SynchronizedMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8798146769416483793L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + SynchronizedSortedMap(SortedMap sm) + { + super(sm); + this.sm = sm; + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param sm the map + */ + SynchronizedSortedMap(Object sync, SortedMap sm) + { + super(sync, sm); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, or null if + * it is the keys' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return sm.comparator(); + } + } + + /** + * Returns the first, lowest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public Object firstKey() + { + synchronized (mutex) + { + return sm.firstKey(); + } + } + + /** + * Returns a submap containing the keys from the first + * key (as returned by <code>firstKey()</code>) to + * the key before that specified. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * This operation is equivalent to <code>subMap(firstKey(), toKey)</code>. + * The submap retains the thread-safe status of this map. + * + * @param toKey the exclusive upper range of the submap. + * @return a submap from <code>firstKey()</code> to the + * the key preceding toKey. + * @throws ClassCastException if toKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if toKey is outside the map's range. + * @throws NullPointerException if toKey is null. but the map does not allow + * null keys. + */ + public SortedMap headMap(Object toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.headMap(toKey)); + } + } + + /** + * Returns the last, highest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public Object lastKey() + { + synchronized (mutex) + { + return sm.lastKey(); + } + } + + /** + * Returns a submap containing the keys from fromKey to + * the key before toKey. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * The submap retains the thread-safe status of this map. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return a submap from fromKey to the key preceding toKey. + * @throws ClassCastException if fromKey or toKey is not comparable + * to the underlying map's contents. + * @throws IllegalArgumentException if fromKey or toKey is outside the map's + * range. + * @throws NullPointerException if fromKey or toKey is null. but the map does + * not allow null keys. + */ + public SortedMap subMap(Object fromKey, Object toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.subMap(fromKey, toKey)); + } + } + + /** + * Returns a submap containing all the keys from fromKey onwards. + * The submap supports all operations supported by the underlying + * map and all actions taking place on the submap are also reflected + * in the underlying map. A lock is obtained on the mutex prior to + * submap creation. The submap retains the thread-safe status of + * this map. + * + * @param fromKey the inclusive lower range of the submap. + * @return a submap from fromKey to <code>lastKey()</code>. + * @throws ClassCastException if fromKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if fromKey is outside the map's range. + * @throws NullPointerException if fromKey is null. but the map does not allow + * null keys. + */ + public SortedMap tailMap(Object fromKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.tailMap(fromKey)); + } + } + } // class SynchronizedSortedMap + + /** + * Returns a synchronized (thread-safe) sorted set wrapper backed by the + * given set. Notice that element access through the iterator and through + * subviews are thread-safe, but if the set can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * SortedSet s = Collections.synchronizedSortedSet(new SortedSet(...)); + * ... + * SortedSet s2 = s.headSet(foo); // safe outside a synchronized block + * synchronized (s) // synch on s, not s2 + * { + * Iterator i = s2.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the sorted set to wrap + * @return a synchronized view of the sorted set + * @see Serializable + */ + public static SortedSet synchronizedSortedSet(SortedSet s) + { + return new SynchronizedSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedSet(SortedSet)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedSet extends SynchronizedSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8695801310862127406L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private final SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + SynchronizedSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param l the list + */ + SynchronizedSortedSet(Object sync, SortedSet ss) + { + super(sync, ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, or null if + * it is the elements' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return ss.comparator(); + } + } + + /** + * Returns the first, lowest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the first element. + * @throws NoSuchElementException if this set is empty. + */ + public Object first() + { + synchronized (mutex) + { + return ss.first(); + } + } + + /** + * Returns a subset containing the element from the first + * element (as returned by <code>first()</code>) to + * the element before that specified. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * This operation is equivalent to <code>subSet(first(), toElement)</code>. + * The subset retains the thread-safe status of this set. + * + * @param toElement the exclusive upper range of the subset. + * @return a subset from <code>first()</code> to the + * the element preceding toElement. + * @throws ClassCastException if toElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if toElement is outside the set's range. + * @throws NullPointerException if toElement is null. but the set does not allow + * null elements. + */ + public SortedSet headSet(Object toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.headSet(toElement)); + } + } + + /** + * Returns the last, highest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the last element. + * @throws NoSuchElementException if this set is empty. + */ + public Object last() + { + synchronized (mutex) + { + return ss.last(); + } + } + + /** + * Returns a subset containing the elements from fromElement to + * the element before toElement. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * The subset retains the thread-safe status of this set. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return a subset from fromElement to the element preceding toElement. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the underlying set's contents. + * @throws IllegalArgumentException if fromElement or toElement is outside the set's + * range. + * @throws NullPointerException if fromElement or toElement is null. but the set does + * not allow null elements. + */ + public SortedSet subSet(Object fromElement, Object toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, + ss.subSet(fromElement, toElement)); + } + } + + /** + * Returns a subset containing all the elements from fromElement onwards. + * The subset supports all operations supported by the underlying + * set and all actions taking place on the subset are also reflected + * in the underlying set. A lock is obtained on the mutex prior to + * subset creation. The subset retains the thread-safe status of + * this set. + * + * @param fromElement the inclusive lower range of the subset. + * @return a subset from fromElement to <code>last()</code>. + * @throws ClassCastException if fromElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if fromElement is outside the set's range. + * @throws NullPointerException if fromElement is null. but the set does not allow + * null elements. + */ + public SortedSet tailSet(Object fromElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.tailSet(fromElement)); + } + } + } // class SynchronizedSortedSet + + + /** + * Returns an unmodifiable view of the given collection. This allows + * "read-only" access, although changes in the backing collection show up + * in this view. Attempts to modify the collection directly or via iterators + * will fail with {@link UnsupportedOperationException}. Although this view + * prevents changes to the structure of the collection and its elements, the values + * referenced by the objects in the collection can still be modified. + * <p> + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a read-only view of the collection + * @see Serializable + */ + public static Collection unmodifiableCollection(Collection c) + { + return new UnmodifiableCollection(c); + } + + /** + * The implementation of {@link #unmodifiableCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1820017752578914078L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + UnmodifiableCollection(Collection c) + { + this.c = c; + if (c == null) + throw new NullPointerException(); + } + + /** + * Blocks the addition of elements to the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o the object to add. + * @return <code>true</code> if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the add operation. + */ + public boolean add(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the underlying + * collection. This method never returns, throwing an exception instead. + * + * @param c the collection to add. + * @return <code>true</code> if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the <code>addAll</code> operation. + */ + public boolean addAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the clearing of the underlying collection. This method never + * returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable collection does + * not support the <code>clear()</code> operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Test whether the underlying collection contains a given object as one of its + * elements. + * + * @param o the element to look for. + * @return <code>true</code> if the underlying collection contains at least + * one element e such that + * <code>o == null ? e == null : o.equals(e)</code>. + * @throws ClassCastException if the type of o is not a valid type for the + * underlying collection. + * @throws NullPointerException if o is null and the underlying collection + * doesn't support null values. + */ + public boolean contains(Object o) + { + return c.contains(o); + } + + /** + * Test whether the underlying collection contains every element in a given + * collection. + * + * @param c1 the collection to test for. + * @return <code>true</code> if for every element o in c, contains(o) would + * return <code>true</code>. + * @throws ClassCastException if the type of any element in c is not a valid + * type for the underlying collection. + * @throws NullPointerException if some element of c is null and the underlying + * collection does not support null values. + * @throws NullPointerException if c itself is null. + */ + public boolean containsAll(Collection c1) + { + return c.containsAll(c1); + } + + /** + * Tests whether the underlying collection is empty, that is, + * if size() == 0. + * + * @return <code>true</code> if this collection contains no elements. + */ + public boolean isEmpty() + { + return c.isEmpty(); + } + + /** + * Obtain an Iterator over the underlying collection, which maintains + * its unmodifiable nature. + * + * @return an UnmodifiableIterator over the elements of the underlying + * collection, in any order. + */ + public Iterator iterator() + { + return new UnmodifiableIterator(c.iterator()); + } + + /** + * Blocks the removal of an object from the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o The object to remove. + * @return <code>true</code> if the object was removed (i.e. the underlying + * collection returned 1 or more instances of o). + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>remove()</code> operation. + */ + public boolean remove(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of a collection of objects from the underlying + * collection. This method never returns, throwing an exception + * instead. + * + * @param c The collection of objects to remove. + * @return <code>true</code> if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>removeAll()</code> operation. + */ + public boolean removeAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of all elements from the underlying collection, + * except those in the supplied collection. This method never returns, + * throwing an exception instead. + * + * @param c The collection of objects to retain. + * @return <code>true</code> if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>retainAll()</code> operation. + */ + public boolean retainAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Retrieves the number of elements in the underlying collection. + * + * @return the number of elements in the collection. + */ + public int size() + { + return c.size(); + } + + /** + * Copy the current contents of the underlying collection into an array. + * + * @return an array of type Object[] with a length equal to the size of the + * underlying collection and containing the elements currently in + * the underlying collection, in any order. + */ + public Object[] toArray() + { + return c.toArray(); + } + + /** + * Copy the current contents of the underlying collection into an array. If + * the array passed as an argument has length less than the size of the + * underlying collection, an array of the same run-time type as a, with a length + * equal to the size of the underlying collection, is allocated using reflection. + * Otherwise, a itself is used. The elements of the underlying collection are + * copied into it, and if there is space in the array, the following element is + * set to null. The resultant array is returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this collection does not contain any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in the underlying + * collection, in any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public Object[] toArray(Object[] a) + { + return c.toArray(a); + } + + /** + * A textual representation of the unmodifiable collection. + * + * @return The unmodifiable collection in the form of a <code>String</code>. + */ + public String toString() + { + return c.toString(); + } + } // class UnmodifiableCollection + + /** + * The implementation of the various iterator methods in the + * unmodifiable classes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableIterator implements Iterator + { + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper. + * @param i the wrapped iterator + */ + UnmodifiableIterator(Iterator i) + { + this.i = i; + } + + /** + * Obtains the next element in the underlying collection. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public Object next() + { + return i.next(); + } + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by <code>next()</code>. When this method + * returns <code>true</code>, an exception will not be thrown on calling + * <code>next()</code>. + * + * @return <code>true</code> if there is at least one more element in the underlying + * collection. + */ + public boolean hasNext() + { + return i.hasNext(); + } + + /** + * Blocks the removal of elements from the underlying collection by the + * iterator. + * + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the removal of elements by its iterator. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableIterator + + /** + * Returns an unmodifiable view of the given list. This allows + * "read-only" access, although changes in the backing list show up + * in this view. Attempts to modify the list directly, via iterators, or + * via sublists, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the list and + * its elements, the values referenced by the objects in the list can + * still be modified. + * <p> + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a read-only view of the list + * @see Serializable + * @see RandomAccess + */ + public static List unmodifiableList(List l) + { + if (l instanceof RandomAccess) + return new UnmodifiableRandomAccessList(l); + return new UnmodifiableList(l); + } + + /** + * The implementation of {@link #unmodifiableList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableList extends UnmodifiableCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -283967356065247728L; + + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableList(List l) + { + super(l); + list = l; + } + + /** + * Blocks the addition of an element to the underlying + * list at a specific index. This method never returns, + * throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param o the object to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the <code>add()</code> operation. + */ + public void add(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the + * underlying list at a specific index. This method never + * returns, throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param c the collections of objects to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the <code>addAll()</code> operation. + */ + public boolean addAll(int index, Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns <code>true</code> if the object, o, is an instance of + * <code>List</code> with the same size and elements + * as the underlying list. + * + * @param o The object to compare. + * @return <code>true</code> if o is equivalent to the underlying list. + */ + public boolean equals(Object o) + { + return list.equals(o); + } + + /** + * Retrieves the element at a given index in the underlying list. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + return list.get(index); + } + + /** + * Computes the hash code for the underlying list. + * The exact computation is described in the documentation + * of the <code>List</code> interface. + * + * @return The hash code of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + return list.hashCode(); + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int indexOf(Object o) + { + return list.indexOf(o); + } + + /** + * Obtain the last index at which a given object is to be found in the + * underlying list. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + /** + * Obtains a list iterator over the underlying list, starting at the beginning + * and maintaining the unmodifiable nature of this list. + * + * @return a <code>UnmodifiableListIterator</code> over the elements of the + * underlying list, in order, starting at the beginning. + */ + public ListIterator listIterator() + { + return new UnmodifiableListIterator(list.listIterator()); + } + + /** + * Obtains a list iterator over the underlying list, starting at the specified + * index and maintaining the unmodifiable nature of this list. An initial call + * to <code>next()</code> will retrieve the element at the specified index, + * and an initial call to <code>previous()</code> will retrieve the element + * at index - 1. + * + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from. + * @return a <code>UnmodifiableListIterator</code> over the elements of the + * underlying list, in order, starting at the specified index. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + return new UnmodifiableListIterator(list.listIterator(index)); + } + + /** + * Blocks the removal of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to remove. + * @return the removed element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the <code>remove()</code> + * operation. + */ + public Object remove(int index) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the replacement of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to replace. + * @param o The new object to place at the specified index. + * @return the replaced element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the <code>set()</code> + * operation. + */ + public Object set(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Obtain a List view of a subsection of the underlying list, from + * fromIndex (inclusive) to toIndex (exclusive). If the two indices + * are equal, the sublist is empty. The returned list will be + * unmodifiable, like this list. Changes to the elements of the + * returned list will be reflected in the underlying list. No structural + * modifications can take place in either list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive). + * @param toIndex the index that the returned list should go to (exclusive). + * @return a List backed by a subsection of the underlying list. + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex. + */ + public List subList(int fromIndex, int toIndex) + { + return unmodifiableList(list.subList(fromIndex, toIndex)); + } + } // class UnmodifiableList + + /** + * The implementation of {@link #unmodifiableList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableRandomAccessList + extends UnmodifiableList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2542308836966382001L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableRandomAccessList(List l) + { + super(l); + } + } // class UnmodifiableRandomAccessList + + /** + * The implementation of {@link UnmodifiableList#listIterator()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableListIterator + extends UnmodifiableIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper. + * @param li the wrapped iterator + */ + UnmodifiableListIterator(ListIterator li) + { + super(li); + this.li = li; + } + + /** + * Blocks the addition of an object to the list underlying this iterator. + * This method never returns, throwing an exception instead. + * + * @param o The object to add. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the <code>add()</code> operation. + */ + public void add(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by <code>previous()</code>. When this method + * returns <code>true</code>, an exception will not be thrown on calling + * <code>previous()</code>. + * + * @return <code>true</code> if there is at least one more element prior to the + * current position in the underlying list. + */ + public boolean hasPrevious() + { + return li.hasPrevious(); + } + + /** + * Find the index of the element that would be returned by a call to next. + * If <code>hasNext()</code> returns <code>false</code>, this returns the list size. + * + * @return the index of the element that would be returned by + * <code>next()</code>. + */ + public int nextIndex() + { + return li.nextIndex(); + } + + /** + * Obtains the previous element in the underlying list. + * + * @return the previous element in the list. + * @throws NoSuchElementException if there are no more prior elements. + */ + public Object previous() + { + return li.previous(); + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If <code>hasPrevious()</code> returns <code>false</code>, + * this returns -1. + * + * @return the index of the element that would be returned by + * <code>previous()</code>. + */ + public int previousIndex() + { + return li.previousIndex(); + } + + /** + * Blocks the replacement of an element in the list underlying this + * iterator. This method never returns, throwing an exception instead. + * + * @param o The new object to replace the existing one. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the <code>set()</code> operation. + */ + public void set(Object o) + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableListIterator + + /** + * Returns an unmodifiable view of the given map. This allows "read-only" + * access, although changes in the backing map show up in this view. + * Attempts to modify the map directly, or via collection views or their + * iterators will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + * <p> + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static Map unmodifiableMap(Map m) + { + return new UnmodifiableMap(m); + } + + /** + * The implementation of {@link #unmodifiableMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -1034234728574286014L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + UnmodifiableMap(Map m) + { + this.m = m; + if (m == null) + throw new NullPointerException(); + } + + /** + * Blocks the clearing of entries from the underlying map. + * This method never returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>clear()</code> operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns <code>true</code> if the underlying map contains a mapping for + * the given key. + * + * @param key the key to search for + * @return <code>true</code> if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys + */ + public boolean containsKey(Object key) + { + return m.containsKey(key); + } + + /** + * Returns <code>true</code> if the underlying map contains at least one mapping with + * the given value. In other words, it returns <code>true</code> if a value v exists where + * <code>(value == null ? v == null : value.equals(v))</code>. This usually + * requires linear time. + * + * @param value the value to search for + * @return <code>true</code> if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + return m.containsValue(value); + } + + /** + * Returns a unmodifiable set view of the entries in the underlying map. + * Each element in the set is a unmodifiable variant of <code>Map.Entry</code>. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the objects. + * + * @return the unmodifiable set view of all mapping entries. + * @see Map.Entry + */ + public Set entrySet() + { + if (entries == null) + entries = new UnmodifiableEntrySet(m.entrySet()); + return entries; + } + + /** + * The implementation of {@link UnmodifiableMap#entrySet()}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableEntrySet extends UnmodifiableSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7854390611657943733L; + + /** + * Wrap a given set. + * @param s the set to wrap + */ + UnmodifiableEntrySet(Set s) + { + super(s); + } + + // The iterator must return unmodifiable map entries. + public Iterator iterator() + { + return new UnmodifiableIterator(c.iterator()) + { + /** + * Obtains the next element from the underlying set of + * map entries. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public Object next() + { + final Map.Entry e = (Map.Entry) super.next(); + return new Map.Entry() + { + /** + * Returns <code>true</code> if the object, o, is also a map entry with an + * identical key and value. + * + * @param o the object to compare. + * @return <code>true</code> if o is an equivalent map entry. + */ + public boolean equals(Object o) + { + return e.equals(o); + } + + /** + * Returns the key of this map entry. + * + * @return the key. + */ + public Object getKey() + { + return e.getKey(); + } + + /** + * Returns the value of this map entry. + * + * @return the value. + */ + public Object getValue() + { + return e.getValue(); + } + + /** + * Computes the hash code of this map entry. + * The computation is described in the <code>Map</code> + * interface documentation. + * + * @return the hash code of this entry. + * @see Map#hashCode() + */ + public int hashCode() + { + return e.hashCode(); + } + + /** + * Blocks the alteration of the value of this map entry. + * This method never returns, throwing an exception instead. + * + * @param value The new value. + * @throws UnsupportedOperationException as an unmodifiable + * map entry does not support the <code>setValue()</code> + * operation. + */ + public Object setValue(Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a textual representation of the map entry. + * + * @return The map entry as a <code>String</code>. + */ + public String toString() + { + return e.toString(); + } + }; + } + }; + } + } // class UnmodifiableEntrySet + + /** + * Returns <code>true</code> if the object, o, is also an instance + * of <code>Map</code> with an equal set of map entries. + * + * @param o The object to compare. + * @return <code>true</code> if o is an equivalent map. + */ + public boolean equals(Object o) + { + return m.equals(o); + } + + /** + * Returns the value associated with the supplied key or + * null if no such mapping exists. An ambiguity can occur + * if null values are accepted by the underlying map. + * In this case, <code>containsKey()</code> can be used + * to separate the two possible cases of a null result. + * + * @param key The key to look up. + * @return the value associated with the key, or null if key not in map. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + * @see #containsKey(Object) + */ + public Object get(Object key) + { + return m.get(key); + } + + /** + * Blocks the addition of a new entry to the underlying map. + * This method never returns, throwing an exception instead. + * + * @param key The new key. + * @param value The new value. + * @return the previous value of the key, or null if there was no mapping. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>put()</code> operation. + */ + public Object put(Object key, Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Computes the hash code for the underlying map, as the sum + * of the hash codes of all entries. + * + * @return The hash code of the underlying map. + * @see Map.Entry#hashCode() + */ + public int hashCode() + { + return m.hashCode(); + } + + /** + * Returns <code>true</code> if the underlying map contains no entries. + * + * @return <code>true</code> if the map is empty. + */ + public boolean isEmpty() + { + return m.isEmpty(); + } + + /** + * Returns a unmodifiable set view of the keys in the underlying map. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the set view of all keys. + */ + public Set keySet() + { + if (keys == null) + keys = new UnmodifiableSet(m.keySet()); + return keys; + } + + /** + * Blocks the addition of the entries in the supplied map. + * This method never returns, throwing an exception instead. + * + * @param m The map, the entries of which should be added + * to the underlying map. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>putAll</code> operation. + */ + public void putAll(Map m) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of an entry from the map. + * This method never returns, throwing an exception instead. + * + * @param o The key of the entry to remove. + * @return The value the key was associated with, or null + * if no such mapping existed. Null is also returned + * if the removed entry had a null key. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>remove</code> operation. + */ + public Object remove(Object o) + { + throw new UnsupportedOperationException(); + } + + + /** + * Returns the number of key-value mappings in the underlying map. + * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE + * is returned. + * + * @return the number of mappings. + */ + public int size() + { + return m.size(); + } + + /** + * Returns a textual representation of the map. + * + * @return The map in the form of a <code>String</code>. + */ + public String toString() + { + return m.toString(); + } + + /** + * Returns a unmodifiable collection view of the values in the underlying map. + * The collection is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the collection view of all values. + */ + public Collection values() + { + if (values == null) + values = new UnmodifiableCollection(m.values()); + return values; + } + } // class UnmodifiableMap + + /** + * Returns an unmodifiable view of the given set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly or via iterators + * will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + * <p> + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static Set unmodifiableSet(Set s) + { + return new UnmodifiableSet(s); + } + + /** + * The implementation of {@link #unmodifiableSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSet extends UnmodifiableCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -9215047833775013803L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + UnmodifiableSet(Set s) + { + super(s); + } + + /** + * Returns <code>true</code> if the object, o, is also an instance of + * <code>Set</code> of the same size and with the same entries. + * + * @return <code>true</code> if o is an equivalent set. + */ + public boolean equals(Object o) + { + return c.equals(o); + } + + /** + * Computes the hash code of this set, as the sum of the + * hash codes of all elements within the set. + * + * @return the hash code of the set. + */ + public int hashCode() + { + return c.hashCode(); + } + } // class UnmodifiableSet + + /** + * Returns an unmodifiable view of the given sorted map. This allows + * "read-only" access, although changes in the backing map show up in this + * view. Attempts to modify the map directly, via subviews, via collection + * views, or iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + * <p> + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static SortedMap unmodifiableSortedMap(SortedMap m) + { + return new UnmodifiableSortedMap(m); + } + + /** + * The implementation of {@link #unmodifiableSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedMap extends UnmodifiableMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8806743815996713206L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + UnmodifiableSortedMap(SortedMap sm) + { + super(sm); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, + * or null if it is the keys' natural ordering. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + return sm.comparator(); + } + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public Object firstKey() + { + return sm.firstKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map strictly less + * than toKey. The view is backed by the underlying map, so changes in + * one show up in the other. The submap supports all optional operations + * of the original. This operation is equivalent to + * <code>subMap(firstKey(), toKey)</code>. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its successor + * object in to toKey. For example, for Integers, you could request + * <code>headMap(new Integer(limit.intValue() + 1))</code>. + * + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if toKey is not comparable to the map contents. + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range. + * @throws NullPointerException if toKey is null but the map does not allow + * null keys. + */ + public SortedMap headMap(Object toKey) + { + return new UnmodifiableSortedMap(sm.headMap(toKey)); + } + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public Object lastKey() + { + return sm.lastKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey, and strictly less than toKey. The view is backed by + * the underlying map, so changes in one show up in the other. The submap + * supports all optional operations of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * <code>subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents. + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range. + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys. + */ + public SortedMap subMap(Object fromKey, Object toKey) + { + return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey)); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey. The view is backed by the underlying map, so changes + * in one show up in the other. The submap supports all optional operations + * of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, fromKey, is + * included; if you do not want this value to be included, pass its successor object in + * to fromKey. For example, for Integers, you could request + * <code>tailMap(new Integer(limit.intValue() + 1))</code>. + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not allow + * null keys + */ + public SortedMap tailMap(Object fromKey) + { + return new UnmodifiableSortedMap(sm.tailMap(fromKey)); + } + } // class UnmodifiableSortedMap + + /** + * Returns an unmodifiable view of the given sorted set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly, via subsets, or via + * iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + * <p> + * + * The returns SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static SortedSet unmodifiableSortedSet(SortedSet s) + { + return new UnmodifiableSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedSet extends UnmodifiableSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -4929149591599911165L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + UnmodifiableSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, + * or null if it is the elements' natural ordering. + * + * @return the sorting comparator + */ + public Comparator comparator() + { + return ss.comparator(); + } + + /** + * Returns the first (lowest sorted) element in the underlying + * set. + * + * @return the first element. + * @throws NoSuchElementException if the set is empty. + */ + public Object first() + { + return ss.first(); + } + + /** + * Returns a unmodifiable view of the portion of the set strictly + * less than toElement. The view is backed by the underlying set, + * so changes in one show up in the other. The subset supports + * all optional operations of the original. This operation + * is equivalent to <code>subSet(first(), toElement)</code>. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of toElement. Note that the endpoint, toElement, + * is not included; if you want this value included, pass its successor object in to + * toElement. For example, for Integers, you could request + * <code>headSet(new Integer(limit.intValue() + 1))</code>. + * + * @param toElement the exclusive upper range of the subset + * @return the subset. + * @throws ClassCastException if toElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and toElement is out + * of range. + * @throws NullPointerException if toElement is null but the set does not + * allow null elements. + */ + public SortedSet headSet(Object toElement) + { + return new UnmodifiableSortedSet(ss.headSet(toElement)); + } + + /** + * Returns the last (highest sorted) element in the underlying + * set. + * + * @return the last element. + * @throws NoSuchElementException if the set is empty. + */ + public Object last() + { + return ss.last(); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or + * equal to fromElement, and strictly less than toElement. The view is backed by + * the underlying set, so changes in one show up in the other. The subset + * supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement and toElement. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you can request + * <code>subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return the subset. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range. + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements. + */ + public SortedSet subSet(Object fromElement, Object toElement) + { + return new UnmodifiableSortedSet(ss.subSet(fromElement, toElement)); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or equal to + * fromElement. The view is backed by the underlying set, so changes in one show up + * in the other. The subset supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement. Note that the endpoint, + * fromElement, is included; if you do not want this value to be included, pass its + * successor object in to fromElement. For example, for Integers, you could request + * <code>tailSet(new Integer(limit.intValue() + 1))</code>. + * + * @param fromElement the inclusive lower range of the subset + * @return the subset. + * @throws ClassCastException if fromElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range. + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements. + */ + public SortedSet tailSet(Object fromElement) + { + return new UnmodifiableSortedSet(ss.tailSet(fromElement)); + } + } // class UnmodifiableSortedSet +} // class Collections diff --git a/libjava/classpath/java/util/Comparator.java b/libjava/classpath/java/util/Comparator.java new file mode 100644 index 00000000000..386bdc1d6a2 --- /dev/null +++ b/libjava/classpath/java/util/Comparator.java @@ -0,0 +1,119 @@ +/* Comparator.java -- Interface for objects that specify an ordering + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface for objects that specify an ordering between objects. The ordering + * should be <em>total</em>, such that any two objects of the correct type + * can be compared, and the comparison is reflexive, anti-symmetric, and + * transitive. It is also recommended that the comparator be <em>consistent + * with equals</em>, although this is not a strict requirement. A relation + * is consistent with equals if these two statements always have the same + * results (if no exceptions occur):<br> + * <code>compare((Object) e1, (Object) e2) == 0</code> and + * <code>e1.equals((Object) e2)</code><br> + * Comparators that violate consistency with equals may cause strange behavior + * in sorted lists and sets. For example, a case-sensitive dictionary order + * comparison of Strings is consistent with equals, but if it is + * case-insensitive it is not, because "abc" and "ABC" compare as equal even + * though "abc".equals("ABC") returns false. + * <P> + * In general, Comparators should be Serializable, because when they are passed + * to Serializable data structures such as SortedMap or SortedSet, the entire + * data structure will only serialize correctly if the comparator is + * Serializable. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Comparable + * @see TreeMap + * @see TreeSet + * @see SortedMap + * @see SortedSet + * @see Arrays#sort(Object[], Comparator) + * @see java.io.Serializable + * @since 1.2 + * @status updated to 1.4 + */ +public interface Comparator +{ + /** + * Return an integer that is negative, zero or positive depending on whether + * the first argument is less than, equal to or greater than the second + * according to this ordering. This method should obey the following + * contract: + * <ul> + * <li>if compare(a, b) < 0 then compare(b, a) > 0</li> + * <li>if compare(a, b) throws an exception, so does compare(b, a)</li> + * <li>if compare(a, b) < 0 and compare(b, c) < 0 then compare(a, c) + * < 0</li> + * <li>if compare(a, b) == 0 then compare(a, c) and compare(b, c) must + * have the same sign</li> + * </ul> + * To be consistent with equals, the following additional constraint is + * in place: + * <ul> + * <li>if a.equals(b) or both a and b are null, then + * compare(a, b) == 0.</li> + * </ul><p> + * + * Although it is permissible for a comparator to provide an order + * inconsistent with equals, that should be documented. + * + * @param o1 the first object + * @param o2 the second object + * @return the comparison + * @throws ClassCastException if the elements are not of types that can be + * compared by this ordering. + */ + int compare(Object o1, Object o2); + + /** + * Return true if the object is equal to this object. To be + * considered equal, the argument object must satisfy the constraints + * of <code>Object.equals()</code>, be a Comparator, and impose the + * same ordering as this Comparator. The default implementation + * inherited from Object is usually adequate. + * + * @param obj The object + * @return true if it is a Comparator that imposes the same order + * @see Object#equals(Object) + */ + boolean equals(Object obj); +} diff --git a/libjava/classpath/java/util/ConcurrentModificationException.java b/libjava/classpath/java/util/ConcurrentModificationException.java new file mode 100644 index 00000000000..3d7ae108415 --- /dev/null +++ b/libjava/classpath/java/util/ConcurrentModificationException.java @@ -0,0 +1,92 @@ +/* ConcurrentModificationException.java -- Data structure concurrently modified + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * Exception that is thrown by the collections classes when it is detected that + * a modification has been made to a data structure when this is not allowed, + * such as when a collection is structurally modified while an Iterator is + * operating over it. In cases where this can be detected, a + * ConcurrentModificationException will be thrown. An Iterator that detects + * this condition is referred to as fail-fast. Notice that this can occur + * even in single-threaded designs, if you call methods out of order. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Iterator + * @see ListIterator + * @see Vector + * @see LinkedList + * @see HashSet + * @see Hashtable + * @see TreeMap + * @see AbstractList + * @since 1.2 + * @status updated to 1.4 + */ +public class ConcurrentModificationException extends RuntimeException +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -3666751008965953603L; + + /** + * Constructs a ConcurrentModificationException with no detail message. + */ + public ConcurrentModificationException() + { + } + + /** + * Constructs a ConcurrentModificationException with a detail message. + * + * @param detail the detail message for the exception + */ + public ConcurrentModificationException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/Currency.java b/libjava/classpath/java/util/Currency.java new file mode 100644 index 00000000000..32ea7534735 --- /dev/null +++ b/libjava/classpath/java/util/Currency.java @@ -0,0 +1,437 @@ +/* Currency.java -- Representation of a currency + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.locale.LocaleHelper; + +import java.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** + * Representation of a currency for a particular locale. Each currency + * is identified by its ISO 4217 code, and only one instance of this + * class exists per currency. As a result, instances are created + * via the <code>getInstance()</code> methods rather than by using + * a constructor. + * + * @see java.util.Locale + * @author Guilhem Lavaux (guilhem.lavaux@free.fr) + * @author Dalibor Topic (robilad@kaffe.org) + * @author Bryce McKinlay (mckinlay@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public final class Currency + implements Serializable +{ + /** + * For compatability with Sun's JDK + */ + static final long serialVersionUID = -158308464356906721L; + + /** + * The set of properties which map a currency to + * the currency information such as the ISO 4217 + * currency code and the number of decimal points. + * + * @see #getCurrencyCode() + * @serial ignored. + */ + private static transient Properties properties; + + /** + * The ISO 4217 currency code associated with this + * particular instance. + * + * @see #getCurrencyCode() + * @serial the ISO 4217 currency code + */ + private String currencyCode; + + /** + * The number of fraction digits associated with this + * particular instance. + * + * @see #getDefaultFractionDigits() + * @serial the number of fraction digits + */ + private transient int fractionDigits; + + /** + * A cached map of country codes + * instances to international currency code + * <code>String</code>s. Seperating this + * from the <code>Currency</code> instances + * ensures we have a common lookup between + * the two <code>getInstance()</code> methods. + * + * @see #getInstance(java.util.Locale) + * @serial ignored. + */ + private static transient Map countryMap; + + /** + * A cache of <code>Currency</code> instances to + * ensure the singleton nature of this class. The key + * is the international currency code. + * + * @see #getInstance(java.util.Locale) + * @see #getInstance(java.lang.String) + * @see #readResolve() + * @serial ignored. + */ + private static transient Map cache; + + /** + * Instantiates the cache and reads in the properties. + */ + static + { + /* Create a hash map for the locale mappings */ + countryMap = new HashMap(); + /* Create a hash map for the cache */ + cache = new HashMap(); + /* Create the properties object */ + properties = new Properties(); + /* Try and load the properties from our iso4217.properties resource */ + try + { + properties.load(Currency.class.getResourceAsStream("iso4217.properties")); + } + catch (IOException exception) + { + System.out.println("Failed to load currency resource: " + exception); + } + } + + /** + * Default constructor for deserialization + */ + private Currency() + { + } + + /** + * Constructor to create a <code>Currency</code> object + * for a particular <code>Locale</code>. + * All components of the given locale, other than the + * country code, are ignored. The results of calling this + * method may vary over time, as the currency associated with + * a particular country changes. For countries without + * a given currency (e.g. Antarctica), the result is null. + * + * @param loc the locale for the new currency, or null if + * there is no country code specified or a currency + * for this country. + */ + private Currency(Locale loc) + { + String countryCode; + String currencyKey; + String fractionDigitsKey; + int commaPosition; + + /* Retrieve the country code from the locale */ + countryCode = loc.getCountry(); + /* If there is no country code, return */ + if (countryCode.equals("")) + { + throw new + IllegalArgumentException("Invalid (empty) country code for locale:" + + loc); + } + /* Construct the key for the currency */ + currencyKey = countryCode + ".currency"; + /* Construct the key for the fraction digits */ + fractionDigitsKey = countryCode + ".fractionDigits"; + /* Retrieve the currency */ + currencyCode = properties.getProperty(currencyKey); + /* Return if the currency code is null */ + if (currencyCode == null) + { + return; + } + /* Split off the first currency code (we only use the first for now) */ + commaPosition = currencyCode.indexOf(","); + if (commaPosition != -1) + { + currencyCode = currencyCode.substring(0, commaPosition); + } + /* Retrieve the fraction digits */ + fractionDigits = Integer.parseInt(properties.getProperty(fractionDigitsKey)); + } + + /** + * Constructor for the "XXX" special case. This allows + * a Currency to be constructed from an assumed good + * currency code. + * + * @param code the code to use. + */ + private Currency(String code) + { + currencyCode = code; + fractionDigits = -1; /* Pseudo currency */ + } + + /** + * Returns the ISO4217 currency code of this currency. + * + * @return a <code>String</code> containing currency code. + */ + public String getCurrencyCode() + { + return currencyCode; + } + + /** + * Returns the number of digits which occur after the decimal point + * for this particular currency. For example, currencies such + * as the U.S. dollar, the Euro and the Great British pound have two + * digits following the decimal point to indicate the value which exists + * in the associated lower-valued coinage (cents in the case of the first + * two, pennies in the latter). Some currencies such as the Japanese + * Yen have no digits after the decimal point. In the case of pseudo + * currencies, such as IMF Special Drawing Rights, -1 is returned. + * + * @return the number of digits after the decimal separator for this currency. + */ + public int getDefaultFractionDigits() + { + return fractionDigits; + } + + /** + * Builds a new currency instance for this locale. + * All components of the given locale, other than the + * country code, are ignored. The results of calling this + * method may vary over time, as the currency associated with + * a particular country changes. For countries without + * a given currency (e.g. Antarctica), the result is null. + * + * @param locale a <code>Locale</code> instance. + * @return a new <code>Currency</code> instance. + * @throws NullPointerException if the locale or its + * country code is null. + * @throws IllegalArgumentException if the country of + * the given locale is not a supported ISO3166 code. + */ + public static Currency getInstance(Locale locale) + { + /** + * The new instance must be the only available instance + * for the currency it supports. We ensure this happens, + * while maintaining a suitable performance level, by + * creating the appropriate object on the first call to + * this method, and returning the cached instance on + * later calls. + */ + Currency newCurrency; + + String country = locale.getCountry(); + if (locale == null || country == null) + { + throw new + NullPointerException("The locale or its country is null."); + } + /* Attempt to get the currency from the cache */ + String code = (String) countryMap.get(country); + if (code == null) + { + /* Create the currency for this locale */ + newCurrency = new Currency(locale); + /* + * If the currency code is null, then creation failed + * and we return null. + */ + code = newCurrency.getCurrencyCode(); + if (code == null) + { + return null; + } + else + { + /* Cache it */ + countryMap.put(country, code); + cache.put(code, newCurrency); + } + } + else + { + newCurrency = (Currency) cache.get(code); + } + /* Return the instance */ + return newCurrency; + } + + /** + * Builds the currency corresponding to the specified currency code. + * + * @param currencyCode a string representing a currency code. + * @return a new <code>Currency</code> instance. + * @throws NullPointerException if currencyCode is null. + * @throws IllegalArgumentException if the supplied currency code + * is not a supported ISO 4217 code. + */ + public static Currency getInstance(String currencyCode) + { + Locale[] allLocales; + + /* + * Throw a null pointer exception explicitly if currencyCode is null. + * One is not thrown otherwise. It results in an + * IllegalArgumentException. + */ + if (currencyCode == null) + { + throw new NullPointerException("The supplied currency code is null."); + } + /* Nasty special case to allow an erroneous currency... blame Sun */ + if (currencyCode.equals("XXX")) + return new Currency("XXX"); + Currency newCurrency = (Currency) cache.get(currencyCode); + if (newCurrency == null) + { + /* Get all locales */ + allLocales = Locale.getAvailableLocales(); + /* Loop through each locale, looking for the code */ + for (int i = 0;i < allLocales.length; i++) + { + try + { + Currency testCurrency = getInstance (allLocales[i]); + if (testCurrency != null && + testCurrency.getCurrencyCode().equals(currencyCode)) + { + return testCurrency; + } + } + catch (IllegalArgumentException exception) + { + /* Ignore locales without valid countries */ + } + } + /* + * If we get this far, the code is not supported by any of + * our locales. + */ + throw new IllegalArgumentException("The currency code, " + currencyCode + + ", is not supported."); + } + else + { + return newCurrency; + } + } + + /** + * This method returns the symbol which precedes or follows a + * value in this particular currency in the default locale. + * In cases where there is no such symbol for the currency, + * the ISO 4217 currency code is returned. + * + * @return the currency symbol, or the ISO 4217 currency code if + * one doesn't exist. + */ + public String getSymbol() + { + return getSymbol(Locale.getDefault()); + } + + /** + * <p> + * This method returns the symbol which precedes or follows a + * value in this particular currency. The returned value is + * the symbol used to denote the currency in the specified locale. + * </p> + * <p> + * For example, a supplied locale may specify a different symbol + * for the currency, due to conflicts with its own currency. + * This would be the case with the American currency, the dollar. + * Locales that also use a dollar-based currency (e.g. Canada, Australia) + * need to differentiate the American dollar using 'US$' rather than '$'. + * So, supplying one of these locales to <code>getSymbol()</code> would + * return this value, rather than the standard '$'. + * </p> + * <p> + * In cases where there is no such symbol for a particular currency, + * the ISO 4217 currency code is returned. + * </p> + * + * @param locale the locale to express the symbol in. + * @return the currency symbol, or the ISO 4217 currency code if + * one doesn't exist. + * @throws NullPointerException if the locale is null. + */ + public String getSymbol(Locale locale) + { + return LocaleHelper.getLocalizedString(locale, currencyCode, + "currenciesSymbol", false, true); + } + + /** + * Returns the international ISO4217 currency code of this currency. + * + * @return a <code>String</code> containing the ISO4217 currency code. + */ + public String toString() + { + return getCurrencyCode(); + } + + /** + * Resolves the deserialized object to the singleton instance for its + * particular currency. The currency code of the deserialized instance + * is used to return the correct instance. + * + * @return the singleton instance for the currency specified by the + * currency code of the deserialized object. This replaces + * the deserialized object as the returned object from + * deserialization. + * @throws ObjectStreamException if a problem occurs with deserializing + * the object. + */ + private Object readResolve() + throws ObjectStreamException + { + return getInstance(currencyCode); + } + +} diff --git a/libjava/classpath/java/util/Date.java b/libjava/classpath/java/util/Date.java new file mode 100644 index 00000000000..aecca29b39e --- /dev/null +++ b/libjava/classpath/java/util/Date.java @@ -0,0 +1,1263 @@ +/* java.util.Date + Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +/** + * <p> + * This class represents a specific time in milliseconds since the epoch. + * The epoch is 1970, January 1 00:00:00.0000 UTC. + * </p> + * <p> + * <code>Date</code> is intended to reflect universal time coordinate (UTC), + * but this depends on the underlying host environment. Most operating systems + * don't handle the leap second, which occurs about once every year or + * so. The leap second is added to the last minute of the day on either + * the 30th of June or the 31st of December, creating a minute 61 seconds + * in length. + * </p> + * <p> + * The representations of the date fields are as follows: + * <ul> + * <li> + * Years are specified as the difference between the year + * and 1900. Thus, the final year used is equal to + * 1900 + y, where y is the input value. + * </li> + * <li> + * Months are represented using zero-based indexing, + * making 0 January and 11 December. + * </li> + * <li> + * Dates are represented with the usual values of + * 1 through to 31. + * </li> + * <li> + * Hours are represented in the twenty-four hour clock, + * with integer values from 0 to 23. 12am is 0, and + * 12pm is 12. + * </li> + * <li> + * Minutes are again as usual, with values from 0 to 59. + * </li> + * <li> + * Seconds are represented with the values 0 through to 61, + * with 60 and 61 being leap seconds (as per the ISO C standard). + * </li> + * </ul> + * </p> + * <p> + * Prior to JDK 1.1, this class was the sole class handling date and time + * related functionality. However, this particular solution was not + * amenable to internationalization. The new <code>Calendar</code> + * class should now be used to handle dates and times, with <code>Date</code> + * being used only for values in milliseconds since the epoch. The + * <code>Calendar</code> class, and its concrete implementations, handle + * the interpretation of these values into minutes, hours, days, months + * and years. The formatting and parsing of dates is left to the + * <code>DateFormat</code> class, which is able to handle the different + * types of date format which occur in different locales. + * </p> + * + * @see Calendar + * @see GregorianCalendar + * @see java.text.DateFormat + * @author Jochen Hoenicke + * @author Per Bothner (bothner@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Date + implements Cloneable, Comparable, Serializable +{ + /** + * This is the serialization UID for this class + * for compatability with Sun's JDK. + */ + private static final long serialVersionUID = 7523967970034938905L; + + /** + * The time in milliseconds since the epoch. + */ + private transient long time; + + /** + * An array of week names used to map names to integer values. + */ + private static final String[] weekNames = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" }; + /** + * An array of month names used to map names to integer values. + */ + private static final String[] monthNames = { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + /** + * Creates a new Date Object representing the current time. + */ + public Date() + { + time = System.currentTimeMillis(); + } + + /** + * Creates a new Date Object representing the given time. + * + * @param time the time in milliseconds since the epoch. + */ + public Date(long time) + { + this.time = time; + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use <code>new GregorianCalendar(year+1900, month, + * day)</code> instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + */ + public Date(int year, int month, int day) + { + this(year, month, day, 0, 0, 0); + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use <code>new GregorianCalendar(year+1900, month, + * day, hour, min)</code> instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + * @param hour the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + */ + public Date(int year, int month, int day, int hour, int min) + { + this(year, month, day, hour, min, 0); + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use <code>new GregorianCalendar(year+1900, month, + * day, hour, min, sec)</code> instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + * @param hour the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + * @param sec the second as a value between 0 and 61 (with 60 + * and 61 being leap seconds). + */ + public Date(int year, int month, int day, int hour, int min, int sec) + { + GregorianCalendar cal = + new GregorianCalendar(year + 1900, month, day, hour, min, sec); + time = cal.getTimeInMillis(); + } + + /** + * Creates a new Date from the given string representation. This + * does the same as <code>new Date(Date.parse(s))</code> + * @see #parse + * @deprecated use <code>java.text.DateFormat.parse(s)</code> instead. + */ + public Date(String s) + { + time = parse(s); + } + + /** + * Returns a copy of this <code>Date</code> object. + * + * @return a copy, or null if the object couldn't be + * cloned. + * @see Object#clone() + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + return null; + } + } + + /** + * Returns the number of milliseconds since the epoch + * specified by the given arguments. The arguments are + * interpreted relative to UTC rather than the local + * time zone. + * + * @deprecated Use <code>Calendar</code> with a UTC + * <code>TimeZone</code> instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param date the day as a value between 0 and 31. + * @param hrs the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + * @param sec the second as a value between 0 and 61 (with 60 + * and 61 being leap seconds). + * @return the time in milliseconds since the epoch. + */ + public static long UTC(int year, int month, int date, + int hrs, int min, int sec) + { + GregorianCalendar cal = + new GregorianCalendar(year + 1900, month, date, hrs, min, sec); + cal.set(Calendar.ZONE_OFFSET, 0); + cal.set(Calendar.DST_OFFSET, 0); + return cal.getTimeInMillis(); + } + + /** + * Gets the time represented by this object. + * + * @return the time in milliseconds since the epoch. + */ + public long getTime() + { + return time; + } + + /** + * Returns the number of minutes offset used with UTC to give the time + * represented by this object in the current time zone. The date information + * from this object is also used to determine whether or not daylight savings + * time is in effect. For example, the offset for the UK would be 0 if the + * month of the date object was January, and 1 if the month was August. + * + * @deprecated use + * <code>Calendar.get(Calendar.ZONE_OFFSET)+Calendar.get(Calendar.DST_OFFSET)</code> + * instead. + * @return The time zone offset in minutes of the local time zone + * relative to UTC. The time represented by this object is used to + * determine if we should use daylight savings. + */ + public int getTimezoneOffset() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return - (cal.get(Calendar.ZONE_OFFSET) + + cal.get(Calendar.DST_OFFSET)) / (60 * 1000); + } + + /** + * Sets the time which this object should represent. + * + * @param time the time in milliseconds since the epoch. + */ + public void setTime(long time) + { + this.time = time; + } + + /** + * Tests if this date is after the specified date. + * + * @param when the other date + * @return true, if the date represented by this object is + * strictly later than the time represented by when. + */ + public boolean after(Date when) + { + return time > when.time; + } + + /** + * Tests if this date is before the specified date. + * + * @param when the other date + * @return true, if the date represented by when is strictly later + * than the time represented by this object. + */ + public boolean before(Date when) + { + return time < when.time; + } + + /** + * Compares two dates for equality. + * + * @param obj the object to compare. + * @return true, if obj is a Date object and the time represented + * by obj is exactly the same as the time represented by this + * object. + */ + public boolean equals(Object obj) + { + return (obj instanceof Date && time == ((Date) obj).time); + } + + /** + * Compares two dates. + * + * @param when the other date. + * @return 0, if the date represented + * by obj is exactly the same as the time represented by this + * object, a negative if this Date is before the other Date, and + * a positive value otherwise. + */ + public int compareTo(Date when) + { + return (time < when.time) ? -1 : (time == when.time) ? 0 : 1; + } + + /** + * Compares this Date to another object. This behaves like + * <code>compareTo(Date)</code>, but it takes a generic object + * and throws a <code>ClassCastException</code> if obj is + * not a <code>Date</code>. + * + * @param obj the other date. + * @return 0, if the date represented + * by obj is exactly the same as the time represented by this + * object, a negative if this Date is before the other Date, and + * a positive value otherwise. + * @exception ClassCastException if obj is not of type Date. + */ + public int compareTo(Object obj) + { + return compareTo((Date) obj); + } + + /** + * Computes the hash code of this <code>Date</code> as the + * XOR of the most significant and the least significant + * 32 bits of the 64 bit milliseconds value. + * + * @return the hash code. + */ + public int hashCode() + { + return (int) time ^ (int) (time >>> 32); + } + + /** + * <p> + * Returns a string representation of this date using + * the following date format: + * </p> + * <p> + * <code>day mon dd hh:mm:ss zz yyyy</code> + * </p> + * <p>where the fields used here are: + * <ul> + * <li> + * <code>day</code> -- the day of the week + * (Sunday through to Saturday). + * </li> + * <li> + * <code>mon</code> -- the month (Jan to Dec). + * </li> + * <li> + * <code>dd</code> -- the day of the month + * as two decimal digits (01 to 31). + * </li> + * <li> + * <code>hh</code> -- the hour of the day + * as two decimal digits in 24-hour clock notation + * (01 to 23). + * </li> + * <li> + * <code>mm</code> -- the minute of the day + * as two decimal digits (01 to 59). + * </li> + * <li> + * <code>ss</code> -- the second of the day + * as two decimal digits (01 to 61). + * </li> + * <li> + * <code>zz</code> -- the time zone information if available. + * The possible time zones used include the abbreviations + * recognised by <code>parse()</code> (e.g. GMT, CET, etc.) + * and may reflect the fact that daylight savings time is in + * effect. The empty string is used if there is no time zone + * information. + * </li> + * <li> + * <code>yyyy</code> -- the year as four decimal digits. + * </li> + * </ul> + * <p> + * The <code>DateFormat</code> class should now be + * preferred over using this method. + * </p> + * + * @return A string of the form 'day mon dd hh:mm:ss zz yyyy' + * @see #parse(String) + * @see DateFormat + */ + public String toString() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + String day = "0" + cal.get(Calendar.DATE); + String hour = "0" + cal.get(Calendar.HOUR_OF_DAY); + String min = "0" + cal.get(Calendar.MINUTE); + String sec = "0" + cal.get(Calendar.SECOND); + String year = "000" + cal.get(Calendar.YEAR); + return weekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " " + + monthNames[cal.get(Calendar.MONTH)] + " " + + day.substring(day.length() - 2) + " " + + hour.substring(hour.length() - 2) + ":" + + min.substring(min.length() - 2) + ":" + + sec.substring(sec.length() - 2) + " " + + + cal.getTimeZone().getDisplayName(cal.getTimeZone().inDaylightTime(this), + TimeZone.SHORT) + " " + + year.substring(year.length() - 4); + } + + /** + * Returns a locale-dependent string representation of this + * <code>Date</code> object. + * + * @deprecated Use DateFormat.format(Date) + * @return A locale-dependent string representation. + * @see #parse(String) + * @see DateFormat + */ + public String toLocaleString() + { + return java.text.DateFormat.getInstance().format(this); + } + + /** + * <p> + * Returns a string representation of this <code>Date</code> + * object using GMT rather than the local timezone. + * The following date format is used: + * </p> + * <p> + * <code>d mon yyyy hh:mm:ss GMT</code> + * </p> + * <p>where the fields used here are: + * <ul> + * <li> + * <code>d</code> -- the day of the month + * as one or two decimal digits (1 to 31). + * </li> + * <li> + * <code>mon</code> -- the month (Jan to Dec). + * </li> + * <li> + * <code>yyyy</code> -- the year as four decimal digits. + * </li> + * <li> + * <code>hh</code> -- the hour of the day + * as two decimal digits in 24-hour clock notation + * (01 to 23). + * </li> + * <li> + * <code>mm</code> -- the minute of the day + * as two decimal digits (01 to 59). + * </li> + * <li> + * <code>ss</code> -- the second of the day + * as two decimal digits (01 to 61). + * </li> + * <li> + * <code>GMT</code> -- the literal string "GMT" + * indicating Greenwich Mean Time as opposed to + * the local timezone. + * </li> + * </ul> + * + * @deprecated Use DateFormat.format(Date) with a GMT TimeZone. + * @return A string of the form 'd mon yyyy hh:mm:ss GMT' using + * GMT as opposed to the local timezone. + * @see #parse(String) + * @see DateFormat + */ + public String toGMTString() + { + java.text.DateFormat format = java.text.DateFormat.getInstance(); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + return format.format(this); + } + + /** + * Parses the time zone string. + * + * @param tok The token containing the time zone. + * @param sign The sign (+ or -) used by the time zone. + * @return An integer representing the number of minutes offset + * from GMT for the time zone. + */ + private static int parseTz(String tok, char sign) + throws IllegalArgumentException + { + int num; + + try + { + // parseInt doesn't handle '+' so strip off sign. + num = Integer.parseInt(tok.substring(1)); + } + catch (NumberFormatException ex) + { + throw new IllegalArgumentException(tok); + } + + // Convert hours to minutes. + if (num < 24) + num *= 60; + else + num = (num / 100) * 60 + num % 100; + + return sign == '-' ? -num : num; + } + + /** + * Parses the month string. + * + * @param tok the token containing the month. + * @return An integer between 0 and 11, representing + * a month from January (0) to December (11), + * or -1 if parsing failed. + */ + private static int parseMonth(String tok) + { + // Initialize strings for month names. + // We could possibly use the fields of DateFormatSymbols but that is + // localized and thus might not match the English words specified. + String months[] = { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", + "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", + "NOVEMBER", "DECEMBER" }; + + int i; + for (i = 0; i < 12; i++) + if (months[i].startsWith(tok)) + return i; + + // Return -1 if not found. + return -1; + } + + /** + * Parses the day of the week string. + * + * @param tok the token containing the day of the week. + * @return true if the token was parsed successfully. + */ + private static boolean parseDayOfWeek(String tok) + { + // Initialize strings for days of the week names. + // We could possibly use the fields of DateFormatSymbols but that is + // localized and thus might not match the English words specified. + String daysOfWeek[] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", + "THURSDAY", "FRIDAY", "SATURDAY" }; + + int i; + for (i = 0; i < 7; i++) + if (daysOfWeek[i].startsWith(tok)) + return true; + + return false; + } + + /** + * <p> + * Parses a String and returns the time, in milliseconds since the + * epoch, it represents. Most syntaxes are handled, including + * the IETF date standard "day, dd mon yyyy hh:mm:ss zz" (see + * <code>toString()</code> for definitions of these fields). + * Standard U.S. time zone abbreviations are recognised, in + * addition to time zone offsets in positive or negative minutes. + * If a time zone is specified, the specified time is assumed to + * be in UTC and the appropriate conversion is applied, following + * parsing, to convert this to the local time zone. If no zone + * is specified, the time is assumed to already be in the local + * time zone. + * </p> + * <p> + * The method parses the string progressively from left to right. + * At the end of the parsing process, either a time is returned + * or an <code>IllegalArgumentException</code> is thrown to signify + * failure. The ASCII characters A-Z, a-z, 0-9, and ',', '+', '-', + * ':' and '/' are the only characters permitted within the string, + * besides whitespace and characters enclosed within parantheses + * '(' and ')'. + * </p> + * <p> + * A sequence of consecutive digits are recognised as a number, + * and interpreted as follows: + * <ul> + * <li> + * A number preceded by a sign (+ or -) is taken to be a time zone + * offset. The time zone offset can be specified in either hours + * or minutes. The former is assumed if the number is less than 24. + * Otherwise, the offset is assumed to be in minutes. A - indicates + * a time zone west of GMT, while a + represents a time zone to the + * east of GMT. The time zones are always assumed to be relative + * to GMT, and a (redundant) specification of this can be included + * with the time zone. For example, '-9', 'utc-9' and 'GMT-9' all + * represent a time zone nine hours west of GMT. Similarly, + * '+4', 'ut+4' and 'UTC+4' all give 4 hours east of GMT. + * </li> + * <li> + * A number equal to or greater than 70 is regarded as a year specification. + * Values lower than 70 are only assumed to indicate a year if both the + * day of the month and the month itself have already been recognised. + * Year values less than 100 are interpreted as being relative to the current + * century when the <code>Date</code> class is initialised.. Given a century, + * x, the year is assumed to be within the range x - 80 to x + 19. The value + * itself is then used as a match against the two last digits of one of these + * years. For example, take x to be 2004. A two-digit year is assumed to fall + * within the range x - 80 (1924) and x + 19 (2023). Thus, any intepreted value + * between 0 and 23 is assumed to be 2000 to 2023 and values between 24 and 99 + * are taken as being 1924 to 1999. This only applies for the case of 2004. + * With a different year, the values will be interpreted differently. 2005 + * will used 0 to 24 as 2000 to 2024 and 25 to 99 as 1925 to 1999, for example. + * This behaviour differs from that of <code>SimpleDateFormat</code> and is + * time-dependent (a two-digit year will be interpreted differently depending + * on the time the code is run). + * </li> + * <li> + * Numbers followed by a colon are interpreted by first an hour, and then + * as a minute, once an hour has been found. + * </li> + * <li> + * <li> + * Numbers followed by a slash are regarded first as a month, and then as + * a day of the month once the month has been found. This follows the + * U.S. date format of mm/dd, rather than the European dd/mm. Months + * are converted to the recognised value - 1 before storage, in order + * to put the number within the range 0 to 11. + * </li> + * <li> + * Numbers followed by commas, whitespace, hyphens or the end of the string + * are interpreted in the following order: hour, minute, second, day of month. + * The first type not already recognised in the current string being parsed is + * assumed. + * </li> + * </ul> + * </p> + * <p> + * A sequence of consecutive alphabetic characters is recognised as a word, + * and interpreted as follows, in a case-insentive fashion: + * <ul> + * <li> + * The characters 'AM' or 'PM' restrict the hour value to a value between 0 + * and 12. In the latter case, 12 is added to the hour value before storage. + * </li> + * <li> + * Any words which match any prefix of one of the days of the week ('Monday', + * 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday'), + * are simply ignored. + * </li> + * <li> + * Any words which match any prefix of one of the months of the year ('January', + * 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', + * 'October', 'November', 'December') are recognised and interpreted as the + * appropriate value between 0 and 11. The first match made against a + * month is the one used, in the order specified here. For example, 'Ma' is + * intepreted as 'March' (2) and not as 'May' (4). Similarly, 'Ju' is 'June', + * and not 'July'. + * </li> + * <li> + * The words 'GMT', 'UT' and 'UTC' are interpreted as specifying UTC as the + * time zone in use for this date. + * </li> + * <li> + * The word pairs 'EST'/'EDT', 'CST'/'CDT', 'MST'/'MDT' and 'PST'/'PDT' are + * interpreted as the appropriate U.S. time zone abbreviation. Each pair + * is the standard and daylight savings time zone specification, respectively, + * for each zone within the U.S, these being Eastern Standard/Daylight Time + * (-5), Central Standard/Daylight Time (-6), Mountain Standard/Daylight Time + * (-7) and Pacific Standard/Daylight Time (-8). + * </li> + * </ul> + * + * @param string The String to parse. + * @return The time in milliseconds since the epoch. + * @throws IllegalArgumentException if the string fails to parse. + * @deprecated Use DateFormat.parse(String) + * @see #toString() + * @see SimpleDateFormat + */ + public static long parse(String string) + { + // Initialize date/time fields before parsing begins. + int year = -1; + int month = -1; + int day = -1; + int hour = -1; + int minute = -1; + int second = -1; + int timezone = 0; + boolean localTimezone = true; + + // Trim out any nested stuff in parentheses now to make parsing easier. + StringBuffer buf = new StringBuffer(); + int parenNesting = 0; + int len = string.length(); + for (int i = 0; i < len; i++) + { + char ch = string.charAt(i); + if (ch >= 'a' && ch <= 'z') + ch -= 'a' - 'A'; + if (ch == '(') + parenNesting++; + else if (parenNesting == 0) + buf.append(ch); + else if (ch == ')') + parenNesting--; + } + int tmpMonth; + + // Make all chars upper case to simplify comparisons later. + // Also ignore commas; treat them as delimiters. + StringTokenizer strtok = new StringTokenizer(buf.toString(), " \t\n\r,"); + + while (strtok.hasMoreTokens()) + { + String tok = strtok.nextToken(); + char firstch = tok.charAt(0); + if ((firstch == '+' || firstch == '-') && year >= 0) + { + timezone = parseTz(tok, firstch); + localTimezone = false; + } + else if (firstch >= '0' && firstch <= '9') + { + while (tok != null && tok.length() > 0) + { + int punctOffset = tok.length(); + int num = 0; + int punct; + for (int i = 0; ; i++) + { + if (i >= punctOffset) + { + punct = -1; + break; + } + else + { + punct = tok.charAt(i); + if (punct >= '0' && punct <= '9') + { + if (num > 999999999) // in case of overflow + throw new IllegalArgumentException(tok); + num = 10 * num + (punct - '0'); + } + else + { + punctOffset = i; + break; + } + } + + } + + if (punct == ':') + { + if (hour < 0) + hour = num; + else + minute = num; + } + else if ((num >= 70 + && (punct == ' ' || punct == ',' + || punct == '/' || punct < 0)) + || (num < 70 && day >= 0 && month >= 0 && year < 0)) + { + if (num >= 100) + year = num; + else + { + int curYear = 1900 + new Date().getYear(); + int firstYear = curYear - 80; + year = firstYear / 100 * 100 + num; + if (year < firstYear) + year += 100; + } + } + else if (punct == '/') + { + if (month < 0) + month = num - 1; + else + day = num; + } + else if (hour >= 0 && minute < 0) + minute = num; + else if (minute >= 0 && second < 0) + second = num; + else if (day < 0) + day = num; + else + throw new IllegalArgumentException(tok); + + // Advance string if there's more to process in this token. + if (punct < 0 || punctOffset + 1 >= tok.length()) + tok = null; + else + tok = tok.substring(punctOffset + 1); + } + } + else if (firstch >= 'A' && firstch <= 'Z') + { + if (tok.equals("AM")) + { + if (hour < 1 || hour > 12) + throw new IllegalArgumentException(tok); + if (hour == 12) + hour = 0; + } + else if (tok.equals("PM")) + { + if (hour < 1 || hour > 12) + throw new IllegalArgumentException(tok); + if (hour < 12) + hour += 12; + } + else if (parseDayOfWeek(tok)) + ; // Ignore it; throw the token away. + else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT")) + localTimezone = false; + else if (tok.startsWith("UT") || tok.startsWith("GMT")) + { + int signOffset = 3; + if (tok.charAt(1) == 'T' && tok.charAt(2) != 'C') + signOffset = 2; + + char sign = tok.charAt(signOffset); + if (sign != '+' && sign != '-') + throw new IllegalArgumentException(tok); + + timezone = parseTz(tok.substring(signOffset), sign); + localTimezone = false; + } + else if ((tmpMonth = parseMonth(tok)) >= 0) + month = tmpMonth; + else if (tok.length() == 3 && tok.charAt(2) == 'T') + { + // Convert timezone offset from hours to minutes. + char ch = tok.charAt(0); + if (ch == 'E') + timezone = -5 * 60; + else if (ch == 'C') + timezone = -6 * 60; + else if (ch == 'M') + timezone = -7 * 60; + else if (ch == 'P') + timezone = -8 * 60; + else + throw new IllegalArgumentException(tok); + + // Shift 60 minutes for Daylight Savings Time. + if (tok.charAt(1) == 'D') + timezone += 60; + else if (tok.charAt(1) != 'S') + throw new IllegalArgumentException(tok); + + localTimezone = false; + } + else + throw new IllegalArgumentException(tok); + } + else + throw new IllegalArgumentException(tok); + } + + // Unspecified hours, minutes, or seconds should default to 0. + if (hour < 0) + hour = 0; + if (minute < 0) + minute = 0; + if (second < 0) + second = 0; + + // Throw exception if any other fields have not been recognized and set. + if (year < 0 || month < 0 || day < 0) + throw new IllegalArgumentException("Missing field"); + + // Return the time in either local time or relative to GMT as parsed. + // If no time-zone was specified, get the local one (in minutes) and + // convert to milliseconds before adding to the UTC. + GregorianCalendar cal + = new GregorianCalendar(year, month, day, hour, minute, second); + if (!localTimezone) + { + cal.set(Calendar.ZONE_OFFSET, timezone * 60 * 1000); + cal.set(Calendar.DST_OFFSET, 0); + } + return cal.getTimeInMillis(); + } + + /** + * Returns the difference between the year represented by this + * <code>Date</code> object and 1900. + * + * @return the year minus 1900 represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.YEAR) + * instead. Note the 1900 difference in the year. + * @see Calendar + * @see #setYear(int) + */ + public int getYear() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.YEAR) - 1900; + } + + /** + * Sets the year to the specified year, plus 1900. The other + * fields are only altered as required to match the same date + * and time in the new year. Usually, this will mean that + * the fields are not changed at all, but in the case of + * a leap day or leap second, the fields will change in + * relation to the existence of such an event in the new year. + * For example, if the date specifies February the 29th, 2000, + * then this will become March the 1st if the year is changed + * to 2001, as 2001 is not a leap year. Similarly, a seconds + * value of 60 or 61 may result in the seconds becoming 0 and + * the minute increasing by 1, if the new time does not include + * a leap second. + * + * @param year the year minus 1900. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.YEAR, year) instead. Note about the 1900 + * difference in year. + * @see #getYear() + * @see Calendar + */ + public void setYear(int year) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.YEAR, 1900 + year); + time = cal.getTimeInMillis(); + } + + /** + * Returns the month represented by this <code>Date</code> object, + * as a value between 0 (January) and 11 (December). + * + * @return the month represented by this date object (zero based). + * @deprecated Use Calendar instead of Date, and use get(Calendar.MONTH) + * instead. + * @see #setMonth(int) + * @see Calendar + */ + public int getMonth() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.MONTH); + } + + /** + * Sets the month to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new month. In most + * cases, the other fields won't change at all. However, + * in the case of a shorter month or a leap second, values + * may be adjusted. For example, if the day of the month + * is currently 31, and the month value is changed from + * January (0) to September (8), the date will become + * October the 1st, as September only has 30 days. Similarly, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the new time does + * not include a leap second. + * + * @param month the month, with a zero-based index + * from January. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.MONTH, month) instead. + * @see #getMonth() + * @see Calendar + */ + public void setMonth(int month) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.MONTH, month); + time = cal.getTimeInMillis(); + } + + /** + * Returns the day of the month of this <code>Date</code> + * object, as a value between 0 and 31. + * + * @return the day of month represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.DATE) + * instead. + * @see Calendar + * @see #setDate(int) + */ + public int getDate() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.DATE); + } + + /** + * Sets the date to the given value. The other + * fields are only altered as necessary to match + * the same date and time on the new day of the month. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second or the day being out of + * the range of the current month, values + * may be adjusted. For example, if the day of the month + * is currently 30 and the month is June, a new day of the + * month value of 31 will cause the month to change to July, + * as June only has 30 days . Similarly, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the new time does + * not include a leap second. + * + * @param date the date. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.DATE, date) instead. + * @see Calendar + * @see #getDate() + */ + public void setDate(int date) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.DATE, date); + time = cal.getTimeInMillis(); + } + + /** + * Returns the day represented by this <code>Date</code> + * object as an integer between 0 (Sunday) and 6 (Saturday). + * + * @return the day represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.DAY_OF_WEEK) + * instead. + * @see Calendar + */ + public int getDay() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + // For Calendar, Sunday is 1. For Date, Sunday is 0. + return cal.get(Calendar.DAY_OF_WEEK) - 1; + } + + /** + * Returns the hours represented by this <code>Date</code> + * object as an integer between 0 and 23. + * + * @return the hours represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.HOUR_OF_DAY) + * instead. + * @see Calendar + * @see #setHours(int) + */ + public int getHours() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.HOUR_OF_DAY); + } + + /** + * Sets the hours to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new hour. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1 if the new hour does + * not contain a leap second. + * + * @param hours the hours. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.HOUR_OF_DAY, hours) instead. + * @see Calendar + * @see #getHours() + */ + public void setHours(int hours) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.HOUR_OF_DAY, hours); + time = cal.getTimeInMillis(); + } + + /** + * Returns the number of minutes represented by the <code>Date</code> + * object, as an integer between 0 and 59. + * + * @return the minutes represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.MINUTE) + * instead. + * @see Calendar + * @see #setMinutes(int) + */ + public int getMinutes() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.MINUTE); + } + + /** + * Sets the minutes to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new minute. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1 if the new minute does + * not contain a leap second. + * + * @param minutes the minutes. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.MINUTE, minutes) instead. + * @see Calendar + * @see #getMinutes() + */ + public void setMinutes(int minutes) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.MINUTE, minutes); + time = cal.getTimeInMillis(); + } + + /** + * Returns the number of seconds represented by the <code>Date</code> + * object, as an integer between 0 and 61 (60 and 61 being leap seconds). + * + * @return the seconds represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.SECOND) + * instead. + * @see Calendar + * @see #setSeconds(int) + */ + public int getSeconds() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.SECOND); + } + + /** + * Sets the seconds to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new minute. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, setting the + * seconds value to 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the current time does + * not contain a leap second. + * + * @param seconds the seconds. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.SECOND, seconds) instead. + * @see Calendar + * @see #getSeconds() + */ + public void setSeconds(int seconds) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.SECOND, seconds); + time = cal.getTimeInMillis(); + } + + /** + * Deserializes a <code>Date</code> object from an + * input stream, setting the time (in milliseconds + * since the epoch) to the long value read from the + * stream. + * + * @param input the input stream. + * @throws IOException if an I/O error occurs in the stream. + * @throws ClassNotFoundException if the class of the + * serialized object could not be found. + */ + private void readObject(ObjectInputStream input) + throws IOException, ClassNotFoundException + { + input.defaultReadObject(); + time = input.readLong(); + } + + /** + * Serializes a <code>Date</code> object to an output stream, + * storing the time (in milliseconds since the epoch) as a long + * value in the stream. + * + * @serialdata A long value representing the offset from the epoch + * in milliseconds. This is the same value that is returned by the + * method getTime(). + * @param output the output stream. + * @throws IOException if an I/O error occurs in the stream. + */ + private void writeObject(ObjectOutputStream output) + throws IOException + { + output.defaultWriteObject(); + output.writeLong(time); + } + +} diff --git a/libjava/classpath/java/util/Dictionary.java b/libjava/classpath/java/util/Dictionary.java new file mode 100644 index 00000000000..0d44ab67de1 --- /dev/null +++ b/libjava/classpath/java/util/Dictionary.java @@ -0,0 +1,136 @@ +/* Dictionary.java -- an abstract (and essentially worthless) + class which is Hashtable's superclass + Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A Dictionary maps keys to values; <i>how</i> it does that is + * implementation-specific. + * + * This is an abstract class which has really gone by the wayside. + * People at Javasoft are probably embarrassed by it. At this point, + * it might as well be an interface rather than a class, but it remains + * this poor, laughable skeleton for the sake of backwards compatibility. + * At any rate, this was what came before the {@link Map} interface + * in the Collections framework. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Hashtable + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Dictionary +{ + // WARNING: Dictionary is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * Sole constructor (often called implicitly). + */ + public Dictionary() + { + } + + /** + * Returns an Enumeration of the values in this Dictionary. + * + * @return an Enumeration of the values + * @see #keys() + */ + public abstract Enumeration elements(); + + /** + * Returns the value associated with the supplied key, or null + * if no such value exists. Since Dictionaries are not allowed null keys + * or elements, a null result always means the key is not present. + * + * @param key the key to use to fetch the value + * @return the mapped value + * @throws NullPointerException if key is null + * @see #put(Object, Object) + */ + public abstract Object get(Object key); + + /** + * Returns true when there are no elements in this Dictionary. + * + * @return <code>size() == 0</code> + */ + public abstract boolean isEmpty(); + + /** + * Returns an Enumeration of the keys in this Dictionary + * + * @return an Enumeration of the keys + * @see #elements() + */ + public abstract Enumeration keys(); + + /** + * Inserts a new value into this Dictionary, located by the + * supplied key. Dictionary does not support null keys or values, so + * a null return can safely be interpreted as adding a new key. + * + * @param key the key which locates the value + * @param value the value to put into the Dictionary + * @return the previous value of the key, or null if there was none + * @throws NullPointerException if key or value is null + * @see #get(Object) + */ + public abstract Object put(Object key, Object value); + + /** + * Removes from the Dictionary the value located by the given key. A null + * return safely means that the key was not mapped in the Dictionary. + * + * @param key the key used to locate the value to be removed + * @return the value associated with the removed key + * @throws NullPointerException if key is null + */ + public abstract Object remove(Object key); + + /** + * Returns the number of values currently in this Dictionary. + * + * @return the number of keys in the Dictionary + */ + public abstract int size(); +} // class Dictionary diff --git a/libjava/classpath/java/util/EmptyStackException.java b/libjava/classpath/java/util/EmptyStackException.java new file mode 100644 index 00000000000..e8b4509ee5c --- /dev/null +++ b/libjava/classpath/java/util/EmptyStackException.java @@ -0,0 +1,69 @@ +/* EmptyStackException.java -- Attempt to pop from an empty stack + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * This exception is thrown by the Stack class when an attempt is made to pop + * or otherwise access elements from an empty stack. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Stack + * @since 1.0 + * @status updated to 1.4 + */ +public class EmptyStackException extends RuntimeException +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 5084686378493302095L; + + /** + * Constructs an EmptyStackException with no detail message. + */ + public EmptyStackException() + { + } +} diff --git a/libjava/classpath/java/util/Enumeration.java b/libjava/classpath/java/util/Enumeration.java new file mode 100644 index 00000000000..1365bbb2f0f --- /dev/null +++ b/libjava/classpath/java/util/Enumeration.java @@ -0,0 +1,81 @@ +/* Enumeration.java -- Interface for enumerating lists of objects + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1. + * Status: Believed complete and correct + */ + +/** + * Interface for lists of objects that can be returned in sequence. Successive + * objects are obtained by the nextElement method. + * <p> + * As of Java 1.2, the Iterator interface provides the same functionality, but + * with shorter method names and a new optional method to remove items from the + * list. If writing for 1.2, consider using Iterator instead. Enumerations over + * the new collections classes, for use with legacy APIs that require them, can + * be obtained by the enumeration method in class Collections. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Iterator + * @see Hashtable + * @see Vector + * @since 1.0 + * @status updated to 1.4 + */ +public interface Enumeration +{ + /** + * Tests whether there are elements remaining in the enumeration. + * + * @return true if there is at least one more element in the enumeration, + * that is, if the next call to nextElement will not throw a + * NoSuchElementException. + */ + boolean hasMoreElements(); + + /** + * Obtain the next element in the enumeration. + * + * @return the next element in the enumeration + * @throws NoSuchElementException if there are no more elements + */ + Object nextElement(); +} diff --git a/libjava/classpath/java/util/EventListener.java b/libjava/classpath/java/util/EventListener.java new file mode 100644 index 00000000000..c9a1795f900 --- /dev/null +++ b/libjava/classpath/java/util/EventListener.java @@ -0,0 +1,54 @@ +/* EventListener.java -- tagging interface for all event listeners + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Empty interface that is implemented by classes that need to receive + * events. Subinterfaces define methods that can be called to fire an + * event notification. Normally the name of these subinterfaces end in + * <code>Listener</code> and all method described by the subinterface + * take as argument an subclass of <code>EventObject</code>. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see EventObject + * @status updated to 1.4 + */ +public interface EventListener +{ +} diff --git a/libjava/classpath/java/util/EventListenerProxy.java b/libjava/classpath/java/util/EventListenerProxy.java new file mode 100644 index 00000000000..245c5ffb458 --- /dev/null +++ b/libjava/classpath/java/util/EventListenerProxy.java @@ -0,0 +1,75 @@ +/* EventListenerProxy.java -- abstract wrapper for event listeners + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An abstract wrapper for event listeners. This allows subclasses to + * attach additional parameters to an existing event listener to create + * a new one. Subclasses are expected to add methods to set and retrieve + * any attached properties. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class EventListenerProxy implements EventListener +{ + /** The listener that this proxy wraps. */ + private final EventListener listener; + + /** + * Construct a proxy event listener, given an existing one to augment. + * + * @param listener the listener to wrap + */ + public EventListenerProxy(EventListener listener) + { + this.listener = listener; + } + + /** + * Return the wrapped event listener. + * + * @return the listener associated with this proxy + */ + public EventListener getListener() + { + return listener; + } +} // class EventListenerProxy diff --git a/libjava/classpath/java/util/EventObject.java b/libjava/classpath/java/util/EventObject.java new file mode 100644 index 00000000000..7ced18aa4ba --- /dev/null +++ b/libjava/classpath/java/util/EventObject.java @@ -0,0 +1,101 @@ +/* EventObject.java -- represents an event on an object + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * Represents Events fired by Objects. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see EventListener + * @since 1.1 + * @status updated to 1.4 + */ +public class EventObject implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5516075349620653480L; + + /** + * The source object; in other words, the object which this event takes + * place on. + */ + protected transient Object source; + + /** + * Constructs an EventObject with the specified source. + * + * @param source the source of the event + * @throws IllegalArgumentException if source is null (This is not + * specified, but matches the behavior of the JDK) + */ + public EventObject(Object source) + { + // This check for null is stupid, if you ask me, since source is + // protected and non-final, so a subclass can set it to null later on. + if (source == null) + throw new IllegalArgumentException(); + this.source = source; + } + + /** + * Returns the source of the event. + * + * @return the event source + */ + public Object getSource() + { + return source; + } + + /** + * Converts the event to a String. The format is not specified, but by + * observation, the JDK uses: + * <code>getClass().getName() + "[source=" + source + "]";</code>. + * + * @return String representation of the Event + */ + public String toString() + { + return getClass().getName() + "[source=" + source + "]"; + } +} // class EventObject diff --git a/libjava/classpath/java/util/GregorianCalendar.java b/libjava/classpath/java/util/GregorianCalendar.java new file mode 100644 index 00000000000..b086a7d04a4 --- /dev/null +++ b/libjava/classpath/java/util/GregorianCalendar.java @@ -0,0 +1,1343 @@ +/* java.util.GregorianCalendar + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + + +/** + * <p> + * This class represents the Gregorian calendar, that is used in most + * countries all over the world. It does also handle the Julian calendar + * for dates smaller than the date of the change to the Gregorian calendar. + * The Gregorian calendar differs from the Julian calendar by a different + * leap year rule (no leap year every 100 years, except if year is divisible + * by 400). + * </p> + * <p> + * This change date is different from country to country, and can be changed with + * <code>setGregorianChange</code>. The first countries to adopt the Gregorian + * calendar did so on the 15th of October, 1582. This date followed October + * the 4th, 1582 in the Julian calendar system. The non-existant days that were + * omitted when the change took place are interpreted as Gregorian dates. + * </p> + * <p> + * Prior to the changeover date, New Year's Day occurred on the 25th of March. + * However, this class always takes New Year's Day as being the 1st of January. + * Client code should manually adapt the year value, if required, for dates + * between January the 1st and March the 24th in years prior to the changeover. + * </p> + * <p> + * Any date infinitely forwards or backwards in time can be represented by + * this class. A <em>proleptic</em> calendar system is used, which allows + * future dates to be created via the existing rules. This allows meaningful + * and consistent dates to be produced for all years. However, dates are only + * historically accurate following March the 1st, 4AD when the Julian calendar + * system was adopted. Prior to this, leap year rules were applied erraticly. + * </p> + * <p> + * There are two eras available for the Gregorian calendar, namely BC and AD. + * </p> + * <p> + * Weeks are defined as a period of seven days, beginning on the first day + * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending + * on the day prior to this. + * </p> + * <p> + * The weeks of the year are numbered from 1 to a possible 53. The first week + * of the year is defined as the first week that contains at least the minimum + * number of days of the first week in the new year (retrieved via + * <code>getMinimalDaysInFirstWeek()</code>). All weeks after this are numbered + * from 2 onwards. + * </p> + * <p> + * For example, take the year 2004. It began on a Thursday. The first week + * of 2004 depends both on where a week begins and how long it must minimally + * last. Let's say that the week begins on a Monday and must have a minimum + * of 5 days. In this case, the first week begins on Monday, the 5th of January. + * The first 4 days (Thursday to Sunday) are not eligible, as they are too few + * to make up the minimum number of days of the first week which must be in + * the new year. If the minimum was lowered to 4 days, then the first week + * would instead begin on Monday, the 29th of December, 2003. This first week + * has 4 of its days in the new year, and is now eligible. + * </p> + * <p> + * The weeks of the month are numbered from 0 to a possible 6. The first week + * of the month (numbered 1) is a set of days, prior to the first day of the week, + * which number at least the minimum number of days in a week. Unlike the first + * week of the year, the first week of the month only uses days from that particular + * month. As a consequence, it may have a variable number of days (from the minimum + * number required up to a full week of 7) and it need not start on the first day of + * the week. It must, however, be following by the first day of the week, as this + * marks the beginning of week 2. Any days of the month which occur prior to the + * first week (because the first day of the week occurs before the minimum number + * of days is met) are seen as week 0. + * </p> + * <p> + * Again, we will take the example of the year 2004 to demonstrate this. September + * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the + * minimum length of the first week as 6, we find that week 1 runs from Monday, + * the 6th of September to Sunday the 12th. Prior to the 6th, there are only + * 5 days (Wednesday through to Sunday). This is too small a number to meet the + * minimum, so these are classed as being days in week 0. Week 2 begins on the + * 13th, and so on. This changes if we reduce the minimum to 5. In this case, + * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week + * 0 doesn't exist. The first seven day week is week 2, starting on the 6th. + * </p> + * <p> + * On using the <code>clear()</code> method, the Gregorian calendar returns + * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch). + * The day of the week is set to the correct day for that particular time. + * The day is also the first of the month, and the date is in week 0. + * </p> + * + * @see Calendar + * @see TimeZone + * @see Calendar#getFirstDayOfWeek() + * @see Calendar#getMinimalDaysInFirstWeek() + */ +public class GregorianCalendar extends Calendar +{ + /** + * Constant representing the era BC (Before Christ). + */ + public static final int BC = 0; + + /** + * Constant representing the era AD (Anno Domini). + */ + public static final int AD = 1; + + /** + * The point at which the Gregorian calendar rules were used. + * This may be changed by using setGregorianChange; + * The default is midnight (UTC) on October 5, 1582 (Julian), + * or October 15, 1582 (Gregorian). + * + * @serial the changeover point from the Julian calendar + * system to the Gregorian. + */ + private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4 + + 1)) / 4 + + (java.util.Calendar.OCTOBER * (31 + + 30 + 31 + 30 + 31) - 9) / 5 + 5) + - ((1970 * (365 * 4 + 1)) / 4 + 1 + - 13)))).getTime(); + + /** + * For compatability with Sun's JDK. + */ + static final long serialVersionUID = -8125100834729963327L; + + /** + * Days in the epoch. Relative Jan 1, year '0' which is not a leap year. + * (although there is no year zero, this does not matter.) + * This is consistent with the formula: + * = (year-1)*365L + ((year-1) >> 2) + * + * Plus the gregorian correction: + * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.); + * For a correct julian date, the correction is -2 instead. + * + * The gregorian cutover in 1582 was 10 days, so by calculating the + * correction from year zero, we have 15 non-leap days (even centuries) + * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects + * this to the correct number 10. + */ + private static final int EPOCH_DAYS = 719162; + + /** + * Constructs a new GregorianCalender representing the current + * time, using the default time zone and the default locale. + */ + public GregorianCalendar() + { + this(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Constructs a new GregorianCalender representing the current + * time, using the specified time zone and the default locale. + * + * @param zone a time zone. + */ + public GregorianCalendar(TimeZone zone) + { + this(zone, Locale.getDefault()); + } + + /** + * Constructs a new GregorianCalender representing the current + * time, using the default time zone and the specified locale. + * + * @param locale a locale. + */ + public GregorianCalendar(Locale locale) + { + this(TimeZone.getDefault(), locale); + } + + /** + * Constructs a new GregorianCalender representing the current + * time with the given time zone and the given locale. + * + * @param zone a time zone. + * @param locale a locale. + */ + public GregorianCalendar(TimeZone zone, Locale locale) + { + this(zone, locale, false); + setTimeInMillis(System.currentTimeMillis()); + complete(); + } + + /** + * Common constructor that all constructors should call. + * @param zone a time zone. + * @param locale a locale. + * @param unused unused parameter to make the signature differ from + * the public constructor (TimeZone, Locale). + */ + private GregorianCalendar(TimeZone zone, Locale locale, boolean unused) + { + super(zone, locale); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + */ + public GregorianCalendar(int year, int month, int day) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + * @param hour corresponds to the HOUR_OF_DAY time field. + * @param minute corresponds to the MINUTE time field. + */ + public GregorianCalendar(int year, int month, int day, int hour, int minute) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day, hour, minute); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + * @param hour corresponds to the HOUR_OF_DAY time field. + * @param minute corresponds to the MINUTE time field. + * @param second corresponds to the SECOND time field. + */ + public GregorianCalendar(int year, int month, int day, int hour, int minute, + int second) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day, hour, minute, second); + } + + /** + * Sets the date of the switch from Julian dates to Gregorian dates. + * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure + * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian + * calendar. + * + * @param date the date of the change. + */ + public void setGregorianChange(Date date) + { + gregorianCutover = date.getTime(); + } + + /** + * Gets the date of the switch from Julian dates to Gregorian dates. + * + * @return the date of the change. + */ + public final Date getGregorianChange() + { + return new Date(gregorianCutover); + } + + /** + * <p> + * Determines if the given year is a leap year. The result is + * undefined if the Gregorian change took place in 1800, so that + * the end of February is skipped, and that year is specified. + * (well...). + * </p> + * <p> + * To specify a year in the BC era, use a negative value calculated + * as 1 - y, where y is the required year in BC. So, 1 BC is 0, + * 2 BC is -1, 3 BC is -2, etc. + * </p> + * + * @param year a year (use a negative value for BC). + * @return true, if the given year is a leap year, false otherwise. + */ + public boolean isLeapYear(int year) + { + // Only years divisible by 4 can be leap years + if ((year & 3) != 0) + return false; + + // Is the leap-day a Julian date? Then it's a leap year + if (! isGregorian(year, 31 + 29 - 1)) + return true; + + // Apply gregorian rules otherwise + return ((year % 100) != 0 || (year % 400) == 0); + } + + /** + * Retrieves the day of the week corresponding to the specified + * day of the specified year. + * + * @param year the year in which the dayOfYear occurs. + * @param dayOfYear the day of the year (an integer between 0 and + * and 366) + */ + private int getWeekDay(int year, int dayOfYear) + { + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * Returns the day of the week for the first day of a given month (0..11) + */ + private int getFirstDayOfMonth(int year, int month) + { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + if (month > 11) + { + year += (month / 12); + month = month % 12; + } + + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } + } + + int dayOfYear = dayCount[month] + 1; + if (month > 1) + if (isLeapYear(year)) + dayOfYear++; + + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * Takes a year, and a (zero based) day of year and determines + * if it is gregorian or not. + */ + private boolean isGregorian(int year, int dayOfYear) + { + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover); + } + + /** + * Check set fields for validity, without leniency. + * + * @throws IllegalArgumentException if a field is invalid + */ + private void nonLeniencyCheck() throws IllegalArgumentException + { + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int year = fields[YEAR]; + int month = fields[MONTH]; + int leap = isLeapYear(year) ? 1 : 0; + + if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC) + throw new IllegalArgumentException("Illegal ERA."); + if (isSet[YEAR] && fields[YEAR] < 1) + throw new IllegalArgumentException("Illegal YEAR."); + if (isSet[MONTH] && (month < 0 || month > 11)) + throw new IllegalArgumentException("Illegal MONTH."); + if (isSet[WEEK_OF_YEAR]) + { + int daysInYear = 365 + leap; + daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week + int last = getFirstDayOfMonth(year, 11) + 4; + if (last > 7) + last -= 7; + daysInYear += 7 - last; + int weeks = daysInYear / 7; + if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_YEAR."); + } + + if (isSet[WEEK_OF_MONTH]) + { + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_MONTH."); + } + + if (isSet[DAY_OF_MONTH]) + if (fields[DAY_OF_MONTH] < 1 + || fields[DAY_OF_MONTH] > month_days[month] + + ((month == 1) ? leap : 0)) + throw new IllegalArgumentException("Illegal DAY_OF_MONTH."); + + if (isSet[DAY_OF_YEAR] + && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap)) + throw new IllegalArgumentException("Illegal DAY_OF_YEAR."); + + if (isSet[DAY_OF_WEEK] + && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK."); + + if (isSet[DAY_OF_WEEK_IN_MONTH]) + { + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks + || fields[DAY_OF_WEEK_IN_MONTH] > weeks) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH."); + } + + if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) + throw new IllegalArgumentException("Illegal AM_PM."); + if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) + throw new IllegalArgumentException("Illegal HOUR."); + if (isSet[HOUR_OF_DAY] + && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23)) + throw new IllegalArgumentException("Illegal HOUR_OF_DAY."); + if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) + throw new IllegalArgumentException("Illegal MINUTE."); + if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) + throw new IllegalArgumentException("Illegal SECOND."); + if (isSet[MILLISECOND] + && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) + throw new IllegalArgumentException("Illegal MILLISECOND."); + if (isSet[ZONE_OFFSET] + && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L + || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal ZONE_OFFSET."); + if (isSet[DST_OFFSET] + && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L + || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal DST_OFFSET."); + } + + /** + * Converts the time field values (<code>fields</code>) to + * milliseconds since the epoch UTC (<code>time</code>). + * + * @throws IllegalArgumentException if any calendar fields + * are invalid. + */ + protected synchronized void computeTime() + { + int millisInDay = 0; + int era = fields[ERA]; + int year = fields[YEAR]; + int month = fields[MONTH]; + int day = fields[DAY_OF_MONTH]; + + int minute = fields[MINUTE]; + int second = fields[SECOND]; + int millis = fields[MILLISECOND]; + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int hour = 0; + + if (! isLenient()) + nonLeniencyCheck(); + + if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR])) + { + // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + if (isSet[WEEK_OF_YEAR]) + { + int first = getFirstDayOfMonth(year, 0); + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + month = 0; + day = offs + 7 * (fields[WEEK_OF_YEAR] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + + if (offs < 0) + offs += 7; + day += offs; + } + else + { + // 4: YEAR + DAY_OF_YEAR + month = 0; + day = fields[DAY_OF_YEAR]; + } + } + else + { + if (isSet[DAY_OF_WEEK]) + { + int first = getFirstDayOfMonth(year, month); + + // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK + if (isSet[DAY_OF_WEEK_IN_MONTH]) + { + if (fields[DAY_OF_WEEK_IN_MONTH] < 0) + { + month++; + first = getFirstDayOfMonth(year, month); + day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]); + } + else + day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1); + + int offs = fields[DAY_OF_WEEK] - first; + if (offs < 0) + offs += 7; + day += offs; + } + else + { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + + day = offs + 7 * (fields[WEEK_OF_MONTH] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + if (offs <= 0) + offs += 7; + day += offs; + } + } + + // 1: YEAR + MONTH + DAY_OF_MONTH + } + if (era == BC && year > 0) + year = 1 - year; + + // rest of code assumes day/month/year set + // should negative BC years be AD? + // get the hour (but no check for validity) + if (isSet[HOUR]) + { + hour = fields[HOUR]; + if (fields[AM_PM] == PM) + hour += 12; + } + else + hour = fields[HOUR_OF_DAY]; + + // Read the era,year,month,day fields and convert as appropriate. + // Calculate number of milliseconds into the day + // This takes care of both h, m, s, ms over/underflows. + long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis; + day += allMillis / (24 * 60 * 60 * 1000L); + millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); + + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } + } + if (month > 11) + { + year += (month / 12); + month = month % 12; + } + + month_days[1] = isLeapYear(year) ? 29 : 28; + + while (day <= 0) + { + if (month == 0) + { + year--; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + month = (month + 11) % 12; + day += month_days[month]; + } + while (day > month_days[month]) + { + day -= (month_days[month]); + month = (month + 1) % 12; + if (month == 0) + { + year++; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + } + + // ok, by here we have valid day,month,year,era and millisinday + int dayOfYear = dayCount[month] + day - 1; // (day starts on 1) + if (isLeapYear(year) && month > 1) + dayOfYear++; + + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover) + relativeDay += gregFactor; + else + relativeDay -= 2; + + time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay; + + // the epoch was a Thursday. + int weekday = (int) (relativeDay + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + fields[DAY_OF_WEEK] = weekday; + + // Time zone corrections. + TimeZone zone = getTimeZone(); + int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET] + : zone.getRawOffset(); + + int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET] + : (zone.getOffset((year < 0) ? BC : AD, + (year < 0) ? 1 - year + : year, + month, day, weekday, + millisInDay) + - zone.getRawOffset()); + + time -= rawOffset + dstOffset; + + isTimeSet = true; + } + + /** + * Get the linear day in days since the epoch, using the + * Julian or Gregorian calendar as specified. If you specify a + * nonpositive year it is interpreted as BC as following: 0 is 1 + * BC, -1 is 2 BC and so on. + * + * @param year the year of the date. + * @param dayOfYear the day of year of the date; 1 based. + * @param gregorian <code>true</code>, if we should use the Gregorian rules. + * @return the days since the epoch, may be negative. + */ + private long getLinearDay(int year, int dayOfYear, boolean gregorian) + { + // The 13 is the number of days, that were omitted in the Gregorian + // Calender until the epoch. + // We shift right by 2 instead of dividing by 4, to get correct + // results for negative years (and this is even more efficient). + long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) + - EPOCH_DAYS; // gregorian days from 1 to epoch. + + if (gregorian) + { + // subtract the days that are missing in gregorian calendar + // with respect to julian calendar. + // + // Okay, here we rely on the fact that the gregorian + // calendar was introduced in the AD era. This doesn't work + // with negative years. + // + // The additional leap year factor accounts for the fact that + // a leap day is not seen on Jan 1 of the leap year. + int gregOffset = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return julianDay + gregOffset; + } + else + julianDay -= 2; + return julianDay; + } + + /** + * Converts the given linear day into era, year, month, + * day_of_year, day_of_month, day_of_week, and writes the result + * into the fields array. + * + * @param day the linear day. + * @param gregorian true, if we should use Gregorian rules. + */ + private void calculateDay(int[] fields, long day, boolean gregorian) + { + // the epoch was a Thursday. + int weekday = (int) (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + fields[DAY_OF_WEEK] = weekday; + + // get a first approximation of the year. This may be one + // year too big. + int year = 1970 + + (int) (gregorian + ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); + if (day >= 0) + year++; + + long firstDayOfYear = getLinearDay(year, 1, gregorian); + + // Now look in which year day really lies. + if (day < firstDayOfYear) + { + year--; + firstDayOfYear = getLinearDay(year, 1, gregorian); + } + + day -= firstDayOfYear - 1; // day of year, one based. + + fields[DAY_OF_YEAR] = (int) day; + if (year <= 0) + { + fields[ERA] = BC; + fields[YEAR] = 1 - year; + } + else + { + fields[ERA] = AD; + fields[YEAR] = year; + } + + int leapday = isLeapYear(year) ? 1 : 0; + if (day <= 31 + 28 + leapday) + { + fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY + fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH]; + } + else + { + // A few more magic formulas + int scaledDay = ((int) day - leapday) * 5 + 8; + fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); + fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; + } + } + + /** + * Converts the milliseconds since the epoch UTC + * (<code>time</code>) to time fields + * (<code>fields</code>). + */ + protected synchronized void computeFields() + { + boolean gregorian = (time >= gregorianCutover); + + TimeZone zone = getTimeZone(); + fields[ZONE_OFFSET] = zone.getRawOffset(); + long localTime = time + fields[ZONE_OFFSET]; + + long day = localTime / (24 * 60 * 60 * 1000L); + int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); + + if (millisInDay < 0) + { + millisInDay += (24 * 60 * 60 * 1000); + day--; + } + + calculateDay(fields, day, gregorian); + fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR], + fields[MONTH], fields[DAY_OF_MONTH], + fields[DAY_OF_WEEK], millisInDay) + - fields[ZONE_OFFSET]; + + millisInDay += fields[DST_OFFSET]; + if (millisInDay >= 24 * 60 * 60 * 1000) + { + millisInDay -= 24 * 60 * 60 * 1000; + calculateDay(fields, ++day, gregorian); + } + + fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7; + + // which day of the week are we (0..6), relative to getFirstDayOfWeek + int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7; + + fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 12) / 7; + + int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7; + + // Do the Correction: getMinimalDaysInFirstWeek() is always in the + // first week. + int minDays = getMinimalDaysInFirstWeek(); + int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays) + - getFirstDayOfWeek()) % 7; + if (minDays - firstWeekday < 1) + weekOfYear++; + fields[WEEK_OF_YEAR] = weekOfYear; + + int hourOfDay = millisInDay / (60 * 60 * 1000); + fields[AM_PM] = (hourOfDay < 12) ? AM : PM; + int hour = hourOfDay % 12; + fields[HOUR] = hour; + fields[HOUR_OF_DAY] = hourOfDay; + millisInDay %= (60 * 60 * 1000); + fields[MINUTE] = millisInDay / (60 * 1000); + millisInDay %= (60 * 1000); + fields[SECOND] = millisInDay / (1000); + fields[MILLISECOND] = millisInDay % 1000; + + areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true; + } + + /** + * Compares the given calendar with this. An object, o, is + * equivalent to this if it is also a <code>GregorianCalendar</code> + * with the same time since the epoch under the same conditions + * (same change date and same time zone). + * + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, that represents + * the same time (but doesn't necessarily have the same fields). + * @throws IllegalArgumentException if one of the fields + * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public boolean equals(Object o) + { + if (! (o instanceof GregorianCalendar)) + return false; + + GregorianCalendar cal = (GregorianCalendar) o; + return (cal.getTimeInMillis() == getTimeInMillis()); + } + + /** + * Adds the specified amount of time to the given time field. The + * amount may be negative to subtract the time. If the field overflows + * it does what you expect: Jan, 25 + 10 Days is Feb, 4. + * @param field one of the time field constants. + * @param amount the amount of time to add. + * @exception IllegalArgumentException if <code>field</code> is + * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or + * if <code>amount</code> contains an out-of-range value and the calendar + * is not in lenient mode. + */ + public void add(int field, int amount) + { + switch (field) + { + case YEAR: + complete(); + fields[YEAR] += amount; + isTimeSet = false; + break; + case MONTH: + complete(); + int months = fields[MONTH] + amount; + fields[YEAR] += months / 12; + fields[MONTH] = months % 12; + if (fields[MONTH] < 0) + { + fields[MONTH] += 12; + fields[YEAR]--; + } + int maxDay = getActualMaximum(DAY_OF_MONTH); + if (fields[DAY_OF_MONTH] > maxDay) + fields[DAY_OF_MONTH] = maxDay; + set(YEAR, fields[YEAR]); + set(MONTH, fields[MONTH]); + break; + case DAY_OF_MONTH: + case DAY_OF_YEAR: + case DAY_OF_WEEK: + if (! isTimeSet) + computeTime(); + time += amount * (24 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case WEEK_OF_YEAR: + case WEEK_OF_MONTH: + case DAY_OF_WEEK_IN_MONTH: + if (! isTimeSet) + computeTime(); + time += amount * (7 * 24 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case AM_PM: + if (! isTimeSet) + computeTime(); + time += amount * (12 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case HOUR: + case HOUR_OF_DAY: + if (! isTimeSet) + computeTime(); + time += amount * (60 * 60 * 1000L); + areFieldsSet = false; + break; + case MINUTE: + if (! isTimeSet) + computeTime(); + time += amount * (60 * 1000L); + areFieldsSet = false; + break; + case SECOND: + if (! isTimeSet) + computeTime(); + time += amount * (1000L); + areFieldsSet = false; + break; + case MILLISECOND: + if (! isTimeSet) + computeTime(); + time += amount; + areFieldsSet = false; + break; + case ZONE_OFFSET: + case DST_OFFSET:default: + throw new IllegalArgumentException("Invalid or unknown field"); + } + } + + /** + * Rolls the specified time field up or down. This means add one + * to the specified field, but don't change the other fields. If + * the maximum for this field is reached, start over with the + * minimum value. + * + * <strong>Note:</strong> There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. + * This requires lenient settings. + * + * @param field the time field. One of the time field constants. + * @param up the direction, true for up, false for down. + * @throws IllegalArgumentException if one of the fields + * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public void roll(int field, boolean up) + { + roll(field, up ? 1 : -1); + } + + /** + * Checks that the fields are still within their legal bounds, + * following use of the <code>roll()</code> method. + * + * @param field the field to check. + * @param delta multipler for alterations to the <code>time</code>. + * @see #roll(int, boolean) + * @see #roll(int, int) + */ + private void cleanUpAfterRoll(int field, int delta) + { + switch (field) + { + case ERA: + case YEAR: + case MONTH: + // check that day of month is still in correct range + if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH)) + fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH); + isTimeSet = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_MONTH: + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (24 * 60 * 60 * 1000L); + break; + case WEEK_OF_MONTH: + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case DAY_OF_WEEK_IN_MONTH: + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case DAY_OF_YEAR: + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (24 * 60 * 60 * 1000L); + break; + case WEEK_OF_YEAR: + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case AM_PM: + isSet[HOUR_OF_DAY] = false; + time += delta * (12 * 60 * 60 * 1000L); + break; + case HOUR: + isSet[HOUR_OF_DAY] = false; + time += delta * (60 * 60 * 1000L); + break; + case HOUR_OF_DAY: + isSet[HOUR] = false; + isSet[AM_PM] = false; + time += delta * (60 * 60 * 1000L); + break; + case MINUTE: + time += delta * (60 * 1000L); + break; + case SECOND: + time += delta * (1000L); + break; + case MILLISECOND: + time += delta; + break; + } + } + + /** + * Rolls the specified time field by the given amount. This means + * add amount to the specified field, but don't change the other + * fields. If the maximum for this field is reached, start over + * with the minimum value and vice versa for negative amounts. + * + * <strong>Note:</strong> There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically corrected to June, 30. + * + * @param field the time field. One of the time field constants. + * @param amount the amount by which we should roll. + * @throws IllegalArgumentException if one of the fields + * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public void roll(int field, int amount) + { + switch (field) + { + case DAY_OF_WEEK: + // day of week is special: it rolls automatically + add(field, amount); + return; + case ZONE_OFFSET: + case DST_OFFSET: + throw new IllegalArgumentException("Can't roll time zone"); + } + complete(); + int min = getActualMinimum(field); + int range = getActualMaximum(field) - min + 1; + int oldval = fields[field]; + int newval = (oldval - min + range + amount) % range + min; + if (newval < min) + newval += range; + fields[field] = newval; + cleanUpAfterRoll(field, newval - oldval); + } + + /** + * The minimum values for the calendar fields. + */ + private static final int[] minimums = + { + BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM, + 1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000), + 0 + }; + + /** + * The maximum values for the calendar fields. + */ + private static final int[] maximums = + { + AD, 5000000, 11, 53, 5, 31, 366, + SATURDAY, 5, PM, 12, 23, 59, 59, 999, + +(12 * 60 * 60 * 1000), + (12 * 60 * 60 * 1000) + }; + + /** + * Gets the smallest value that is allowed for the specified field. + * + * @param field one of the time field constants. + * @return the smallest value for the specified field. + */ + public int getMinimum(int field) + { + return minimums[field]; + } + + /** + * Gets the biggest value that is allowed for the specified field. + * + * @param field one of the time field constants. + * @return the biggest value. + */ + public int getMaximum(int field) + { + return maximums[field]; + } + + /** + * Gets the greatest minimum value that is allowed for the specified field. + * This is the largest value returned by the <code>getActualMinimum(int)</code> + * method. + * + * @param field the time field. One of the time field constants. + * @return the greatest minimum value. + * @see #getActualMinimum(int) + */ + public int getGreatestMinimum(int field) + { + if (field == WEEK_OF_YEAR) + return 1; + return minimums[field]; + } + + /** + * Gets the smallest maximum value that is allowed for the + * specified field. This is the smallest value returned + * by the <code>getActualMaximum(int)</code>. For example, + * this is 28 for DAY_OF_MONTH (as all months have at least + * 28 days). + * + * @param field the time field. One of the time field constants. + * @return the least maximum value. + * @see #getActualMaximum(int) + * @since 1.2 + */ + public int getLeastMaximum(int field) + { + switch (field) + { + case WEEK_OF_YEAR: + return 52; + case DAY_OF_MONTH: + return 28; + case DAY_OF_YEAR: + return 365; + case DAY_OF_WEEK_IN_MONTH: + case WEEK_OF_MONTH: + return 4; + default: + return maximums[field]; + } + } + + /** + * Gets the actual minimum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. Note that + * this calls <code>complete()</code> if not enough fields are set. This + * can have ugly side effects. The value given depends on the current + * time used by this instance. + * + * @param field the time field. One of the time field constants. + * @return the actual minimum value. + * @since 1.2 + */ + public int getActualMinimum(int field) + { + if (field == WEEK_OF_YEAR) + { + int min = getMinimalDaysInFirstWeek(); + if (min == 0) + return 1; + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + int weekday = getWeekDay(year, min); + if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1) + return 1; + return 0; + } + return minimums[field]; + } + + /** + * Gets the actual maximum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. Note that + * this calls <code>complete()</code> if not enough fields are set. This + * can have ugly side effects. The value given depends on the current time + * used by this instance; thus, leap years have a maximum day of month value of + * 29, rather than 28. + * + * @param field the time field. One of the time field constants. + * @return the actual maximum value. + */ + public int getActualMaximum(int field) + { + switch (field) + { + case WEEK_OF_YEAR: + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + + // This is wrong for the year that contains the gregorian change. + // I.e it gives the weeks in the julian year or in the gregorian + // year in that case. + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + int lastDay = isLeapYear(year) ? 366 : 365; + int weekday = getWeekDay(year, lastDay); + int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + + int minimalDays = getMinimalDaysInFirstWeek(); + int firstWeekday = getWeekDay(year, minimalDays); + /* + * Is there a set of days at the beginning of the year, before the + * first day of the week, equal to or greater than the minimum number + * of days required in the first week? + */ + if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) + return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */ + } + case DAY_OF_MONTH: + { + if (! areFieldsSet || ! isSet[MONTH]) + complete(); + int month = fields[MONTH]; + + // If you change this, you should also change + // SimpleTimeZone.getDaysInMonth(); + if (month == FEBRUARY) + { + if (! isSet[YEAR] || ! isSet[ERA]) + complete(); + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + return isLeapYear(year) ? 29 : 28; + } + else if (month < AUGUST) + return 31 - (month & 1); + else + return 30 + (month & 1); + } + case DAY_OF_YEAR: + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + return isLeapYear(year) ? 366 : 365; + } + case DAY_OF_WEEK_IN_MONTH: + { + // This is wrong for the month that contains the gregorian change. + int daysInMonth = getActualMaximum(DAY_OF_MONTH); + + // That's black magic, I know + return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; + } + case WEEK_OF_MONTH: + { + int daysInMonth = getActualMaximum(DAY_OF_MONTH); + int weekday = (daysInMonth - fields[DAY_OF_MONTH] + + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; + return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + } + default: + return maximums[field]; + } + } +} diff --git a/libjava/classpath/java/util/HashMap.java b/libjava/classpath/java/util/HashMap.java new file mode 100644 index 00000000000..5ca9cf6d500 --- /dev/null +++ b/libjava/classpath/java/util/HashMap.java @@ -0,0 +1,906 @@ +/* HashMap.java -- a class providing a basic hashtable data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +// NOTE: This implementation is very similar to that of Hashtable. If you fix +// a bug in here, chances are you should make a similar change to the Hashtable +// code. + +// NOTE: This implementation has some nasty coding style in order to +// support LinkedHashMap, which extends this. + +/** + * This class provides a hashtable-backed implementation of the + * Map interface. + * <p> + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. + * <p> + * + * Under ideal circumstances (no collisions), HashMap offers O(1) + * performance on most operations (<code>containsValue()</code> is, + * of course, O(n)). In the worst case (all keys map to the same + * hash code -- very unlikely), most operations are O(n). + * <p> + * + * HashMap is part of the JDK1.2 Collections API. It differs from + * Hashtable in that it accepts the null key and null values, and it + * does not support "Enumeration views." Also, it is not synchronized; + * if you plan to use it in multiple threads, consider using:<br> + * <code>Map m = Collections.synchronizedMap(new HashMap(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * <code>ConcurrentModificationException</code> rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Jochen Hoenicke + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Object#hashCode() + * @see Collection + * @see Map + * @see TreeMap + * @see LinkedHashMap + * @see IdentityHashMap + * @see Hashtable + * @since 1.2 + * @status updated to 1.4 + */ +public class HashMap extends AbstractMap + implements Map, Cloneable, Serializable +{ + /** + * Default number of buckets. This is the value the JDK 1.3 uses. Some + * early documentation specified this value as 101. That is incorrect. + * Package visible for use by HashSet. + */ + static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor; this is explicitly specified by the spec. + * Package visible for use by HashSet. + */ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 362498820763181265L; + + /** + * The rounded product of the capacity and the load factor; when the number + * of elements exceeds the threshold, the HashMap calls + * <code>rehash()</code>. + * @serial the threshold for rehashing + */ + private int threshold; + + /** + * Load factor of this HashMap: used in computing the threshold. + * Package visible for use by HashSet. + * @serial the load factor + */ + final float loadFactor; + + /** + * Array containing the actual key-value mappings. + * Package visible for use by nested and subclasses. + */ + transient HashEntry[] buckets; + + /** + * Counts the number of modifications this HashMap has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + * Package visible for use by nested and subclasses. + */ + transient int modCount; + + /** + * The size of this HashMap: denotes the number of key-value pairs. + * Package visible for use by nested and subclasses. + */ + transient int size; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set entries; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair. Package visible for use by subclass. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class HashEntry extends AbstractMap.BasicMapEntry + { + /** + * The next entry in the linked list. Package visible for use by subclass. + */ + HashEntry next; + + /** + * Simple constructor. + * @param key the key + * @param value the value + */ + HashEntry(Object key, Object value) + { + super(key, value); + } + + /** + * Called when this entry is accessed via {@link #put(Object, Object)}. + * This version does nothing, but in LinkedHashMap, it must do some + * bookkeeping for access-traversal mode. + */ + void access() + { + } + + /** + * Called when this entry is removed from the map. This version simply + * returns the value, but in LinkedHashMap, it must also do bookkeeping. + * + * @return the value of this key as it is removed + */ + Object cleanup() + { + return value; + } + } + + /** + * Construct a new HashMap with the default capacity (11) and the default + * load factor (0.75). + */ + public HashMap() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new HashMap from the given Map, with initial capacity + * the greater of the size of <code>m</code> or the default of 11. + * <p> + * + * Every element in Map m will be put into this new HashMap. + * + * @param m a Map whose key / value pairs will be put into the new HashMap. + * <b>NOTE: key / value pairs are not cloned in this constructor.</b> + * @throws NullPointerException if m is null + */ + public HashMap(Map m) + { + this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Construct a new HashMap with a specific inital capacity and + * default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this HashMap (>=0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public HashMap(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new HashMap with a specific inital capacity and load factor. + * + * @param initialCapacity the initial capacity (>=0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public HashMap(int initialCapacity, float loadFactor) + { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: " + + initialCapacity); + if (! (loadFactor > 0)) // check for NaN too + throw new IllegalArgumentException("Illegal Load: " + loadFactor); + + if (initialCapacity == 0) + initialCapacity = 1; + buckets = new HashEntry[initialCapacity]; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + } + + /** + * Returns the number of kay-value mappings currently in this Map. + * + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns true if there are no key-value mappings currently in this Map. + * + * @return <code>size() == 0</code> + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Return the value in this HashMap associated with the supplied key, + * or <code>null</code> if the key maps to nothing. NOTE: Since the value + * could also be null, you must use containsKey to see if this key + * actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public Object get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + return e.value; + e = e.next; + } + return null; + } + + /** + * Returns true if the supplied object <code>equals()</code> a key + * in this HashMap. + * + * @param key the key to search for in this HashMap + * @return true if the key is in the table + * @see #containsValue(Object) + */ + public boolean containsKey(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + return true; + e = e.next; + } + return false; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which <code>equals()</code> + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. + * + * @param key the key used to locate the value + * @param value the value to be stored in the HashMap + * @return the prior mapping of the key, or null if there was none + * @see #get(Object) + * @see Object#equals(Object) + */ + public Object put(Object key, Object value) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + + while (e != null) + { + if (equals(key, e.key)) + { + e.access(); // Must call this for bookkeeping in LinkedHashMap. + Object r = e.value; + e.value = value; + return r; + } + else + e = e.next; + } + + // At this point, we know we need to add a new entry. + modCount++; + if (++size > threshold) + { + rehash(); + // Need a new hash value to suit the bigger table. + idx = hash(key); + } + + // LinkedHashMap cannot override put(), hence this call. + addEntry(key, value, idx, true); + return null; + } + + /** + * Copies all elements of the given map into this hashtable. If this table + * already has a mapping for a key, the new mapping replaces the current + * one. + * + * @param m the map to be hashed into this + */ + public void putAll(Map m) + { + Iterator itr = m.entrySet().iterator(); + while (itr.hasNext()) + { + Map.Entry e = (Map.Entry) itr.next(); + // Optimize in case the Entry is one of our own. + if (e instanceof AbstractMap.BasicMapEntry) + { + AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e; + put(entry.key, entry.value); + } + else + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes from the HashMap and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the HashMap remains + * unchanged, and <code>null</code> is returned. NOTE: Since the value + * could also be null, you must use containsKey to see if you are + * actually removing a mapping. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public Object remove(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + HashEntry last = null; + + while (e != null) + { + if (equals(key, e.key)) + { + modCount++; + if (last == null) + buckets[idx] = e.next; + else + last.next = e.next; + size--; + // Method call necessary for LinkedHashMap to work correctly. + return e.cleanup(); + } + last = e; + e = e.next; + } + return null; + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + if (size != 0) + { + modCount++; + Arrays.fill(buckets, null); + size = 0; + } + } + + /** + * Returns true if this HashMap contains a value <code>o</code>, such that + * <code>o.equals(value)</code>. + * + * @param value the value to search for in this HashMap + * @return true if at least one key maps to the value + * @see containsKey(Object) + */ + public boolean containsValue(Object value) + { + for (int i = buckets.length - 1; i >= 0; i--) + { + HashEntry e = buckets[i]; + while (e != null) + { + if (equals(value, e.value)) + return true; + e = e.next; + } + } + return false; + } + + /** + * Returns a shallow clone of this HashMap. The Map itself is cloned, + * but its contents are not. This is O(n). + * + * @return the clone + */ + public Object clone() + { + HashMap copy = null; + try + { + copy = (HashMap) super.clone(); + } + catch (CloneNotSupportedException x) + { + // This is impossible. + } + copy.buckets = new HashEntry[buckets.length]; + copy.putAllInternal(this); + // Clear the entry cache. AbstractMap.clone() does the others. + copy.entries = null; + return copy; + } + + /** + * Returns a "set view" of this HashMap's keys. The set is backed by the + * HashMap, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + keys = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(KEYS); + } + + public void clear() + { + HashMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public boolean remove(Object o) + { + // Test against the size of the HashMap to determine if anything + // really got removed. This is necessary because the return value + // of HashMap.remove() is ambiguous in the null case. + int oldsize = size; + HashMap.this.remove(o); + return oldsize != size; + } + }; + return keys; + } + + /** + * Returns a "collection view" (or "bag view") of this HashMap's values. + * The collection is backed by the HashMap, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(VALUES); + } + + public void clear() + { + HashMap.this.clear(); + } + }; + return values; + } + + /** + * Returns a "set view" of this HashMap's entries. The set is backed by + * the HashMap, so changes in one show up in the other. The set supports + * element removal, but not element addition.<p> + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the HashMap in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + entries = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(ENTRIES); + } + + public void clear() + { + HashMap.this.clear(); + } + + public boolean contains(Object o) + { + return getEntry(o) != null; + } + + public boolean remove(Object o) + { + HashEntry e = getEntry(o); + if (e != null) + { + HashMap.this.remove(e.key); + return true; + } + return false; + } + }; + return entries; + } + + /** + * Helper method for put, that creates and adds a new Entry. This is + * overridden in LinkedHashMap for bookkeeping purposes. + * + * @param key the key of the new Entry + * @param value the value + * @param idx the index in buckets where the new Entry belongs + * @param callRemove whether to call the removeEldestEntry method + * @see #put(Object, Object) + */ + void addEntry(Object key, Object value, int idx, boolean callRemove) + { + HashEntry e = new HashEntry(key, value); + e.next = buckets[idx]; + buckets[idx] = e; + } + + /** + * Helper method for entrySet(), which matches both key and value + * simultaneously. + * + * @param o the entry to match + * @return the matching entry, if found, or null + * @see #entrySet() + */ + // Package visible, for use in nested classes. + final HashEntry getEntry(Object o) + { + if (! (o instanceof Map.Entry)) + return null; + Map.Entry me = (Map.Entry) o; + Object key = me.getKey(); + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(e.key, key)) + return equals(e.value, me.getValue()) ? e : null; + e = e.next; + } + return null; + } + + /** + * Helper method that returns an index in the buckets array for `key' + * based on its hashCode(). Package visible for use by subclasses. + * + * @param key the key + * @return the bucket number + */ + final int hash(Object key) + { + return key == null ? 0 : Math.abs(key.hashCode() % buckets.length); + } + + /** + * Generates a parameterized iterator. Must be overrideable, since + * LinkedHashMap iterates in a different order. + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @return the appropriate iterator + */ + Iterator iterator(int type) + { + return new HashIterator(type); + } + + /** + * A simplified, more efficient internal implementation of putAll(). clone() + * should not call putAll or put, in order to be compatible with the JDK + * implementation with respect to subclasses. + * + * @param m the map to initialize this from + */ + void putAllInternal(Map m) + { + Iterator itr = m.entrySet().iterator(); + size = 0; + while (itr.hasNext()) + { + size++; + Map.Entry e = (Map.Entry) itr.next(); + Object key = e.getKey(); + int idx = hash(key); + addEntry(key, e.getValue(), idx, false); + } + } + + /** + * Increases the size of the HashMap and rehashes all keys to new + * array indices; this is called when the addition of a new value + * would cause size() > threshold. Note that the existing Entry + * objects are reused in the new hash table. + * + * <p>This is not specified, but the new size is twice the current size + * plus one; this number is not always prime, unfortunately. + */ + private void rehash() + { + HashEntry[] oldBuckets = buckets; + + int newcapacity = (buckets.length * 2) + 1; + threshold = (int) (newcapacity * loadFactor); + buckets = new HashEntry[newcapacity]; + + for (int i = oldBuckets.length - 1; i >= 0; i--) + { + HashEntry e = oldBuckets[i]; + while (e != null) + { + int idx = hash(e.key); + HashEntry dest = buckets[idx]; + HashEntry next = e.next; + e.next = buckets[idx]; + buckets[idx] = e; + e = next; + } + } + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i>(int) that is the length of the + * bucket array, the <i>size</i>(int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + // Write the threshold and loadFactor fields. + s.defaultWriteObject(); + + s.writeInt(buckets.length); + s.writeInt(size); + // Avoid creating a wasted Set by creating the iterator directly. + Iterator it = iterator(ENTRIES); + while (it.hasNext()) + { + HashEntry entry = (HashEntry) it.next(); + s.writeObject(entry.key); + s.writeObject(entry.value); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i>(int) that is the length of the + * bucket array, the <i>size</i>(int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // Read the threshold and loadFactor fields. + s.defaultReadObject(); + + // Read and use capacity, followed by key/value pairs. + buckets = new HashEntry[s.readInt()]; + int len = s.readInt(); + size = len; + while (len-- > 0) + { + Object key = s.readObject(); + addEntry(key, s.readObject(), hash(key), false); + } + } + + /** + * Iterate over HashMap's entries. + * This implementation is parameterized to give a sequential view of + * keys, values, or entries. + * + * @author Jon Zeppieri + */ + private final class HashIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + private final int type; + /** + * The number of modifications to the backing HashMap that we know about. + */ + private int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + private int count = size; + /** Current index in the physical hash table. */ + private int idx = buckets.length; + /** The last Entry returned by a next() call. */ + private HashEntry last; + /** + * The next entry that should be returned by next(). It is set to something + * if we're iterating through a bucket that contains multiple linked + * entries. It is null if next() needs to find a new bucket. + */ + private HashEntry next; + + /** + * Construct a new HashIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + HashIterator(int type) + { + this.type = type; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + * @throws ConcurrentModificationException if the HashMap was modified + */ + public boolean hasNext() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the HashMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + HashEntry e = next; + + while (e == null) + e = buckets[--idx]; + + next = e.next; + last = e; + if (type == VALUES) + return e.value; + if (type == KEYS) + return e.key; + return e; + } + + /** + * Removes from the backing HashMap the last element which was fetched + * with the <code>next()</code> method. + * @throws ConcurrentModificationException if the HashMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + + HashMap.this.remove(last.key); + last = null; + knownMod++; + } + } +} diff --git a/libjava/classpath/java/util/HashSet.java b/libjava/classpath/java/util/HashSet.java new file mode 100644 index 00000000000..681d5bb1b07 --- /dev/null +++ b/libjava/classpath/java/util/HashSet.java @@ -0,0 +1,293 @@ +/* HashSet.java -- a class providing a HashMap-backed Set + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a HashMap-backed implementation of the Set interface. + * <p> + * + * Most operations are O(1), assuming no hash collisions. In the worst + * case (where all hashes collide), operations are O(n). Setting the + * initial capacity too low will force many resizing operations, but + * setting the initial capacity too high (or loadfactor too low) leads + * to wasted memory and slower iteration. + * <p> + * + * HashSet accepts the null key and null values. It is not synchronized, + * so if you need multi-threaded access, consider using:<br> + * <code>Set s = Collections.synchronizedSet(new HashSet(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see TreeSet + * @see Collections#synchronizedSet(Set) + * @see HashMap + * @see LinkedHashSet + * @since 1.2 + * @status updated to 1.4 + */ +public class HashSet extends AbstractSet + implements Set, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -5024744406713321676L; + + /** + * The HashMap which backs this Set. + */ + private transient HashMap map; + + /** + * Construct a new, empty HashSet whose backing HashMap has the default + * capacity (11) and loadFacor (0.75). + */ + public HashSet() + { + this(HashMap.DEFAULT_CAPACITY, HashMap.DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity of the backing HashMap + * @throws IllegalArgumentException if the capacity is negative + */ + public HashSet(int initialCapacity) + { + this(initialCapacity, HashMap.DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and load factor. + * + * @param initialCapacity the initial capacity of the backing HashMap + * @param loadFactor the load factor of the backing HashMap + * @throws IllegalArgumentException if either argument is negative, or + * if loadFactor is POSITIVE_INFINITY or NaN + */ + public HashSet(int initialCapacity, float loadFactor) + { + map = init(initialCapacity, loadFactor); + } + + /** + * Construct a new HashSet with the same elements as are in the supplied + * collection (eliminating any duplicates, of course). The backing storage + * has twice the size of the collection, or the default size of 11, + * whichever is greater; and the default load factor (0.75). + * + * @param c a collection of initial set elements + * @throws NullPointerException if c is null + */ + public HashSet(Collection c) + { + this(Math.max(2 * c.size(), HashMap.DEFAULT_CAPACITY)); + addAll(c); + } + + /** + * Adds the given Object to the set if it is not already in the Set. + * This set permits a null element. + * + * @param o the Object to add to this Set + * @return true if the set did not already contain o + */ + public boolean add(Object o) + { + return map.put(o, "") == null; + } + + /** + * Empties this Set of all elements; this takes constant time. + */ + public void clear() + { + map.clear(); + } + + /** + * Returns a shallow copy of this Set. The Set itself is cloned; its + * elements are not. + * + * @return a shallow clone of the set + */ + public Object clone() + { + HashSet copy = null; + try + { + copy = (HashSet) super.clone(); + } + catch (CloneNotSupportedException x) + { + // Impossible to get here. + } + copy.map = (HashMap) map.clone(); + return copy; + } + + /** + * Returns true if the supplied element is in this Set. + * + * @param o the Object to look for + * @return true if it is in the set + */ + public boolean contains(Object o) + { + return map.containsKey(o); + } + + /** + * Returns true if this set has no elements in it. + * + * @return <code>size() == 0</code>. + */ + public boolean isEmpty() + { + return map.size == 0; + } + + /** + * Returns an Iterator over the elements of this Set, which visits the + * elements in no particular order. For this class, the Iterator allows + * removal of elements. The iterator is fail-fast, and will throw a + * ConcurrentModificationException if the set is modified externally. + * + * @return a set iterator + * @see ConcurrentModificationException + */ + public Iterator iterator() + { + // Avoid creating intermediate keySet() object by using non-public API. + return map.iterator(HashMap.KEYS); + } + + /** + * Removes the supplied Object from this Set if it is in the Set. + * + * @param o the object to remove + * @return true if an element was removed + */ + public boolean remove(Object o) + { + return (map.remove(o) != null); + } + + /** + * Returns the number of elements in this Set (its cardinality). + * + * @return the size of the set + */ + public int size() + { + return map.size; + } + + /** + * Helper method which initializes the backing Map. Overridden by + * LinkedHashSet for correct semantics. + * + * @param capacity the initial capacity + * @param load the initial load factor + * @return the backing HashMap + */ + HashMap init(int capacity, float load) + { + return new HashMap(capacity, load); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float) + * of the backing store, followed by the set size (int), + * then a listing of its elements (Object) in no order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + // Avoid creating intermediate keySet() object by using non-public API. + Iterator it = map.iterator(HashMap.KEYS); + s.writeInt(map.buckets.length); + s.writeFloat(map.loadFactor); + s.writeInt(map.size); + while (it.hasNext()) + s.writeObject(it.next()); + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float) + * of the backing store, followed by the set size (int), + * then a listing of its elements (Object) in no order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + map = init(s.readInt(), s.readFloat()); + for (int size = s.readInt(); size > 0; size--) + map.put(s.readObject(), ""); + } +} diff --git a/libjava/classpath/java/util/Hashtable.java b/libjava/classpath/java/util/Hashtable.java new file mode 100644 index 00000000000..011cafaa855 --- /dev/null +++ b/libjava/classpath/java/util/Hashtable.java @@ -0,0 +1,1151 @@ +/* Hashtable.java -- a class providing a basic hashtable data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +// NOTE: This implementation is very similar to that of HashMap. If you fix +// a bug in here, chances are you should make a similar change to the HashMap +// code. + +/** + * A class which implements a hashtable data structure. + * <p> + * + * This implementation of Hashtable uses a hash-bucket approach. That is: + * linear probing and rehashing is avoided; instead, each hashed value maps + * to a simple linked-list which, in the best case, only has one node. + * Assuming a large enough table, low enough load factor, and / or well + * implemented hashCode() methods, Hashtable should provide O(1) + * insertion, deletion, and searching of keys. Hashtable is O(n) in + * the worst case for all of these (if all keys hash to the same bucket). + * <p> + * + * This is a JDK-1.2 compliant implementation of Hashtable. As such, it + * belongs, partially, to the Collections framework (in that it implements + * Map). For backwards compatibility, it inherits from the obsolete and + * utterly useless Dictionary class. + * <p> + * + * Being a hybrid of old and new, Hashtable has methods which provide redundant + * capability, but with subtle and even crucial differences. + * For example, one can iterate over various aspects of a Hashtable with + * either an Iterator (which is the JDK-1.2 way of doing things) or with an + * Enumeration. The latter can end up in an undefined state if the Hashtable + * changes while the Enumeration is open. + * <p> + * + * Unlike HashMap, Hashtable does not accept `null' as a key value. Also, + * all accesses are synchronized: in a single thread environment, this is + * expensive, but in a multi-thread environment, this saves you the effort + * of extra synchronization. However, the old-style enumerators are not + * synchronized, because they can lead to unspecified behavior even if + * they were synchronized. You have been warned. + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * <code>ConcurrentModificationException</code> rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Warren Levy + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see HashMap + * @see TreeMap + * @see IdentityHashMap + * @see LinkedHashMap + * @since 1.0 + * @status updated to 1.4 + */ +public class Hashtable extends Dictionary + implements Map, Cloneable, Serializable +{ + // WARNING: Hashtable is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** Default number of buckets. This is the value the JDK 1.3 uses. Some + * early documentation specified this value as 101. That is incorrect. + */ + private static final int DEFAULT_CAPACITY = 11; + + /** An "enum" of iterator types. */ + // Package visible for use by nested classes. + static final int KEYS = 0, + VALUES = 1, + ENTRIES = 2; + + /** + * The default load factor; this is explicitly specified by the spec. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1421746759512286392L; + + /** + * The rounded product of the capacity and the load factor; when the number + * of elements exceeds the threshold, the Hashtable calls + * <code>rehash()</code>. + * @serial + */ + private int threshold; + + /** + * Load factor of this Hashtable: used in computing the threshold. + * @serial + */ + private final float loadFactor; + + /** + * Array containing the actual key-value mappings. + */ + // Package visible for use by nested classes. + transient HashEntry[] buckets; + + /** + * Counts the number of modifications this Hashtable has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + */ + // Package visible for use by nested classes. + transient int modCount; + + /** + * The size of this Hashtable: denotes the number of key-value pairs. + */ + // Package visible for use by nested classes. + transient int size; + + /** + * The cache for {@link #keySet()}. + */ + private transient Set keys; + + /** + * The cache for {@link #values()}. + */ + private transient Collection values; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set entries; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair. A Hashtable Entry is identical to a HashMap Entry, except that + * `null' is not allowed for keys and values. + */ + private static final class HashEntry extends AbstractMap.BasicMapEntry + { + /** The next entry in the linked list. */ + HashEntry next; + + /** + * Simple constructor. + * @param key the key, already guaranteed non-null + * @param value the value, already guaranteed non-null + */ + HashEntry(Object key, Object value) + { + super(key, value); + } + + /** + * Resets the value. + * @param newVal the new value + * @return the prior value + * @throws NullPointerException if <code>newVal</code> is null + */ + public Object setValue(Object newVal) + { + if (newVal == null) + throw new NullPointerException(); + return super.setValue(newVal); + } + } + + /** + * Construct a new Hashtable with the default capacity (11) and the default + * load factor (0.75). + */ + public Hashtable() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new Hashtable from the given Map, with initial capacity + * the greater of the size of <code>m</code> or the default of 11. + * <p> + * + * Every element in Map m will be put into this new Hashtable. + * + * @param m a Map whose key / value pairs will be put into + * the new Hashtable. <b>NOTE: key / value pairs + * are not cloned in this constructor.</b> + * @throws NullPointerException if m is null, or if m contains a mapping + * to or from `null'. + * @since 1.2 + */ + public Hashtable(Map m) + { + this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Construct a new Hashtable with a specific inital capacity and + * default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this Hashtable (>= 0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public Hashtable(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new Hashtable with a specific initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity (>= 0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public Hashtable(int initialCapacity, float loadFactor) + { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: " + + initialCapacity); + if (! (loadFactor > 0)) // check for NaN too + throw new IllegalArgumentException("Illegal Load: " + loadFactor); + + if (initialCapacity == 0) + initialCapacity = 1; + buckets = new HashEntry[initialCapacity]; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + } + + /** + * Returns the number of key-value mappings currently in this hashtable. + * @return the size + */ + public synchronized int size() + { + return size; + } + + /** + * Returns true if there are no key-value mappings currently in this table. + * @return <code>size() == 0</code> + */ + public synchronized boolean isEmpty() + { + return size == 0; + } + + /** + * Return an enumeration of the keys of this table. There's no point + * in synchronizing this, as you have already been warned that the + * enumeration is not specified to be thread-safe. + * + * @return the keys + * @see #elements() + * @see #keySet() + */ + public Enumeration keys() + { + return new Enumerator(KEYS); + } + + /** + * Return an enumeration of the values of this table. There's no point + * in synchronizing this, as you have already been warned that the + * enumeration is not specified to be thread-safe. + * + * @return the values + * @see #keys() + * @see #values() + */ + public Enumeration elements() + { + return new Enumerator(VALUES); + } + + /** + * Returns true if this Hashtable contains a value <code>o</code>, + * such that <code>o.equals(value)</code>. This is the same as + * <code>containsValue()</code>, and is O(n). + * <p> + * + * @param value the value to search for in this Hashtable + * @return true if at least one key maps to the value + * @throws NullPointerException if <code>value</code> is null + * @see #containsValue(Object) + * @see #containsKey(Object) + */ + public synchronized boolean contains(Object value) + { + for (int i = buckets.length - 1; i >= 0; i--) + { + HashEntry e = buckets[i]; + while (e != null) + { + if (value.equals(e.value)) + return true; + e = e.next; + } + } + + // Must throw on null argument even if the table is empty + if (value == null) + throw new NullPointerException(); + + return false; + } + + /** + * Returns true if this Hashtable contains a value <code>o</code>, such that + * <code>o.equals(value)</code>. This is the new API for the old + * <code>contains()</code>. + * + * @param value the value to search for in this Hashtable + * @return true if at least one key maps to the value + * @see #contains(Object) + * @see #containsKey(Object) + * @throws NullPointerException if <code>value</code> is null + * @since 1.2 + */ + public boolean containsValue(Object value) + { + // Delegate to older method to make sure code overriding it continues + // to work. + return contains(value); + } + + /** + * Returns true if the supplied object <code>equals()</code> a key + * in this Hashtable. + * + * @param key the key to search for in this Hashtable + * @return true if the key is in the table + * @throws NullPointerException if key is null + * @see #containsValue(Object) + */ + public synchronized boolean containsKey(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (key.equals(e.key)) + return true; + e = e.next; + } + return false; + } + + /** + * Return the value in this Hashtable associated with the supplied key, + * or <code>null</code> if the key maps to nothing. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @throws NullPointerException if key is null + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public synchronized Object get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (key.equals(e.key)) + return e.value; + e = e.next; + } + return null; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * Neither parameter may be null. The value may be retrieved by any + * object which <code>equals()</code> this key. + * + * @param key the key used to locate the value + * @param value the value to be stored in the table + * @return the prior mapping of the key, or null if there was none + * @throws NullPointerException if key or value is null + * @see #get(Object) + * @see Object#equals(Object) + */ + public synchronized Object put(Object key, Object value) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + + // Check if value is null since it is not permitted. + if (value == null) + throw new NullPointerException(); + + while (e != null) + { + if (key.equals(e.key)) + { + // Bypass e.setValue, since we already know value is non-null. + Object r = e.value; + e.value = value; + return r; + } + else + { + e = e.next; + } + } + + // At this point, we know we need to add a new entry. + modCount++; + if (++size > threshold) + { + rehash(); + // Need a new hash value to suit the bigger table. + idx = hash(key); + } + + e = new HashEntry(key, value); + + e.next = buckets[idx]; + buckets[idx] = e; + + return null; + } + + /** + * Removes from the table and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the table remains + * unchanged, and <code>null</code> is returned. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public synchronized Object remove(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + HashEntry last = null; + + while (e != null) + { + if (key.equals(e.key)) + { + modCount++; + if (last == null) + buckets[idx] = e.next; + else + last.next = e.next; + size--; + return e.value; + } + last = e; + e = e.next; + } + return null; + } + + /** + * Copies all elements of the given map into this hashtable. However, no + * mapping can contain null as key or value. If this table already has + * a mapping for a key, the new mapping replaces the current one. + * + * @param m the map to be hashed into this + * @throws NullPointerException if m is null, or contains null keys or values + */ + public synchronized void putAll(Map m) + { + Iterator itr = m.entrySet().iterator(); + + while (itr.hasNext()) + { + Map.Entry e = (Map.Entry) itr.next(); + // Optimize in case the Entry is one of our own. + if (e instanceof AbstractMap.BasicMapEntry) + { + AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e; + put(entry.key, entry.value); + } + else + { + put(e.getKey(), e.getValue()); + } + } + } + + /** + * Clears the hashtable so it has no keys. This is O(1). + */ + public synchronized void clear() + { + if (size > 0) + { + modCount++; + Arrays.fill(buckets, null); + size = 0; + } + } + + /** + * Returns a shallow clone of this Hashtable. The Map itself is cloned, + * but its contents are not. This is O(n). + * + * @return the clone + */ + public synchronized Object clone() + { + Hashtable copy = null; + try + { + copy = (Hashtable) super.clone(); + } + catch (CloneNotSupportedException x) + { + // This is impossible. + } + copy.buckets = new HashEntry[buckets.length]; + copy.putAllInternal(this); + // Clear the caches. + copy.keys = null; + copy.values = null; + copy.entries = null; + return copy; + } + + /** + * Converts this Hashtable to a String, surrounded by braces, and with + * key/value pairs listed with an equals sign between, separated by a + * comma and space. For example, <code>"{a=1, b=2}"</code>.<p> + * + * NOTE: if the <code>toString()</code> method of any key or value + * throws an exception, this will fail for the same reason. + * + * @return the string representation + */ + public synchronized String toString() + { + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized HashIterator instead. + Iterator entries = new HashIterator(ENTRIES); + StringBuffer r = new StringBuffer("{"); + for (int pos = size; pos > 0; pos--) + { + r.append(entries.next()); + if (pos > 1) + r.append(", "); + } + r.append("}"); + return r.toString(); + } + + /** + * Returns a "set view" of this Hashtable's keys. The set is backed by + * the hashtable, so changes in one show up in the other. The set supports + * element removal, but not element addition. The set is properly + * synchronized on the original hashtable. Sun has not documented the + * proper interaction of null with this set, but has inconsistent behavior + * in the JDK. Therefore, in this implementation, contains, remove, + * containsAll, retainAll, removeAll, and equals just ignore a null key + * rather than throwing a {@link NullPointerException}. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + * @since 1.2 + */ + public Set keySet() + { + if (keys == null) + { + // Create a synchronized AbstractSet with custom implementations of + // those methods that can be overridden easily and efficiently. + Set r = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new HashIterator(KEYS); + } + + public void clear() + { + Hashtable.this.clear(); + } + + public boolean contains(Object o) + { + if (o == null) + return false; + return containsKey(o); + } + + public boolean remove(Object o) + { + return Hashtable.this.remove(o) != null; + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + keys = new Collections.SynchronizedSet(this, r); + } + return keys; + } + + /** + * Returns a "collection view" (or "bag view") of this Hashtable's values. + * The collection is backed by the hashtable, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. The collection is properly synchronized on the original + * hashtable. Sun has not documented the proper interaction of null with + * this set, but has inconsistent behavior in the JDK. Therefore, in this + * implementation, contains, remove, containsAll, retainAll, removeAll, and + * equals just ignore a null value rather than throwing a + * {@link NullPointerException}. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + * @since 1.2 + */ + public Collection values() + { + if (values == null) + { + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + Collection r = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new HashIterator(VALUES); + } + + public void clear() + { + Hashtable.this.clear(); + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + values = new Collections.SynchronizedCollection(this, r); + } + return values; + } + + /** + * Returns a "set view" of this Hashtable's entries. The set is backed by + * the hashtable, so changes in one show up in the other. The set supports + * element removal, but not element addition. The set is properly + * synchronized on the original hashtable. Sun has not documented the + * proper interaction of null with this set, but has inconsistent behavior + * in the JDK. Therefore, in this implementation, contains, remove, + * containsAll, retainAll, removeAll, and equals just ignore a null entry, + * or an entry with a null key or value, rather than throwing a + * {@link NullPointerException}. However, calling entry.setValue(null) + * will fail. + * <p> + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the hashtable in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + * @since 1.2 + */ + public Set entrySet() + { + if (entries == null) + { + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + Set r = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new HashIterator(ENTRIES); + } + + public void clear() + { + Hashtable.this.clear(); + } + + public boolean contains(Object o) + { + return getEntry(o) != null; + } + + public boolean remove(Object o) + { + HashEntry e = getEntry(o); + if (e != null) + { + Hashtable.this.remove(e.key); + return true; + } + return false; + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + entries = new Collections.SynchronizedSet(this, r); + } + return entries; + } + + /** + * Returns true if this Hashtable equals the supplied Object <code>o</code>. + * As specified by Map, this is: + * <code> + * (o instanceof Map) && entrySet().equals(((Map) o).entrySet()); + * </code> + * + * @param o the object to compare to + * @return true if o is an equal map + * @since 1.2 + */ + public boolean equals(Object o) + { + // no need to synchronize, entrySet().equals() does that + if (o == this) + return true; + if (!(o instanceof Map)) + return false; + + return entrySet().equals(((Map) o).entrySet()); + } + + /** + * Returns the hashCode for this Hashtable. As specified by Map, this is + * the sum of the hashCodes of all of its Map.Entry objects + * + * @return the sum of the hashcodes of the entries + * @since 1.2 + */ + public synchronized int hashCode() + { + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized HashIterator instead. + Iterator itr = new HashIterator(ENTRIES); + int hashcode = 0; + for (int pos = size; pos > 0; pos--) + hashcode += itr.next().hashCode(); + + return hashcode; + } + + /** + * Helper method that returns an index in the buckets array for `key' + * based on its hashCode(). + * + * @param key the key + * @return the bucket number + * @throws NullPointerException if key is null + */ + private int hash(Object key) + { + // Note: Inline Math.abs here, for less method overhead, and to avoid + // a bootstrap dependency, since Math relies on native methods. + int hash = key.hashCode() % buckets.length; + return hash < 0 ? -hash : hash; + } + + /** + * Helper method for entrySet(), which matches both key and value + * simultaneously. Ignores null, as mentioned in entrySet(). + * + * @param o the entry to match + * @return the matching entry, if found, or null + * @see #entrySet() + */ + // Package visible, for use in nested classes. + HashEntry getEntry(Object o) + { + if (! (o instanceof Map.Entry)) + return null; + Object key = ((Map.Entry) o).getKey(); + if (key == null) + return null; + + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (o.equals(e)) + return e; + e = e.next; + } + return null; + } + + /** + * A simplified, more efficient internal implementation of putAll(). clone() + * should not call putAll or put, in order to be compatible with the JDK + * implementation with respect to subclasses. + * + * @param m the map to initialize this from + */ + void putAllInternal(Map m) + { + Iterator itr = m.entrySet().iterator(); + size = 0; + + while (itr.hasNext()) + { + size++; + Map.Entry e = (Map.Entry) itr.next(); + Object key = e.getKey(); + int idx = hash(key); + HashEntry he = new HashEntry(key, e.getValue()); + he.next = buckets[idx]; + buckets[idx] = he; + } + } + + /** + * Increases the size of the Hashtable and rehashes all keys to new array + * indices; this is called when the addition of a new value would cause + * size() > threshold. Note that the existing Entry objects are reused in + * the new hash table. + * <p> + * + * This is not specified, but the new size is twice the current size plus + * one; this number is not always prime, unfortunately. This implementation + * is not synchronized, as it is only invoked from synchronized methods. + */ + protected void rehash() + { + HashEntry[] oldBuckets = buckets; + + int newcapacity = (buckets.length * 2) + 1; + threshold = (int) (newcapacity * loadFactor); + buckets = new HashEntry[newcapacity]; + + for (int i = oldBuckets.length - 1; i >= 0; i--) + { + HashEntry e = oldBuckets[i]; + while (e != null) + { + int idx = hash(e.key); + HashEntry dest = buckets[idx]; + + if (dest != null) + { + while (dest.next != null) + dest = dest.next; + dest.next = e; + } + else + { + buckets[idx] = e; + } + + HashEntry next = e.next; + e.next = null; + e = next; + } + } + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i> (int) that is the length of the + * bucket array, the <i>size</i> (int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + // Write the threshold and loadFactor fields. + s.defaultWriteObject(); + + s.writeInt(buckets.length); + s.writeInt(size); + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized HashIterator instead. + Iterator it = new HashIterator(ENTRIES); + while (it.hasNext()) + { + HashEntry entry = (HashEntry) it.next(); + s.writeObject(entry.key); + s.writeObject(entry.value); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the <i>capacity</i> (int) that is the length of the + * bucket array, the <i>size</i> (int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // Read the threshold and loadFactor fields. + s.defaultReadObject(); + + // Read and use capacity. + buckets = new HashEntry[s.readInt()]; + int len = s.readInt(); + + // Read and use key/value pairs. + // TODO: should we be defensive programmers, and check for illegal nulls? + while (--len >= 0) + put(s.readObject(), s.readObject()); + } + + /** + * A class which implements the Iterator interface and is used for + * iterating over Hashtables. + * This implementation is parameterized to give a sequential view of + * keys, values, or entries; it also allows the removal of elements, + * as per the Javasoft spec. Note that it is not synchronized; this is + * a performance enhancer since it is never exposed externally and is + * only used within synchronized blocks above. + * + * @author Jon Zeppieri + */ + private final class HashIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + final int type; + /** + * The number of modifications to the backing Hashtable that we know about. + */ + int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Current index in the physical hash table. */ + int idx = buckets.length; + /** The last Entry returned by a next() call. */ + HashEntry last; + /** + * The next entry that should be returned by next(). It is set to something + * if we're iterating through a bucket that contains multiple linked + * entries. It is null if next() needs to find a new bucket. + */ + HashEntry next; + + /** + * Construct a new HashIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + HashIterator(int type) + { + this.type = type; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + * @throws ConcurrentModificationException if the hashtable was modified + */ + public boolean hasNext() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the hashtable was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + HashEntry e = next; + + while (e == null) + e = buckets[--idx]; + + next = e.next; + last = e; + if (type == VALUES) + return e.value; + if (type == KEYS) + return e.key; + return e; + } + + /** + * Removes from the backing Hashtable the last element which was fetched + * with the <code>next()</code> method. + * @throws ConcurrentModificationException if the hashtable was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + + Hashtable.this.remove(last.key); + last = null; + knownMod++; + } + } // class HashIterator + + + /** + * Enumeration view of this Hashtable, providing sequential access to its + * elements; this implementation is parameterized to provide access either + * to the keys or to the values in the Hashtable. + * + * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table + * as this could cause a rehash and we'd completely lose our place. Even + * without a rehash, it is undetermined if a new element added would + * appear in the enumeration. The spec says nothing about this, but + * the "Java Class Libraries" book infers that modifications to the + * hashtable during enumeration causes indeterminate results. Don't do it! + * + * @author Jon Zeppieri + */ + private final class Enumerator implements Enumeration + { + /** + * The type of this Iterator: {@link #KEYS} or {@link #VALUES}. + */ + final int type; + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Current index in the physical hash table. */ + int idx = buckets.length; + /** + * Entry which will be returned by the next nextElement() call. It is + * set if we are iterating through a bucket with multiple entries, or null + * if we must look in the next bucket. + */ + HashEntry next; + + /** + * Construct the enumeration. + * @param type either {@link #KEYS} or {@link #VALUES}. + */ + Enumerator(int type) + { + this.type = type; + } + + /** + * Checks whether more elements remain in the enumeration. + * @return true if nextElement() will not fail. + */ + public boolean hasMoreElements() + { + return count > 0; + } + + /** + * Returns the next element. + * @return the next element + * @throws NoSuchElementException if there is none. + */ + public Object nextElement() + { + if (count == 0) + throw new NoSuchElementException("Hashtable Enumerator"); + count--; + HashEntry e = next; + + while (e == null) + e = buckets[--idx]; + + next = e.next; + return type == VALUES ? e.value : e.key; + } + } // class Enumerator +} // class Hashtable diff --git a/libjava/classpath/java/util/IdentityHashMap.java b/libjava/classpath/java/util/IdentityHashMap.java new file mode 100644 index 00000000000..6369fac691b --- /dev/null +++ b/libjava/classpath/java/util/IdentityHashMap.java @@ -0,0 +1,935 @@ +/* IdentityHashMap.java -- a class providing a hashtable data structure, + mapping Object --> Object, which uses object identity for hashing. + Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a hashtable-backed implementation of the + * Map interface, but uses object identity to do its hashing. In fact, + * it uses object identity for comparing values, as well. It uses a + * linear-probe hash table, which may have faster performance + * than the chaining employed by HashMap. + * <p> + * + * <em>WARNING: This is not a general purpose map. Because it uses + * System.identityHashCode and ==, instead of hashCode and equals, for + * comparison, it violated Map's general contract, and may cause + * undefined behavior when compared to other maps which are not + * IdentityHashMaps. This is designed only for the rare cases when + * identity semantics are needed.</em> An example use is + * topology-preserving graph transformations, such as deep cloning, + * or as proxy object mapping such as in debugging. + * <p> + * + * This map permits <code>null</code> keys and values, and does not + * guarantee that elements will stay in the same order over time. The + * basic operations (<code>get</code> and <code>put</code>) take + * constant time, provided System.identityHashCode is decent. You can + * tune the behavior by specifying the expected maximum size. As more + * elements are added, the map may need to allocate a larger table, + * which can be expensive. + * <p> + * + * This implementation is unsynchronized. If you want multi-thread + * access to be consistent, you must synchronize it, perhaps by using + * <code>Collections.synchronizedMap(new IdentityHashMap(...));</code>. + * The iterators are <i>fail-fast</i>, meaning that a structural modification + * made to the map outside of an iterator's remove method cause the + * iterator, and in the case of the entrySet, the Map.Entry, to + * fail with a {@link ConcurrentModificationException}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see System#identityHashCode(Object) + * @see Collection + * @see Map + * @see HashMap + * @see TreeMap + * @see LinkedHashMap + * @see WeakHashMap + * @since 1.4 + * @status updated to 1.4 + */ +public class IdentityHashMap extends AbstractMap + implements Map, Serializable, Cloneable +{ + /** The default capacity. */ + private static final int DEFAULT_CAPACITY = 21; + + /** + * This object is used to mark deleted items. Package visible for use by + * nested classes. + */ + static final Object tombstone = new Object(); + + /** + * This object is used to mark empty slots. We need this because + * using null is ambiguous. Package visible for use by nested classes. + */ + static final Object emptyslot = new Object(); + + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8188218128353913216L; + + /** + * The number of mappings in the table. Package visible for use by nested + * classes. + * @serial + */ + int size; + + /** + * The table itself. Package visible for use by nested classes. + */ + transient Object[] table; + + /** + * The number of structural modifications made so far. Package visible for + * use by nested classes. + */ + transient int modCount; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set entries; + + /** + * The threshold for rehashing, which is 75% of (table.length / 2). + */ + private transient int threshold; + + /** + * Create a new IdentityHashMap with the default capacity (21 entries). + */ + public IdentityHashMap() + { + this(DEFAULT_CAPACITY); + } + + /** + * Create a new IdentityHashMap with the indicated number of + * entries. If the number of elements added to this hash map + * exceeds this maximum, the map will grow itself; however, that + * incurs a performance penalty. + * + * @param max initial size + * @throws IllegalArgumentException if max is negative + */ + public IdentityHashMap(int max) + { + if (max < 0) + throw new IllegalArgumentException(); + // Need at least two slots, or hash() will break. + if (max < 2) + max = 2; + table = new Object[max << 1]; + Arrays.fill(table, emptyslot); + threshold = (max >> 2) * 3; + } + + /** + * Create a new IdentityHashMap whose contents are taken from the + * given Map. + * + * @param m The map whose elements are to be put in this map + * @throws NullPointerException if m is null + */ + public IdentityHashMap(Map m) + { + this(Math.max(m.size() << 1, DEFAULT_CAPACITY)); + putAll(m); + } + + /** + * Remove all mappings from this map. + */ + public void clear() + { + if (size != 0) + { + modCount++; + Arrays.fill(table, emptyslot); + size = 0; + } + } + + /** + * Creates a shallow copy where keys and values are not cloned. + */ + public Object clone() + { + try + { + IdentityHashMap copy = (IdentityHashMap) super.clone(); + copy.table = (Object[]) table.clone(); + copy.entries = null; // invalidate the cache + return copy; + } + catch (CloneNotSupportedException e) + { + // Can't happen. + return null; + } + } + + /** + * Tests whether the specified key is in this map. Unlike normal Maps, + * this test uses <code>entry == key</code> instead of + * <code>entry == null ? key == null : entry.equals(key)</code>. + * + * @param key the key to look for + * @return true if the key is contained in the map + * @see #containsValue(Object) + * @see #get(Object) + */ + public boolean containsKey(Object key) + { + return key == table[hash(key)]; + } + + /** + * Returns true if this HashMap contains the value. Unlike normal maps, + * this test uses <code>entry == value</code> instead of + * <code>entry == null ? value == null : entry.equals(value)</code>. + * + * @param value the value to search for in this HashMap + * @return true if at least one key maps to the value + * @see #containsKey(Object) + */ + public boolean containsValue(Object value) + { + for (int i = table.length - 1; i > 0; i -= 2) + if (table[i] == value) + return true; + return false; + } + + /** + * Returns a "set view" of this Map's entries. The set is backed by + * the Map, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * <p> + * + * <em>The semantics of this set, and of its contained entries, are + * different from the contract of Set and Map.Entry in order to make + * IdentityHashMap work. This means that while you can compare these + * objects between IdentityHashMaps, comparing them with regular sets + * or entries is likely to have undefined behavior.</em> The entries + * in this set are reference-based, rather than the normal object + * equality. Therefore, <code>e1.equals(e2)</code> returns + * <code>e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue()</code>, + * and <code>e.hashCode()</code> returns + * <code>System.identityHashCode(e.getKey()) ^ + * System.identityHashCode(e.getValue())</code>. + * <p> + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the Map in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set entrySet() + { + if (entries == null) + entries = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new IdentityIterator(ENTRIES); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry m = (Map.Entry) o; + return m.getValue() == table[hash(m.getKey()) + 1]; + } + + public int hashCode() + { + return IdentityHashMap.this.hashCode(); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Object key = ((Map.Entry) o).getKey(); + int h = hash(key); + if (table[h] == key) + { + size--; + modCount++; + table[h] = tombstone; + table[h + 1] = tombstone; + return true; + } + return false; + } + }; + return entries; + } + + /** + * Compares two maps for equality. This returns true only if both maps + * have the same reference-identity comparisons. While this returns + * <code>this.entrySet().equals(m.entrySet())</code> as specified by Map, + * this will not work with normal maps, since the entry set compares + * with == instead of .equals. + * + * @param o the object to compare to + * @return true if it is equal + */ + public boolean equals(Object o) + { + // Why did Sun specify this one? The superclass does the right thing. + return super.equals(o); + } + + /** + * Return the value in this Map associated with the supplied key, or + * <code>null</code> if the key maps to nothing. + * + * <p>NOTE: Since the value could also be null, you must use + * containsKey to see if this key actually maps to something. + * Unlike normal maps, this tests for the key with <code>entry == + * key</code> instead of <code>entry == null ? key == null : + * entry.equals(key)</code>. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public Object get(Object key) + { + int h = hash(key); + return table[h] == key ? table[h + 1] : null; + } + + /** + * Returns the hashcode of this map. This guarantees that two + * IdentityHashMaps that compare with equals() will have the same hash code, + * but may break with comparison to normal maps since it uses + * System.identityHashCode() instead of hashCode(). + * + * @return the hash code + */ + public int hashCode() + { + int hash = 0; + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key == emptyslot || key == tombstone) + continue; + hash += (System.identityHashCode(key) + ^ System.identityHashCode(table[i + 1])); + } + return hash; + } + + /** + * Returns true if there are no key-value mappings currently in this Map + * @return <code>size() == 0</code> + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Returns a "set view" of this Map's keys. The set is backed by the + * Map, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * <p> + * + * <em>The semantics of this set are different from the contract of Set + * in order to make IdentityHashMap work. This means that while you can + * compare these objects between IdentityHashMaps, comparing them with + * regular sets is likely to have undefined behavior.</em> The hashCode + * of the set is the sum of the identity hash codes, instead of the + * regular hashCodes, and equality is determined by reference instead + * of by the equals method. + * <p> + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + keys = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new IdentityIterator(KEYS); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public int hashCode() + { + int hash = 0; + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key == emptyslot || key == tombstone) + continue; + hash += System.identityHashCode(key); + } + return hash; + + } + + public boolean remove(Object o) + { + int h = hash(o); + if (table[h] == o) + { + size--; + modCount++; + table[h] = tombstone; + table[h + 1] = tombstone; + return true; + } + return false; + } + }; + return keys; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which <code>equals()</code> + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. Unlike normal maps, this tests for the key + * with <code>entry == key</code> instead of + * <code>entry == null ? key == null : entry.equals(key)</code>. + * + * @param key the key used to locate the value + * @param value the value to be stored in the HashMap + * @return the prior mapping of the key, or null if there was none + * @see #get(Object) + */ + public Object put(Object key, Object value) + { + // Rehash if the load factor is too high. + if (size > threshold) + { + Object[] old = table; + // This isn't necessarily prime, but it is an odd number of key/value + // slots, which has a higher probability of fewer collisions. + table = new Object[(old.length * 2) + 2]; + Arrays.fill(table, emptyslot); + size = 0; + threshold = (table.length >>> 3) * 3; + + for (int i = old.length - 2; i >= 0; i -= 2) + { + Object oldkey = old[i]; + if (oldkey != tombstone && oldkey != emptyslot) + // Just use put. This isn't very efficient, but it is ok. + put(oldkey, old[i + 1]); + } + } + + int h = hash(key); + if (table[h] == key) + { + Object r = table[h + 1]; + table[h + 1] = value; + return r; + } + + // At this point, we add a new mapping. + modCount++; + size++; + table[h] = key; + table[h + 1] = value; + return null; + } + + /** + * Copies all of the mappings from the specified map to this. If a key + * is already in this map, its value is replaced. + * + * @param m the map to copy + * @throws NullPointerException if m is null + */ + public void putAll(Map m) + { + // Why did Sun specify this one? The superclass does the right thing. + super.putAll(m); + } + + /** + * Removes from the HashMap and returns the value which is mapped by + * the supplied key. If the key maps to nothing, then the HashMap + * remains unchanged, and <code>null</code> is returned. + * + * NOTE: Since the value could also be null, you must use + * containsKey to see if you are actually removing a mapping. + * Unlike normal maps, this tests for the key with <code>entry == + * key</code> instead of <code>entry == null ? key == null : + * entry.equals(key)</code>. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public Object remove(Object key) + { + int h = hash(key); + if (table[h] == key) + { + modCount++; + size--; + Object r = table[h + 1]; + table[h] = tombstone; + table[h + 1] = tombstone; + return r; + } + return null; + } + + /** + * Returns the number of kay-value mappings currently in this Map + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns a "collection view" (or "bag view") of this Map's values. + * The collection is backed by the Map, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + * <p> + * + * <em>The semantics of this set are different from the contract of + * Collection in order to make IdentityHashMap work. This means that + * while you can compare these objects between IdentityHashMaps, comparing + * them with regular sets is likely to have undefined behavior.</em> + * Likewise, contains and remove go by == instead of equals(). + * <p> + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new IdentityIterator(VALUES); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean remove(Object o) + { + for (int i = table.length - 1; i > 0; i -= 2) + if (table[i] == o) + { + modCount++; + table[i - 1] = tombstone; + table[i] = tombstone; + size--; + return true; + } + return false; + } + }; + return values; + } + + /** + * Helper method which computes the hash code, then traverses the table + * until it finds the key, or the spot where the key would go. + * + * @param key the key to check + * @return the index where the key belongs + * @see #IdentityHashMap(int) + * @see #put(Object, Object) + */ + // Package visible for use by nested classes. + int hash(Object key) + { + // Implementation note: it is feasible for the table to have no + // emptyslots, if it is full with entries and tombstones, so we must + // remember where we started. If we encounter the key or an emptyslot, + // we are done. If we encounter a tombstone, the key may still be in + // the array. If we don't encounter the key, we use the first emptyslot + // or tombstone we encountered as the location where the key would go. + // By requiring at least 2 key/value slots, and rehashing at 75% + // capacity, we guarantee that there will always be either an emptyslot + // or a tombstone somewhere in the table. + int h = Math.abs(System.identityHashCode(key) % (table.length >> 1)) << 1; + int del = -1; + int save = h; + + do + { + if (table[h] == key) + return h; + if (table[h] == emptyslot) + break; + if (table[h] == tombstone && del < 0) + del = h; + h -= 2; + if (h < 0) + h = table.length - 2; + } + while (h != save); + + return del < 0 ? h : del; + } + + /** + * This class allows parameterized iteration over IdentityHashMaps. Based + * on its construction, it returns the key or value of a mapping, or + * creates the appropriate Map.Entry object with the correct fail-fast + * semantics and identity comparisons. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + */ + private class IdentityIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + final int type; + /** The number of modifications to the backing Map that we know about. */ + int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Location in the table. */ + int loc = table.length; + + /** + * Construct a new Iterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + IdentityIterator(int type) + { + this.type = type; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + * @throws ConcurrentModificationException if the Map was modified + */ + public boolean hasNext() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the Map was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + + Object key; + do + { + loc -= 2; + key = table[loc]; + } + while (key == emptyslot || key == tombstone); + + return type == KEYS ? key : (type == VALUES ? table[loc + 1] + : new IdentityEntry(loc)); + } + + /** + * Removes from the backing Map the last element which was fetched + * with the <code>next()</code> method. + * + * @throws ConcurrentModificationException if the Map was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (loc == table.length || table[loc] == tombstone) + throw new IllegalStateException(); + modCount++; + size--; + table[loc] = tombstone; + table[loc + 1] = tombstone; + knownMod++; + } + } // class IdentityIterator + + /** + * This class provides Map.Entry objects for IdentityHashMaps. The entry + * is fail-fast, and will throw a ConcurrentModificationException if + * the underlying map is modified, or if remove is called on the iterator + * that generated this object. It is identity based, so it violates + * the general contract of Map.Entry, and is probably unsuitable for + * comparison to normal maps; but it works among other IdentityHashMaps. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class IdentityEntry implements Map.Entry + { + /** The location of this entry. */ + final int loc; + /** The number of modifications to the backing Map that we know about. */ + final int knownMod = modCount; + + /** + * Constructs the Entry. + * + * @param loc the location of this entry in table + */ + IdentityEntry(int loc) + { + this.loc = loc; + } + + /** + * Compares the specified object with this entry, using identity + * semantics. Note that this can lead to undefined results with + * Entry objects created by normal maps. + * + * @param o the object to compare + * @return true if it is equal + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public boolean equals(Object o) + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + if (! (o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry) o; + return table[loc] == e.getKey() && table[loc + 1] == e.getValue(); + } + + /** + * Returns the key of this entry. + * + * @return the key + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public Object getKey() + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + return table[loc]; + } + + /** + * Returns the value of this entry. + * + * @return the value + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public Object getValue() + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + return table[loc + 1]; + } + + /** + * Returns the hashcode of the entry, using identity semantics. + * Note that this can lead to undefined results with Entry objects + * created by normal maps. + * + * @return the hash code + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public int hashCode() + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + return (System.identityHashCode(table[loc]) + ^ System.identityHashCode(table[loc + 1])); + } + + /** + * Replaces the value of this mapping, and returns the old value. + * + * @param value the new value + * @return the old value + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public Object setValue(Object value) + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + Object r = table[loc + 1]; + table[loc + 1] = value; + return r; + } + + /** + * This provides a string representation of the entry. It is of the form + * "key=value", where string concatenation is used on key and value. + * + * @return the string representation + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public String toString() + { + if (knownMod != modCount || table[loc] == tombstone) + throw new ConcurrentModificationException(); + return table[loc] + "=" + table[loc + 1]; + } + } // class IdentityEntry + + /** + * Reads the object from a serial stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData expects the size (int), followed by that many key (Object) + * and value (Object) pairs, with the pairs in no particular + * order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + int num = s.readInt(); + table = new Object[Math.max(num << 1, DEFAULT_CAPACITY) << 1]; + // Read key/value pairs. + while (--num >= 0) + put(s.readObject(), s.readObject()); + } + + /** + * Writes the object to a serial stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData outputs the size (int), followed by that many key (Object) + * and value (Object) pairs, with the pairs in no particular + * order + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + s.writeInt(size); + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key != tombstone && key != emptyslot) + { + s.writeObject(key); + s.writeObject(table[i + 1]); + } + } + } +} diff --git a/libjava/classpath/java/util/Iterator.java b/libjava/classpath/java/util/Iterator.java new file mode 100644 index 00000000000..31ecff8a257 --- /dev/null +++ b/libjava/classpath/java/util/Iterator.java @@ -0,0 +1,87 @@ +/* Iterator.java -- Interface for iterating over collections + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An object which iterates over a collection. An Iterator is used to return + * the items once only, in sequence, by successive calls to the next method. + * It is also possible to remove elements from the underlying collection by + * using the optional remove method. Iterator is intended as a replacement + * for the Enumeration interface of previous versions of Java, which did not + * have the remove method and had less conveniently named methods. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see ListIterator + * @see Enumeration + * @since 1.2 + * @status updated to 1.4 + */ +public interface Iterator +{ + /** + * Tests whether there are elements remaining in the collection. In other + * words, calling <code>next()</code> will not throw an exception. + * + * @return true if there is at least one more element in the collection + */ + boolean hasNext(); + + /** + * Obtain the next element in the collection. + * + * @return the next element in the collection + * @throws NoSuchElementException if there are no more elements + */ + Object next(); + + /** + * Remove from the underlying collection the last element returned by next + * (optional operation). This method can be called only once after each + * call to <code>next()</code>. It does not affect what will be returned + * by subsequent calls to next. + * + * @throws IllegalStateException if next has not yet been called or remove + * has already been called since the last call to next. + * @throws UnsupportedOperationException if this Iterator does not support + * the remove operation. + */ + void remove(); +} diff --git a/libjava/classpath/java/util/LinkedHashMap.java b/libjava/classpath/java/util/LinkedHashMap.java new file mode 100644 index 00000000000..8e895a9e0d8 --- /dev/null +++ b/libjava/classpath/java/util/LinkedHashMap.java @@ -0,0 +1,501 @@ +/* LinkedHashMap.java -- a class providing hashtable data structure, + mapping Object --> Object, with linked list traversal + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class provides a hashtable-backed implementation of the + * Map interface, with predictable traversal order. + * <p> + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. In + * addition, this maintains a doubly-linked list which tracks either + * insertion or access order. + * <p> + * + * In insertion order, calling <code>put</code> adds the key to the end of + * traversal, unless the key was already in the map; changing traversal order + * requires removing and reinserting a key. On the other hand, in access + * order, all calls to <code>put</code> and <code>get</code> cause the + * accessed key to move to the end of the traversal list. Note that any + * accesses to the map's contents via its collection views and iterators do + * not affect the map's traversal order, since the collection views do not + * call <code>put</code> or <code>get</code>. + * <p> + * + * One of the nice features of tracking insertion order is that you can + * copy a hashtable, and regardless of the implementation of the original, + * produce the same results when iterating over the copy. This is possible + * without needing the overhead of <code>TreeMap</code>. + * <p> + * + * When using this {@link #LinkedHashMap(int, float, boolean) constructor}, + * you can build an access-order mapping. This can be used to implement LRU + * caches, for example. By overriding {@link #removeEldestEntry(Map.Entry)}, + * you can also control the removal of the oldest entry, and thereby do + * things like keep the map at a fixed size. + * <p> + * + * Under ideal circumstances (no collisions), LinkedHashMap offers O(1) + * performance on most operations (<code>containsValue()</code> is, + * of course, O(n)). In the worst case (all keys map to the same + * hash code -- very unlikely), most operations are O(n). Traversal is + * faster than in HashMap (proportional to the map size, and not the space + * allocated for the map), but other operations may be slower because of the + * overhead of the maintaining the traversal order list. + * <p> + * + * LinkedHashMap accepts the null key and null values. It is not + * synchronized, so if you need multi-threaded access, consider using:<br> + * <code>Map m = Collections.synchronizedMap(new LinkedHashMap(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Object#hashCode() + * @see Collection + * @see Map + * @see HashMap + * @see TreeMap + * @see Hashtable + * @since 1.4 + * @status updated to 1.4 + */ +public class LinkedHashMap extends HashMap +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3801124242820219131L; + + /** + * The oldest Entry to begin iteration at. + */ + transient LinkedHashEntry root; + + /** + * The iteration order of this linked hash map: <code>true</code> for + * access-order, <code>false</code> for insertion-order. + * + * @serial true for access order traversal + */ + final boolean accessOrder; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair and the doubly-linked insertion order list. + */ + class LinkedHashEntry extends HashEntry + { + /** + * The predecessor in the iteration list. If this entry is the root + * (eldest), pred points to the newest entry. + */ + LinkedHashEntry pred; + + /** The successor in the iteration list, null if this is the newest. */ + LinkedHashEntry succ; + + /** + * Simple constructor. + * + * @param key the key + * @param value the value + */ + LinkedHashEntry(Object key, Object value) + { + super(key, value); + if (root == null) + { + root = this; + pred = this; + } + else + { + pred = root.pred; + pred.succ = this; + root.pred = this; + } + } + + /** + * Called when this entry is accessed via put or get. This version does + * the necessary bookkeeping to keep the doubly-linked list in order, + * after moving this element to the newest position in access order. + */ + void access() + { + if (accessOrder && succ != null) + { + modCount++; + if (this == root) + { + root = succ; + pred.succ = this; + succ = null; + } + else + { + pred.succ = succ; + succ.pred = pred; + succ = null; + pred = root.pred; + pred.succ = this; + root.pred = this; + } + } + } + + /** + * Called when this entry is removed from the map. This version does + * the necessary bookkeeping to keep the doubly-linked list in order. + * + * @return the value of this key as it is removed + */ + Object cleanup() + { + if (this == root) + { + root = succ; + if (succ != null) + succ.pred = pred; + } + else if (succ == null) + { + pred.succ = null; + root.pred = pred; + } + else + { + pred.succ = succ; + succ.pred = pred; + } + return value; + } + } // class LinkedHashEntry + + /** + * Construct a new insertion-ordered LinkedHashMap with the default + * capacity (11) and the default load factor (0.75). + */ + public LinkedHashMap() + { + super(); + accessOrder = false; + } + + /** + * Construct a new insertion-ordered LinkedHashMap from the given Map, + * with initial capacity the greater of the size of <code>m</code> or + * the default of 11. + * <p> + * + * Every element in Map m will be put into this new HashMap, in the + * order of m's iterator. + * + * @param m a Map whose key / value pairs will be put into + * the new HashMap. <b>NOTE: key / value pairs + * are not cloned in this constructor.</b> + * @throws NullPointerException if m is null + */ + public LinkedHashMap(Map m) + { + super(m); + accessOrder = false; + } + + /** + * Construct a new insertion-ordered LinkedHashMap with a specific + * inital capacity and default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this HashMap (>= 0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public LinkedHashMap(int initialCapacity) + { + super(initialCapacity); + accessOrder = false; + } + + /** + * Construct a new insertion-orderd LinkedHashMap with a specific + * inital capacity and load factor. + * + * @param initialCapacity the initial capacity (>= 0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public LinkedHashMap(int initialCapacity, float loadFactor) + { + super(initialCapacity, loadFactor); + accessOrder = false; + } + + /** + * Construct a new LinkedHashMap with a specific inital capacity, load + * factor, and ordering mode. + * + * @param initialCapacity the initial capacity (>=0) + * @param loadFactor the load factor (>0, not NaN) + * @param accessOrder true for access-order, false for insertion-order + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public LinkedHashMap(int initialCapacity, float loadFactor, + boolean accessOrder) + { + super(initialCapacity, loadFactor); + this.accessOrder = accessOrder; + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + super.clear(); + root = null; + } + + /** + * Returns <code>true</code> if this HashMap contains a value + * <code>o</code>, such that <code>o.equals(value)</code>. + * + * @param value the value to search for in this HashMap + * @return <code>true</code> if at least one key maps to the value + */ + public boolean containsValue(Object value) + { + LinkedHashEntry e = root; + while (e != null) + { + if (equals(value, e.value)) + return true; + e = e.succ; + } + return false; + } + + /** + * Return the value in this Map associated with the supplied key, + * or <code>null</code> if the key maps to nothing. If this is an + * access-ordered Map and the key is found, this performs structural + * modification, moving the key to the newest end of the list. NOTE: + * Since the value could also be null, you must use containsKey to + * see if this key actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public Object get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + { + e.access(); + return e.value; + } + e = e.next; + } + return null; + } + + /** + * Returns <code>true</code> if this map should remove the eldest entry. + * This method is invoked by all calls to <code>put</code> and + * <code>putAll</code> which place a new entry in the map, providing + * the implementer an opportunity to remove the eldest entry any time + * a new one is added. This can be used to save memory usage of the + * hashtable, as well as emulating a cache, by deleting stale entries. + * <p> + * + * For example, to keep the Map limited to 100 entries, override as follows: + * <pre> + * private static final int MAX_ENTRIES = 100; + * protected boolean removeEldestEntry(Map.Entry eldest) + * { + * return size() > MAX_ENTRIES; + * } + * </pre><p> + * + * Typically, this method does not modify the map, but just uses the + * return value as an indication to <code>put</code> whether to proceed. + * However, if you override it to modify the map, you must return false + * (indicating that <code>put</code> should leave the modified map alone), + * or you face unspecified behavior. Remember that in access-order mode, + * even calling <code>get</code> is a structural modification, but using + * the collections views (such as <code>keySet</code>) is not. + * <p> + * + * This method is called after the eldest entry has been inserted, so + * if <code>put</code> was called on a previously empty map, the eldest + * entry is the one you just put in! The default implementation just + * returns <code>false</code>, so that this map always behaves like + * a normal one with unbounded growth. + * + * @param eldest the eldest element which would be removed if this + * returns true. For an access-order map, this is the least + * recently accessed; for an insertion-order map, this is the + * earliest element inserted. + * @return true if <code>eldest</code> should be removed + */ + protected boolean removeEldestEntry(Map.Entry eldest) + { + return false; + } + + /** + * Helper method called by <code>put</code>, which creates and adds a + * new Entry, followed by performing bookkeeping (like removeEldestEntry). + * + * @param key the key of the new Entry + * @param value the value + * @param idx the index in buckets where the new Entry belongs + * @param callRemove whether to call the removeEldestEntry method + * @see #put(Object, Object) + * @see #removeEldestEntry(Map.Entry) + * @see LinkedHashEntry#LinkedHashEntry(Object, Object) + */ + void addEntry(Object key, Object value, int idx, boolean callRemove) + { + LinkedHashEntry e = new LinkedHashEntry(key, value); + e.next = buckets[idx]; + buckets[idx] = e; + if (callRemove && removeEldestEntry(root)) + remove(root.key); + } + + /** + * Helper method, called by clone() to reset the doubly-linked list. + * + * @param m the map to add entries from + * @see #clone() + */ + void putAllInternal(Map m) + { + root = null; + super.putAllInternal(m); + } + + /** + * Generates a parameterized iterator. This allows traversal to follow + * the doubly-linked list instead of the random bin order of HashMap. + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @return the appropriate iterator + */ + Iterator iterator(final int type) + { + return new Iterator() + { + /** The current Entry. */ + LinkedHashEntry current = root; + + /** The previous Entry returned by next(). */ + LinkedHashEntry last; + + /** The number of known modifications to the backing Map. */ + int knownMod = modCount; + + /** + * Returns true if the Iterator has more elements. + * + * @return true if there are more elements + * @throws ConcurrentModificationException if the HashMap was modified + */ + public boolean hasNext() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return current != null; + } + + /** + * Returns the next element in the Iterator's sequential view. + * + * @return the next element + * @throws ConcurrentModificationException if the HashMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (current == null) + throw new NoSuchElementException(); + last = current; + current = current.succ; + return type == VALUES ? last.value : type == KEYS ? last.key : last; + } + + /** + * Removes from the backing HashMap the last element which was fetched + * with the <code>next()</code> method. + * + * @throws ConcurrentModificationException if the HashMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + LinkedHashMap.this.remove(last.key); + last = null; + knownMod++; + } + }; + } +} // class LinkedHashMap diff --git a/libjava/classpath/java/util/LinkedHashSet.java b/libjava/classpath/java/util/LinkedHashSet.java new file mode 100644 index 00000000000..6c68195c3bd --- /dev/null +++ b/libjava/classpath/java/util/LinkedHashSet.java @@ -0,0 +1,160 @@ +/* LinkedHashSet.java -- a set backed by a LinkedHashMap, for linked + list traversal. + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * This class provides a hashtable-backed implementation of the + * Set interface, with predictable traversal order. + * <p> + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. In + * addition, this maintains a doubly-linked list which tracks insertion + * order. Note that the insertion order is not modified if an + * <code>add</code> simply reinserts an element in the set. + * <p> + * + * One of the nice features of tracking insertion order is that you can + * copy a set, and regardless of the implementation of the original, + * produce the same results when iterating over the copy. This is possible + * without needing the overhead of <code>TreeSet</code>. + * <p> + * + * Under ideal circumstances (no collisions), LinkedHashSet offers O(1) + * performance on most operations. In the worst case (all elements map + * to the same hash code -- very unlikely), most operations are O(n). + * <p> + * + * LinkedHashSet accepts the null entry. It is not synchronized, so if + * you need multi-threaded access, consider using:<br> + * <code>Set s = Collections.synchronizedSet(new LinkedHashSet(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Object#hashCode() + * @see Collection + * @see Set + * @see HashSet + * @see TreeSet + * @see Collections#synchronizedSet(Set) + * @since 1.4 + * @status updated to 1.4 + */ +public class LinkedHashSet extends HashSet + implements Set, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2851667679971038690L; + + /** + * Construct a new, empty HashSet whose backing HashMap has the default + * capacity (11) and loadFacor (0.75). + */ + public LinkedHashSet() + { + super(); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity of the backing HashMap + * @throws IllegalArgumentException if the capacity is negative + */ + public LinkedHashSet(int initialCapacity) + { + super(initialCapacity); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and load factor. + * + * @param initialCapacity the initial capacity of the backing HashMap + * @param loadFactor the load factor of the backing HashMap + * @throws IllegalArgumentException if either argument is negative, or + * if loadFactor is POSITIVE_INFINITY or NaN + */ + public LinkedHashSet(int initialCapacity, float loadFactor) + { + super(initialCapacity, loadFactor); + } + + /** + * Construct a new HashSet with the same elements as are in the supplied + * collection (eliminating any duplicates, of course). The backing storage + * has twice the size of the collection, or the default size of 11, + * whichever is greater; and the default load factor (0.75). + * + * @param c a collection of initial set elements + * @throws NullPointerException if c is null + */ + public LinkedHashSet(Collection c) + { + super(c); + } + + /** + * Helper method which initializes the backing Map. + * + * @param capacity the initial capacity + * @param load the initial load factor + * @return the backing HashMap + */ + HashMap init(int capacity, float load) + { + return new LinkedHashMap(capacity, load); + } + +} diff --git a/libjava/classpath/java/util/LinkedList.java b/libjava/classpath/java/util/LinkedList.java new file mode 100644 index 00000000000..2a35425fa24 --- /dev/null +++ b/libjava/classpath/java/util/LinkedList.java @@ -0,0 +1,958 @@ +/* LinkedList.java -- Linked list implementation of the List interface + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * Linked list implementation of the List interface. In addition to the + * methods of the List interface, this class provides access to the first + * and last list elements in O(1) time for easy stack, queue, or double-ended + * queue (deque) creation. The list is doubly-linked, with traversal to a + * given index starting from the end closest to the element.<p> + * + * LinkedList is not synchronized, so if you need multi-threaded access, + * consider using:<br> + * <code>List l = Collections.synchronizedList(new LinkedList(...));</code> + * <p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @see ArrayList + * @see Vector + * @see Collections#synchronizedList(List) + * @since 1.2 + * @status missing javadoc, but complete to 1.4 + */ +public class LinkedList extends AbstractSequentialList + implements List, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 876323262645176354L; + + /** + * The first element in the list. + */ + transient Entry first; + + /** + * The last element in the list. + */ + transient Entry last; + + /** + * The current length of the list. + */ + transient int size = 0; + + /** + * Class to represent an entry in the list. Holds a single element. + */ + private static final class Entry + { + /** The element in the list. */ + Object data; + + /** The next list entry, null if this is last. */ + Entry next; + + /** The previous list entry, null if this is first. */ + Entry previous; + + /** + * Construct an entry. + * @param data the list element + */ + Entry(Object data) + { + this.data = data; + } + } // class Entry + + /** + * Obtain the Entry at a given position in a list. This method of course + * takes linear time, but it is intelligent enough to take the shorter of the + * paths to get to the Entry required. This implies that the first or last + * entry in the list is obtained in constant time, which is a very desirable + * property. + * For speed and flexibility, range checking is not done in this method: + * Incorrect values will be returned if (n < 0) or (n >= size). + * + * @param n the number of the entry to get + * @return the entry at position n + */ + // Package visible for use in nested classes. + Entry getEntry(int n) + { + Entry e; + if (n < size / 2) + { + e = first; + // n less than size/2, iterate from start + while (n-- > 0) + e = e.next; + } + else + { + e = last; + // n greater than size/2, iterate from end + while (++n < size) + e = e.previous; + } + return e; + } + + /** + * Remove an entry from the list. This will adjust size and deal with + * `first' and `last' appropriatly. + * + * @param e the entry to remove + */ + // Package visible for use in nested classes. + void removeEntry(Entry e) + { + modCount++; + size--; + if (size == 0) + first = last = null; + else + { + if (e == first) + { + first = e.next; + e.next.previous = null; + } + else if (e == last) + { + last = e.previous; + e.previous.next = null; + } + else + { + e.next.previous = e.previous; + e.previous.next = e.next; + } + } + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index < 0 || index > size + */ + private void checkBoundsInclusive(int index) + { + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index < 0 || index >= size + */ + private void checkBoundsExclusive(int index) + { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Create an empty linked list. + */ + public LinkedList() + { + } + + /** + * Create a linked list containing the elements, in order, of a given + * collection. + * + * @param c the collection to populate this list from + * @throws NullPointerException if c is null + */ + public LinkedList(Collection c) + { + addAll(c); + } + + /** + * Returns the first element in the list. + * + * @return the first list element + * @throws NoSuchElementException if the list is empty + */ + public Object getFirst() + { + if (size == 0) + throw new NoSuchElementException(); + return first.data; + } + + /** + * Returns the last element in the list. + * + * @return the last list element + * @throws NoSuchElementException if the list is empty + */ + public Object getLast() + { + if (size == 0) + throw new NoSuchElementException(); + return last.data; + } + + /** + * Remove and return the first element in the list. + * + * @return the former first element in the list + * @throws NoSuchElementException if the list is empty + */ + public Object removeFirst() + { + if (size == 0) + throw new NoSuchElementException(); + modCount++; + size--; + Object r = first.data; + + if (first.next != null) + first.next.previous = null; + else + last = null; + + first = first.next; + + return r; + } + + /** + * Remove and return the last element in the list. + * + * @return the former last element in the list + * @throws NoSuchElementException if the list is empty + */ + public Object removeLast() + { + if (size == 0) + throw new NoSuchElementException(); + modCount++; + size--; + Object r = last.data; + + if (last.previous != null) + last.previous.next = null; + else + first = null; + + last = last.previous; + + return r; + } + + /** + * Insert an element at the first of the list. + * + * @param o the element to insert + */ + public void addFirst(Object o) + { + Entry e = new Entry(o); + + modCount++; + if (size == 0) + first = last = e; + else + { + e.next = first; + first.previous = e; + first = e; + } + size++; + } + + /** + * Insert an element at the last of the list. + * + * @param o the element to insert + */ + public void addLast(Object o) + { + addLastEntry(new Entry(o)); + } + + /** + * Inserts an element at the end of the list. + * + * @param e the entry to add + */ + private void addLastEntry(Entry e) + { + modCount++; + if (size == 0) + first = last = e; + else + { + e.previous = last; + last.next = e; + last = e; + } + size++; + } + + /** + * Returns true if the list contains the given object. Comparison is done by + * <code>o == null ? e = null : o.equals(e)</code>. + * + * @param o the element to look for + * @return true if it is found + */ + public boolean contains(Object o) + { + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + return true; + e = e.next; + } + return false; + } + + /** + * Returns the size of the list. + * + * @return the list size + */ + public int size() + { + return size; + } + + /** + * Adds an element to the end of the list. + * + * @param o the entry to add + * @return true, as it always succeeds + */ + public boolean add(Object o) + { + addLastEntry(new Entry(o)); + return true; + } + + /** + * Removes the entry at the lowest index in the list that matches the given + * object, comparing by <code>o == null ? e = null : o.equals(e)</code>. + * + * @param o the object to remove + * @return true if an instance of the object was removed + */ + public boolean remove(Object o) + { + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + { + removeEntry(e); + return true; + } + e = e.next; + } + return false; + } + + /** + * Append the elements of the collection in iteration order to the end of + * this list. If this list is modified externally (for example, if this + * list is the collection), behavior is unspecified. + * + * @param c the collection to append + * @return true if the list was modified + * @throws NullPointerException if c is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Insert the elements of the collection in iteration order at the given + * index of this list. If this list is modified externally (for example, + * if this list is the collection), behavior is unspecified. + * + * @param c the collection to append + * @return true if the list was modified + * @throws NullPointerException if c is null + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public boolean addAll(int index, Collection c) + { + checkBoundsInclusive(index); + int csize = c.size(); + + if (csize == 0) + return false; + + Iterator itr = c.iterator(); + + // Get the entries just before and after index. If index is at the start + // of the list, BEFORE is null. If index is at the end of the list, AFTER + // is null. If the list is empty, both are null. + Entry after = null; + Entry before = null; + if (index != size) + { + after = getEntry(index); + before = after.previous; + } + else + before = last; + + // Create the first new entry. We do not yet set the link from `before' + // to the first entry, in order to deal with the case where (c == this). + // [Actually, we don't have to handle this case to fufill the + // contract for addAll(), but Sun's implementation appears to.] + Entry e = new Entry(itr.next()); + e.previous = before; + Entry prev = e; + Entry firstNew = e; + + // Create and link all the remaining entries. + for (int pos = 1; pos < csize; pos++) + { + e = new Entry(itr.next()); + e.previous = prev; + prev.next = e; + prev = e; + } + + // Link the new chain of entries into the list. + modCount++; + size += csize; + prev.next = after; + if (after != null) + after.previous = e; + else + last = e; + + if (before != null) + before.next = firstNew; + else + first = firstNew; + return true; + } + + /** + * Remove all elements from this list. + */ + public void clear() + { + if (size > 0) + { + modCount++; + first = null; + last = null; + size = 0; + } + } + + /** + * Return the element at index. + * + * @param index the place to look + * @return the element at index + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + checkBoundsExclusive(index); + return getEntry(index).data; + } + + /** + * Replace the element at the given location in the list. + * + * @param index which index to change + * @param o the new element + * @return the prior element + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object set(int index, Object o) + { + checkBoundsExclusive(index); + Entry e = getEntry(index); + Object old = e.data; + e.data = o; + return old; + } + + /** + * Inserts an element in the given position in the list. + * + * @param index where to insert the element + * @param o the element to insert + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, Object o) + { + checkBoundsInclusive(index); + Entry e = new Entry(o); + + if (index < size) + { + modCount++; + Entry after = getEntry(index); + e.next = after; + e.previous = after.previous; + if (after.previous == null) + first = e; + else + after.previous.next = e; + after.previous = e; + size++; + } + else + addLastEntry(e); + } + + /** + * Removes the element at the given position from the list. + * + * @param index the location of the element to remove + * @return the removed element + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public Object remove(int index) + { + checkBoundsExclusive(index); + Entry e = getEntry(index); + removeEntry(e); + return e.data; + } + + /** + * Returns the first index where the element is located in the list, or -1. + * + * @param o the element to look for + * @return its position, or -1 if not found + */ + public int indexOf(Object o) + { + int index = 0; + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + return index; + index++; + e = e.next; + } + return -1; + } + + /** + * Returns the last index where the element is located in the list, or -1. + * + * @param o the element to look for + * @return its position, or -1 if not found + */ + public int lastIndexOf(Object o) + { + int index = size - 1; + Entry e = last; + while (e != null) + { + if (equals(o, e.data)) + return index; + index--; + e = e.previous; + } + return -1; + } + + /** + * Obtain a ListIterator over this list, starting at a given index. The + * ListIterator returned by this method supports the add, remove and set + * methods. + * + * @param index the index of the element to be returned by the first call to + * next(), or size() to be initially positioned at the end of the list + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + checkBoundsInclusive(index); + return new LinkedListItr(index); + } + + /** + * Create a shallow copy of this LinkedList (the elements are not cloned). + * + * @return an object of the same class as this object, containing the + * same elements in the same order + */ + public Object clone() + { + LinkedList copy = null; + try + { + copy = (LinkedList) super.clone(); + } + catch (CloneNotSupportedException ex) + { + } + copy.clear(); + copy.addAll(this); + return copy; + } + + /** + * Returns an array which contains the elements of the list in order. + * + * @return an array containing the list elements + */ + public Object[] toArray() + { + Object[] array = new Object[size]; + Entry e = first; + for (int i = 0; i < size; i++) + { + array[i] = e.data; + e = e.next; + } + return array; + } + + /** + * Returns an Array whose component type is the runtime component type of + * the passed-in Array. The returned Array is populated with all of the + * elements in this LinkedList. If the passed-in Array is not large enough + * to store all of the elements in this List, a new Array will be created + * and returned; if the passed-in Array is <i>larger</i> than the size + * of this List, then size() index will be set to null. + * + * @param a the passed-in Array + * @return an array representation of this list + * @throws ArrayStoreException if the runtime type of a does not allow + * an element in this list + * @throws NullPointerException if a is null + */ + public Object[] toArray(Object[] a) + { + if (a.length < size) + a = (Object[]) Array.newInstance(a.getClass().getComponentType(), size); + else if (a.length > size) + a[size] = null; + Entry e = first; + for (int i = 0; i < size; i++) + { + a[i] = e.data; + e = e.next; + } + return a; + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the size of the list (int), followed by all the elements + * (Object) in proper order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeInt(size); + Entry e = first; + while (e != null) + { + s.writeObject(e.data); + e = e.next; + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the size of the list (int), followed by all the elements + * (Object) in proper order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int i = s.readInt(); + while (--i >= 0) + addLastEntry(new Entry(s.readObject())); + } + + /** + * A ListIterator over the list. This class keeps track of its + * position in the list and the two list entries it is between. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class LinkedListItr implements ListIterator + { + /** Number of modifications we know about. */ + private int knownMod = modCount; + + /** Entry that will be returned by next(). */ + private Entry next; + + /** Entry that will be returned by previous(). */ + private Entry previous; + + /** Entry that will be affected by remove() or set(). */ + private Entry lastReturned; + + /** Index of `next'. */ + private int position; + + /** + * Initialize the iterator. + * + * @param index the initial index + */ + LinkedListItr(int index) + { + if (index == size) + { + next = null; + previous = last; + } + else + { + next = getEntry(index); + previous = next.previous; + } + position = index; + } + + /** + * Checks for iterator consistency. + * + * @throws ConcurrentModificationException if the list was modified + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Returns the index of the next element. + * + * @return the next index + * @throws ConcurrentModificationException if the list was modified + */ + public int nextIndex() + { + checkMod(); + return position; + } + + /** + * Returns the index of the previous element. + * + * @return the previous index + * @throws ConcurrentModificationException if the list was modified + */ + public int previousIndex() + { + checkMod(); + return position - 1; + } + + /** + * Returns true if more elements exist via next. + * + * @return true if next will succeed + * @throws ConcurrentModificationException if the list was modified + */ + public boolean hasNext() + { + checkMod(); + return (next != null); + } + + /** + * Returns true if more elements exist via previous. + * + * @return true if previous will succeed + * @throws ConcurrentModificationException if the list was modified + */ + public boolean hasPrevious() + { + checkMod(); + return (previous != null); + } + + /** + * Returns the next element. + * + * @return the next element + * @throws ConcurrentModificationException if the list was modified + * @throws NoSuchElementException if there is no next + */ + public Object next() + { + checkMod(); + if (next == null) + throw new NoSuchElementException(); + position++; + lastReturned = previous = next; + next = lastReturned.next; + return lastReturned.data; + } + + /** + * Returns the previous element. + * + * @return the previous element + * @throws ConcurrentModificationException if the list was modified + * @throws NoSuchElementException if there is no previous + */ + public Object previous() + { + checkMod(); + if (previous == null) + throw new NoSuchElementException(); + position--; + lastReturned = next = previous; + previous = lastReturned.previous; + return lastReturned.data; + } + + /** + * Remove the most recently returned element from the list. + * + * @throws ConcurrentModificationException if the list was modified + * @throws IllegalStateException if there was no last element + */ + public void remove() + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + + // Adjust the position to before the removed element, if the element + // being removed is behind the cursor. + if (lastReturned == previous) + position--; + + next = lastReturned.next; + previous = lastReturned.previous; + removeEntry(lastReturned); + knownMod++; + + lastReturned = null; + } + + /** + * Adds an element between the previous and next, and advance to the next. + * + * @param o the element to add + * @throws ConcurrentModificationException if the list was modified + */ + public void add(Object o) + { + checkMod(); + modCount++; + knownMod++; + size++; + position++; + Entry e = new Entry(o); + e.previous = previous; + e.next = next; + + if (previous != null) + previous.next = e; + else + first = e; + + if (next != null) + next.previous = e; + else + last = e; + + previous = e; + lastReturned = null; + } + + /** + * Changes the contents of the element most recently returned. + * + * @param o the new element + * @throws ConcurrentModificationException if the list was modified + * @throws IllegalStateException if there was no last element + */ + public void set(Object o) + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + lastReturned.data = o; + } + } // class LinkedListItr +} diff --git a/libjava/classpath/java/util/List.java b/libjava/classpath/java/util/List.java new file mode 100644 index 00000000000..445811292ac --- /dev/null +++ b/libjava/classpath/java/util/List.java @@ -0,0 +1,451 @@ +/* List.java -- An ordered collection which allows indexed access + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An ordered collection (also known as a list). This collection allows + * access to elements by position, as well as control on where elements + * are inserted. Unlike sets, duplicate elements are permitted by this + * general contract (if a subclass forbids duplicates, this should be + * documented). + * <p> + * + * List places additional requirements on <code>iterator</code>, + * <code>add</code>, <code>remove</code>, <code>equals</code>, and + * <code>hashCode</code>, in addition to requiring more methods. List + * indexing is 0-based (like arrays), although some implementations may + * require time proportional to the index to obtain an arbitrary element. + * The List interface is incompatible with Set; you cannot implement both + * simultaneously. + * <p> + * + * Lists also provide a <code>ListIterator</code> which allows bidirectional + * traversal and other features atop regular iterators. Lists can be + * searched for arbitrary elements, and allow easy insertion and removal + * of multiple elements in one method call. + * <p> + * + * Note: While lists may contain themselves as elements, this leads to + * undefined (usually infinite recursive) behavior for some methods like + * hashCode or equals. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see ArrayList + * @see LinkedList + * @see Vector + * @see Arrays#asList(Object[]) + * @see Collections#nCopies(int, Object) + * @see Collections#EMPTY_LIST + * @see AbstractList + * @see AbstractSequentialList + * @since 1.2 + * @status updated to 1.4 + */ +public interface List extends Collection +{ + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + void add(int index, Object o); + + /** + * Add an element to the end of the list (optional operation). If the list + * imposes restraints on what can be inserted, such as no null elements, + * this should be documented. + * + * @param o the object to add + * @return true, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + boolean add(Object o); + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if some element of c is null and this list + * doesn't support the addition of null values. + * @throws NullPointerException if the specified collection is null + * @see #add(int, Object) + */ + boolean addAll(int index, Collection c); + + /** + * Add the contents of a collection to the end of the list (optional + * operation). This operation is undefined if this list is modified + * during the operation (for example, if you try to insert a list into + * itself). + * + * @param c the collection to add + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @throws NullPointerException if some element of c is null and this list + * doesn't support the addition of null values. + * @see #add(Object) + */ + boolean addAll(Collection c); + + /** + * Clear the list, such that a subsequent call to isEmpty() would return + * true (optional operation). + * + * @throws UnsupportedOperationException if this list does not support the + * clear operation + */ + void clear(); + + /** + * Test whether this list contains a given object as one of its elements. + * This is defined as the existence of an element e such that + * <code>o == null ? e == null : o.equals(e)</code>. + * + * @param o the element to look for + * @return true if this list contains the element + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws NullPointerException if o is null and the list doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Test whether this list contains every element in a given collection. + * + * @param c the collection to test for + * @return true if for every element o in c, contains(o) would return true + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support null values. + * @see #contains(Object) + */ + boolean containsAll(Collection c); + + /** + * Test whether this list is equal to another object. A List is defined to be + * equal to an object if and only if that object is also a List, and the two + * lists have the same sequence. Two lists l1 and l2 are equal if and only + * if <code>l1.size() == l2.size()</code>, and for every integer n between 0 + * and <code>l1.size() - 1</code> inclusive, <code>l1.get(n) == null ? + * l2.get(n) == null : l1.get(n).equals(l2.get(n))</code>. + * + * @param o the object to test for equality with this list + * @return true if o is equal to this list + * @see Object#equals(Object) + * @see #hashCode() + */ + boolean equals(Object o); + + /** + * Get the element at a given index in this list. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + Object get(int index); + + /** + * Obtains a hash code for this list. In order to obey the general + * contract of the hashCode method of class Object, this value is + * calculated as follows: + * +<p><pre>hashCode = 1; +Iterator i = list.iterator(); +while (i.hasNext()) +{ + Object obj = i.next(); + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); +}</pre> + * + * <p>This ensures that the general contract of Object.hashCode() + * is adhered to. + * + * @return the hash code of this list + * @see Object#hashCode() + * @see #equals(Object) + */ + int hashCode(); + + /** + * Obtain the first index at which a given object is to be found in this + * list. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + int indexOf(Object o); + + /** + * Test whether this list is empty, that is, if size() == 0. + * + * @return true if this list contains no elements + */ + boolean isEmpty(); + + /** + * Obtain an Iterator over this list, whose sequence is the list order. + * + * @return an Iterator over the elements of this list, in order + */ + Iterator iterator(); + + /** + * Obtain the last index at which a given object is to be found in this + * list. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + int lastIndexOf(Object o); + + /** + * Obtain a ListIterator over this list, starting at the beginning. + * + * @return a ListIterator over the elements of this list, in order, starting + * at the beginning + */ + ListIterator listIterator(); + + /** + * Obtain a ListIterator over this list, starting at a given position. + * A first call to next() would return the same as get(index), and a + * first call to previous() would return the same as get(index - 1). + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return a ListIterator over the elements of this list, in order, starting + * at index + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + ListIterator listIterator(int index); + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + Object remove(int index); + + /** + * Remove the first occurence of an object from this list (optional + * operation). That is, remove the first element e such that + * <code>o == null ? e == null : o.equals(e)</code>. + * + * @param o the object to remove + * @return true if the list changed as a result of this call, that is, if + * the list contained at least one occurrence of o + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support removing null values. + */ + boolean remove(Object o); + + /** + * Remove all elements of a given collection from this list (optional + * operation). That is, remove every element e such that c.contains(e). + * + * @param c the collection to filter out + * @return true if this list was modified as a result of this call + * @throws UnsupportedOperationException if this list does not support the + * removeAll operation + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support removing null values. + * @see #remove(Object) + * @see #contains(Object) + */ + boolean removeAll(Collection c); + + /** + * Remove all elements of this list that are not contained in a given + * collection (optional operation). That is, remove every element e such + * that !c.contains(e). + * + * @param c the collection to retain + * @return true if this list was modified as a result of this call + * @throws UnsupportedOperationException if this list does not support the + * retainAll operation + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support retaining null values. + * @see #remove(Object) + * @see #contains(Object) + */ + boolean retainAll(Collection c); + + /** + * Replace an element of this list with another object (optional operation). + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + Object set(int index, Object o); + + /** + * Get the number of elements in this list. If the list contains more + * than Integer.MAX_VALUE elements, return Integer.MAX_VALUE. + * + * @return the number of elements in the list + */ + int size(); + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + List subList(int fromIndex, int toIndex); + + /** + * Copy the current contents of this list into an array. + * + * @return an array of type Object[] and length equal to the length of this + * list, containing the elements currently in this list, in order + */ + Object[] toArray(); + + /** + * Copy the current contents of this list into an array. If the array passed + * as an argument has length less than that of this list, an array of the + * same run-time type as a, and length equal to the length of this list, is + * allocated using Reflection. Otherwise, a itself is used. The elements of + * this list are copied into it, and if there is space in the array, the + * following element is set to null. The resultant array is returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this list does not contain any null elements. + * + * @param a the array to copy this list into + * @return an array containing the elements currently in this list, in + * order + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a + * @throws NullPointerException if the specified array is null + */ + Object[] toArray(Object[] a); +} diff --git a/libjava/classpath/java/util/ListIterator.java b/libjava/classpath/java/util/ListIterator.java new file mode 100644 index 00000000000..5e17108c991 --- /dev/null +++ b/libjava/classpath/java/util/ListIterator.java @@ -0,0 +1,170 @@ +/* ListIterator.java -- Extended Iterator for iterating over ordered lists + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An extended version of Iterator to support the extra features of Lists. The + * elements may be accessed in forward or reverse order, elements may be + * replaced as well as removed, and new elements may be inserted, during the + * traversal of the list. + * <p> + * + * A list with n elements provides n+1 iterator positions (the front, the end, + * or between two elements). Note that <code>remove</code> and <code>set</code> + * operate on the last element returned, whether it was by <code>next</code> + * or <code>previous</code>. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see Iterator + * @see Enumeration + * @since 1.2 + * @status updated to 1.4 + */ +public interface ListIterator extends Iterator +{ + /** + * Tests whether there are elements remaining in the list in the forward + * direction. In other words, next() will not fail with a + * NoSuchElementException. + * + * @return true if the list continues in the forward direction + */ + boolean hasNext(); + + /** + * Tests whether there are elements remaining in the list in the reverse + * direction. In other words, previous() will not fail with a + * NoSuchElementException. + * + * @return true if the list continues in the reverse direction + */ + boolean hasPrevious(); + + /** + * Obtain the next element in the list in the forward direction. Repeated + * calls to next may be used to iterate over the entire list, or calls to + * next and previous may be used together to go forwards and backwards. + * Alternating calls to next and previous will return the same element. + * + * @return the next element in the list in the forward direction + * @throws NoSuchElementException if there are no more elements + */ + Object next(); + + /** + * Obtain the next element in the list in the reverse direction. Repeated + * calls to previous may be used to iterate backwards over the entire list, + * or calls to next and previous may be used together to go forwards and + * backwards. Alternating calls to next and previous will return the same + * element. + * + * @return the next element in the list in the reverse direction + * @throws NoSuchElementException if there are no more elements + */ + Object previous(); + + /** + * Find the index of the element that would be returned by a call to next. + * If hasNext() returns false, this returns the list size. + * + * @return the index of the element that would be returned by next() + */ + int nextIndex(); + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns false, this returns -1. + * + * @return the index of the element that would be returned by previous() + */ + int previousIndex(); + + /** + * Insert an element into the list at the current position of the iterator + * (optional operation). The element is inserted in between the element that + * would be returned by previous and the element that would be returned by + * next. After the insertion, a subsequent call to next is unaffected, but + * a call to previous returns the item that was added. The values returned + * by nextIndex() and previousIndex() are incremented. + * + * @param o the object to insert into the list + * @throws ClassCastException if the object is of a type which cannot be added + * to this list. + * @throws IllegalArgumentException if some other aspect of the object stops + * it being added to this list. + * @throws UnsupportedOperationException if this ListIterator does not + * support the add operation. + */ + void add(Object o); + + /** + * Remove from the list the element last returned by a call to next or + * previous (optional operation). This method may only be called if neither + * add nor remove have been called since the last call to next or previous. + * + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the remove operation + */ + void remove(); + + /** + * Replace the element last returned by a call to next or previous with a + * given object (optional operation). This method may only be called if + * neither add nor remove have been called since the last call to next or + * previous. + * + * @param o the object to replace the element with + * @throws ClassCastException the object is of a type which cannot be added + * to this list + * @throws IllegalArgumentException some other aspect of the object stops + * it being added to this list + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the set operation + */ + void set(Object o); +} diff --git a/libjava/classpath/java/util/ListResourceBundle.java b/libjava/classpath/java/util/ListResourceBundle.java new file mode 100644 index 00000000000..2bc51c3b013 --- /dev/null +++ b/libjava/classpath/java/util/ListResourceBundle.java @@ -0,0 +1,140 @@ +/* ListResourceBundle -- a resource bundle build around a list + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A <code>ListResouceBundle</code> provides an easy way, to create your own + * resource bundle. It is an abstract class that you can subclass. You should + * then overwrite the getContents method, that provides a key/value list. + * + * <p>The key/value list is a two dimensional list of Object. The first + * dimension ranges over the resources. The second dimension ranges from + * zero (key) to one (value). The keys must be of type String, and they are + * case-sensitive. For example: + * +<br><pre>public class MyResources + extends ListResourceBundle +{ + public Object[][] getContents() + { + return contents; + } + + static final Object[][] contents = + { + // LOCALIZED STRINGS + {"s1", "The disk \"{1}\" contains {0}."}, // MessageFormat pattern + {"s2", "1"}, // location of {0} in pattern + {"s3", "My Disk"}, // sample disk name + {"s4", "no files"}, // first ChoiceFormat choice + {"s5", "one file"}, // second ChoiceFormat choice + {"s6", "{0,number} files"} // third ChoiceFormat choice + {"s7", "3 Mar 96"}, // sample date + {"s8", new Dimension(1,5)} // real object, not just string + // END OF LOCALIZED MATERIAL + }; +}</pre> + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see Locale + * @see PropertyResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ListResourceBundle extends ResourceBundle +{ + /** + * The constructor. It does nothing special. + */ + public ListResourceBundle() + { + } + + /** + * Gets a resource for a given key. This is called by <code>getObject</code>. + * + * @param key the key of the resource + * @return the resource for the key, or null if it doesn't exist + */ + public final Object handleGetObject(String key) + { + Object[][] contents = getContents(); + int i = contents.length; + while (--i >= 0) + if (key.equals(contents[i][0])) + return contents[i][1]; + return null; + } + + /** + * This method should return all keys for which a resource exists. + * + * @return an enumeration of the keys + */ + public Enumeration getKeys() + { + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + final Object[][] contents = getContents(); + Set s = new HashSet(); + int i = contents.length; + while (--i >= 0) + s.add(contents[i][0]); + ResourceBundle bundle = parent; + // Eliminate tail recursion. + while (bundle != null) + { + Enumeration e = bundle.getKeys(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + bundle = bundle.parent; + } + return Collections.enumeration(s); + } + + /** + * Gets the key/value list. You must override this method, and should not + * provide duplicate keys or null entries. + * + * @return a two dimensional list of String key / Object resouce pairs + */ + protected abstract Object[][] getContents(); +} // class ListResourceBundle diff --git a/libjava/classpath/java/util/Locale.java b/libjava/classpath/java/util/Locale.java new file mode 100644 index 00000000000..6d3f8463252 --- /dev/null +++ b/libjava/classpath/java/util/Locale.java @@ -0,0 +1,952 @@ +/* Locale.java -- i18n locales + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.classpath.SystemProperties; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * Locales represent a specific country and culture. Classes which can be + * passed a Locale object tailor their information for a given locale. For + * instance, currency number formatting is handled differently for the USA + * and France. + * + * <p>Locales are made up of a language code, a country code, and an optional + * set of variant strings. Language codes are represented by + * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt"> + * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989 + * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997. + * + * <p>Country codes are represented by + * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html"> + * ISO 3166</a>. Variant strings are vendor and browser specific. Standard + * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and + * "MAC" for Macintosh. When there is more than one variant string, they must + * be separated by an underscore (U+005F). + * + * <p>The default locale is determined by the values of the system properties + * user.language, user.region, and user.variant, defaulting to "en". Note that + * the locale does NOT contain the conversion and formatting capabilities (for + * that, use ResourceBundle and java.text). Rather, it is an immutable tag + * object for identifying a given locale, which is referenced by these other + * classes when they must make locale-dependent decisions. + * + * @see ResourceBundle + * @see java.text.Format + * @see java.text.NumberFormat + * @see java.text.Collator + * @author Jochen Hoenicke + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + * @status updated to 1.4 + */ +public final class Locale implements Serializable, Cloneable +{ + /** Locale which represents the English language. */ + public static final Locale ENGLISH = getLocale("en"); + + /** Locale which represents the French language. */ + public static final Locale FRENCH = getLocale("fr"); + + /** Locale which represents the German language. */ + public static final Locale GERMAN = getLocale("de"); + + /** Locale which represents the Italian language. */ + public static final Locale ITALIAN = getLocale("it"); + + /** Locale which represents the Japanese language. */ + public static final Locale JAPANESE = getLocale("ja"); + + /** Locale which represents the Korean language. */ + public static final Locale KOREAN = getLocale("ko"); + + /** Locale which represents the Chinese language. */ + public static final Locale CHINESE = getLocale("zh"); + + /** Locale which represents the Chinese language as used in China. */ + public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN"); + + /** + * Locale which represents the Chinese language as used in Taiwan. + * Same as TAIWAN Locale. + */ + public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW"); + + /** Locale which represents France. */ + public static final Locale FRANCE = getLocale("fr", "FR"); + + /** Locale which represents Germany. */ + public static final Locale GERMANY = getLocale("de", "DE"); + + /** Locale which represents Italy. */ + public static final Locale ITALY = getLocale("it", "IT"); + + /** Locale which represents Japan. */ + public static final Locale JAPAN = getLocale("ja", "JP"); + + /** Locale which represents Korea. */ + public static final Locale KOREA = getLocale("ko", "KR"); + + /** + * Locale which represents China. + * Same as SIMPLIFIED_CHINESE Locale. + */ + public static final Locale CHINA = SIMPLIFIED_CHINESE; + + /** + * Locale which represents the People's Republic of China. + * Same as CHINA Locale. + */ + public static final Locale PRC = CHINA; + + /** + * Locale which represents Taiwan. + * Same as TRADITIONAL_CHINESE Locale. + */ + public static final Locale TAIWAN = TRADITIONAL_CHINESE; + + /** Locale which represents the United Kingdom. */ + public static final Locale UK = getLocale("en", "GB"); + + /** Locale which represents the United States. */ + public static final Locale US = getLocale("en", "US"); + + /** Locale which represents the English speaking portion of Canada. */ + public static final Locale CANADA = getLocale("en", "CA"); + + /** Locale which represents the French speaking portion of Canada. */ + public static final Locale CANADA_FRENCH = getLocale("fr", "CA"); + + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 9149081749638150636L; + + /** + * The language code, as returned by getLanguage(). + * + * @serial the languange, possibly "" + */ + private String language; + + /** + * The country code, as returned by getCountry(). + * + * @serial the country, possibly "" + */ + private String country; + + /** + * The variant code, as returned by getVariant(). + * + * @serial the variant, possibly "" + */ + private String variant; + + /** + * This is the cached hashcode. When writing to stream, we write -1. + * + * @serial should be -1 in serial streams + */ + private transient int hashcode; + + /** + * Array storing all available locales. + */ + private static transient Locale[] availableLocales; + + /** + * Locale cache. Only created locale objects are stored. + * Contains all supported locales when getAvailableLocales() + * got called. + */ + private static transient HashMap localeMap; + + /** + * The default locale. Except for during bootstrapping, this should never be + * null. Note the logic in the main constructor, to detect when + * bootstrapping has completed. + */ + private static Locale defaultLocale = + getLocale(SystemProperties.getProperty("user.language", "en"), + SystemProperties.getProperty("user.region", ""), + SystemProperties.getProperty("user.variant", "")); + + /** + * Array storing all the available two-letter ISO639 languages. + */ + private static transient String[] languageCache; + + /** + * Array storing all the available two-letter ISO3166 country codes. + */ + private static transient String[] countryCache; + + /** + * Retrieves the locale with the specified language from the cache. + * + * @param language the language of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language) + { + return getLocale(language, "", ""); + } + + /** + * Retrieves the locale with the specified language and region + * from the cache. + * + * @param language the language of the locale to retrieve. + * @param region the region of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language, String region) + { + return getLocale(language, region, ""); + } + + /** + * Retrieves the locale with the specified language, region + * and variant from the cache. + * + * @param language the language of the locale to retrieve. + * @param region the region of the locale to retrieve. + * @param variant the variant of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language, String region, String variant) + { + if (localeMap == null) + localeMap = new HashMap(256); + + String name = language + "_" + region + "_" + variant; + Locale locale = (Locale) localeMap.get(name); + + if (locale == null) + { + locale = new Locale(language, region, variant); + localeMap.put(name, locale); + } + + return locale; + } + + /** + * Convert new iso639 codes to the old ones. + * + * @param language the language to check + * @return the appropriate code + */ + private String convertLanguage(String language) + { + if (language.equals("")) + return language; + language = language.toLowerCase(); + int index = "he,id,yi".indexOf(language); + if (index != -1) + return "iw,in,ji".substring(index, index + 2); + return language; + } + + /** + * Creates a new locale for the given language and country. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @param country uppercase two-letter ISO-3166 A2 contry code + * @param variant vendor and browser specific + * @throws NullPointerException if any argument is null + */ + public Locale(String language, String country, String variant) + { + // During bootstrap, we already know the strings being passed in are + // the correct capitalization, and not null. We can't call + // String.toUpperCase during this time, since that depends on the + // default locale. + if (defaultLocale != null) + { + language = convertLanguage(language).intern(); + country = country.toUpperCase().intern(); + variant = variant.intern(); + } + this.language = language; + this.country = country; + this.variant = variant; + hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + } + + /** + * Creates a new locale for the given language and country. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @param country uppercase two-letter ISO-3166 A2 country code + * @throws NullPointerException if either argument is null + */ + public Locale(String language, String country) + { + this(language, country, ""); + } + + /** + * Creates a new locale for a language. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @throws NullPointerException if either argument is null + * @since 1.4 + */ + public Locale(String language) + { + this(language, "", ""); + } + + /** + * Returns the default Locale. The default locale is generally once set + * on start up and then never changed. Normally you should use this locale + * for everywhere you need a locale. The initial setting matches the + * default locale, the user has chosen. + * + * @return the default locale for this virtual machine + */ + public static Locale getDefault() + { + return defaultLocale; + } + + /** + * Changes the default locale. Normally only called on program start up. + * Note that this doesn't change the locale for other programs. This has + * a security check, + * <code>PropertyPermission("user.language", "write")</code>, because of + * its potential impact to running code. + * + * @param newLocale the new default locale + * @throws NullPointerException if newLocale is null + * @throws SecurityException if permission is denied + */ + public static void setDefault(Locale newLocale) + { + if (newLocale == null) + throw new NullPointerException(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new PropertyPermission("user.language", "write")); + defaultLocale = newLocale; + } + + /** + * Returns the list of available locales. + * + * @return the installed locales + */ + public static synchronized Locale[] getAvailableLocales() + { + if (availableLocales == null) + { + String[] localeNames = LocaleData.localeNames; + availableLocales = new Locale[localeNames.length]; + + for (int i = 0; i < localeNames.length; i++) + { + String language; + String region = ""; + String variant = ""; + String name = localeNames[i]; + + language = name.substring(0, 2); + + if (name.length() > 2) + region = name.substring(3); + + int index = region.indexOf("_"); + if (index > 0) + { + variant = region.substring(index + 1); + region = region.substring(0, index - 1); + } + + availableLocales[i] = getLocale(language, region, variant); + } + } + + return availableLocales; + } + + /** + * Returns a list of all 2-letter uppercase country codes as defined + * in ISO 3166. + * + * @return a list of acceptable country codes + */ + public static String[] getISOCountries() + { + if (countryCache == null) + { + countryCache = getISOStrings("territories"); + } + + return countryCache; + } + + /** + * Returns a list of all 2-letter lowercase language codes as defined + * in ISO 639 (both old and new variant). + * + * @return a list of acceptable language codes + */ + public static String[] getISOLanguages() + { + if (languageCache == null) + { + languageCache = getISOStrings("languages"); + } + return languageCache; + } + + /** + * Returns the set of keys from the specified resource hashtable, filtered + * so that only two letter strings are returned. + * + * @param tableName the name of the table from which to retrieve the keys. + * @return an array of two-letter strings. + */ + private static String[] getISOStrings(String tableName) + { + int count = 0; + ResourceBundle bundle = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation"); + Enumeration e = bundle.getKeys(); + ArrayList tempList = new ArrayList(); + + while (e.hasMoreElements()) + { + String key = (String) e.nextElement(); + + if (key.startsWith(tableName + ".")) + { + String str = key.substring(tableName.length() + 1); + + if (str.length() == 2 + && Character.isLetter(str.charAt(0)) + && Character.isLetter(str.charAt(1))) + { + tempList.add(str); + ++count; + } + } + } + + String[] strings = new String[count]; + + for (int a = 0; a < count; ++a) + strings[a] = (String) tempList.get(a); + + return strings; + } + + /** + * Returns the language code of this locale. Some language codes have changed + * as ISO 639 has evolved; this returns the old name, even if you built + * the locale with the new one. + * + * @return language code portion of this locale, or an empty String + */ + public String getLanguage() + { + return language; + } + + /** + * Returns the country code of this locale. + * + * @return country code portion of this locale, or an empty String + */ + public String getCountry() + { + return country; + } + + /** + * Returns the variant code of this locale. + * + * @return the variant code portion of this locale, or an empty String + */ + public String getVariant() + { + return variant; + } + + /** + * Gets the string representation of the current locale. This consists of + * the language, the country, and the variant, separated by an underscore. + * The variant is listed only if there is a language or country. Examples: + * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC". + * + * @return the string representation of this Locale + * @see #getDisplayName() + */ + public String toString() + { + if (language.length() == 0 && country.length() == 0) + return ""; + else if (country.length() == 0 && variant.length() == 0) + return language; + StringBuffer result = new StringBuffer(language); + result.append('_').append(country); + if (variant.length() != 0) + result.append('_').append(variant); + return result.toString(); + } + + /** + * Returns the three-letter ISO language abbrevation of this locale. + * + * @throws MissingResourceException if the three-letter code is not known + */ + public String getISO3Language() + { + // We know all strings are interned so we can use '==' for better performance. + if (language == "") + return ""; + int index + = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da," + + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw," + + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn," + + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne," + + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk," + + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr," + + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu") + .indexOf(language); + + if (index % 3 != 0 || language.length() != 2) + throw new MissingResourceException + ("Can't find ISO3 language for " + language, + "java.util.Locale", language); + + // Don't read this aloud. These are the three letter language codes. + return + ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu" + + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv" + + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur" + + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm" + + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi" + + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd" + + "uzbvievolwolxhoyidyorzhazhozul") + .substring(index, index + 3); + } + + /** + * Returns the three-letter ISO country abbrevation of the locale. + * + * @throws MissingResourceException if the three-letter code is not known + */ + public String getISO3Country() + { + // We know all strings are interned so we can use '==' for better performance. + if (country == "") + return ""; + int index + = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF," + + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK," + + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER," + + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ," + + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT," + + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS," + + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV," + + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG," + + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH," + + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN," + + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF," + + "WS,YE,YT,YU,ZA,ZM,ZR,ZW") + .indexOf(country); + + if (index % 3 != 0 || country.length() != 2) + throw new MissingResourceException + ("Can't find ISO3 country for " + country, + "java.util.Locale", country); + + // Don't read this aloud. These are the three letter country codes. + return + ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR" + + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR" + + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN" + + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM" + + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ" + + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO" + + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER" + + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW" + + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP" + + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI" + + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE") + .substring(index, index + 3); + } + + /** + * Gets the country name suitable for display to the user, formatted + * for the default locale. This has the same effect as + * <pre> + * getDisplayLanguage(Locale.getDefault()); + * </pre> + * + * @return the language name of this locale localized to the default locale, + * with the ISO code as backup + */ + public String getDisplayLanguage() + { + return getDisplayLanguage(defaultLocale); + } + + /** + * <p> + * Gets the name of the language specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is + * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>, + * the result would be 'German'. Using the German locale would instead give + * 'Deutsch'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + * </p> + * <ul> + * <li>the display name in the default locale</li> + * <li>the display name in English</li> + * <li>the ISO code</li> + * </ul> + * <p> + * If the language is unspecified by this locale, then the empty string is + * returned. + * </p> + * + * @param inLocale the locale to use for formatting the display string. + * @return the language name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayLanguage(Locale inLocale) + { + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("languages." + language); + } + catch (MissingResourceException e) + { + return language; + } + } + + /** + * Returns the country name of this locale localized to the + * default locale. If the localized is not found, the ISO code + * is returned. This has the same effect as + * <pre> + * getDisplayCountry(Locale.getDefault()); + * </pre> + * + * @return the country name of this locale localized to the given locale, + * with the ISO code as backup + */ + public String getDisplayCountry() + { + return getDisplayCountry(defaultLocale); + } + + /** + * <p> + * Gets the name of the country specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is + * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>, + * the result would be 'Germany'. Using the German locale would instead give + * 'Deutschland'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + * </p> + * <ul> + * <li>the display name in the default locale</li> + * <li>the display name in English</li> + * <li>the ISO code</li> + * </ul> + * <p> + * If the country is unspecified by this locale, then the empty string is + * returned. + * </p> + * + * @param inLocale the locale to use for formatting the display string. + * @return the country name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayCountry(Locale inLocale) + { + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("territories." + country); + } + catch (MissingResourceException e) + { + return country; + } + } + + /** + * Returns the variant name of this locale localized to the + * default locale. If the localized is not found, the variant code + * itself is returned. This has the same effect as + * <pre> + * getDisplayVariant(Locale.getDefault()); + * </pre> + * + * @return the variant code of this locale localized to the given locale, + * with the ISO code as backup + */ + public String getDisplayVariant() + { + return getDisplayVariant(defaultLocale); + } + + + /** + * <p> + * Gets the name of the variant specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is a revised + * variant, and the specified locale is <code>Locale.UK</code>, the result + * would be 'REVISED'. Using the German locale would instead give + * 'Revidiert'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + * </p> + * <ul> + * <li>the display name in the default locale</li> + * <li>the display name in English</li> + * <li>the ISO code</li> + * </ul> + * <p> + * If the variant is unspecified by this locale, then the empty string is + * returned. + * </p> + * + * @param inLocale the locale to use for formatting the display string. + * @return the variant name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayVariant(Locale inLocale) + { + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("variants." + variant); + } + catch (MissingResourceException e) + { + return variant; + } + } + + /** + * Gets all local components suitable for display to the user, formatted + * for the default locale. For the language component, getDisplayLanguage + * is called. For the country component, getDisplayCountry is called. + * For the variant set component, getDisplayVariant is called. + * + * <p>The returned String will be one of the following forms:<br> + * <pre> + * language (country, variant) + * language (country) + * language (variant) + * country (variant) + * language + * country + * variant + * </pre> + * + * @return String version of this locale, suitable for display to the user + */ + public String getDisplayName() + { + return getDisplayName(defaultLocale); + } + + /** + * Gets all local components suitable for display to the user, formatted + * for a specified locale. For the language component, + * getDisplayLanguage(Locale) is called. For the country component, + * getDisplayCountry(Locale) is called. For the variant set component, + * getDisplayVariant(Locale) is called. + * + * <p>The returned String will be one of the following forms:<br> + * <pre> + * language (country, variant) + * language (country) + * language (variant) + * country (variant) + * language + * country + * variant + * </pre> + * + * @param locale locale to use for formatting + * @return String version of this locale, suitable for display to the user + */ + public String getDisplayName(Locale locale) + { + StringBuffer result = new StringBuffer(); + int count = 0; + String[] delimiters = {"", " (", ","}; + if (language.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayLanguage(locale)); + } + if (country.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayCountry(locale)); + } + if (variant.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayVariant(locale)); + } + if (count > 1) + result.append(")"); + return result.toString(); + } + + /** + * Does the same as <code>Object.clone()</code> but does not throw + * a <code>CloneNotSupportedException</code>. Why anyone would + * use this method is a secret to me, since this class is immutable. + * + * @return the clone + */ + public Object clone() + { + // This class is final, so no need to use native super.clone(). + return new Locale(language, country, variant); + } + + /** + * Return the hash code for this locale. The hashcode is the logical + * xor of the hash codes of the language, the country and the variant. + * The hash code is precomputed, since <code>Locale</code>s are often + * used in hash tables. + * + * @return the hashcode + */ + public int hashCode() + { + return hashcode; + } + + /** + * Compares two locales. To be equal, obj must be a Locale with the same + * language, country, and variant code. + * + * @param obj the other locale + * @return true if obj is equal to this + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof Locale)) + return false; + Locale l = (Locale) obj; + + return (language == l.language + && country == l.country + && variant == l.variant); + } + + /** + * Write the locale to an object stream. + * + * @param output the stream to write to + * @throws IOException if the write fails + * @serialData The first three fields are Strings representing language, + * country, and variant. The fourth field is a placeholder for + * the cached hashcode, but this is always written as -1, and + * recomputed when reading it back. + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.writeObject(language); + s.writeObject(country); + s.writeObject(variant); + // Hashcode field is always written as -1. + s.writeInt(-1); + } + + /** + * Reads a locale from the input stream. + * + * @param input the stream to read from + * @throws IOException if reading fails + * @throws ClassNotFoundException if reading fails + * @serialData the hashCode is always invalid and must be recomputed + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + language = ((String) s.readObject()).intern(); + country = ((String) s.readObject()).intern(); + variant = ((String) s.readObject()).intern(); + // Recompute hashcode. + hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + } +} // class Locale diff --git a/libjava/classpath/java/util/Map.java b/libjava/classpath/java/util/Map.java new file mode 100644 index 00000000000..256e98899f6 --- /dev/null +++ b/libjava/classpath/java/util/Map.java @@ -0,0 +1,338 @@ +/* Map.java: interface Map -- An object that maps keys to values + interface Map.Entry -- an Entry in a Map + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An object that maps keys onto values. Keys cannot be duplicated. This + * interface replaces the obsolete {@link Dictionary} abstract class. + * <p> + * + * The map has three collection views, which are backed by the map + * (modifications on one show up on the other): a set of keys, a collection + * of values, and a set of key-value mappings. Some maps have a guaranteed + * order, but not all do. + * <p> + * + * Note: Be careful about using mutable keys. Behavior is unspecified if + * a key's comparison behavior is changed after the fact. As a corollary + * to this rule, don't use a Map as one of its own keys or values, as it makes + * hashCode and equals have undefined behavior. + * <p> + * + * All maps are recommended to provide a no argument constructor, which builds + * an empty map, and one that accepts a Map parameter and copies the mappings + * (usually by putAll), to create an equivalent map. Unfortunately, Java + * cannot enforce these suggestions. + * <p> + * + * The map may be unmodifiable, in which case unsupported operations will + * throw an UnsupportedOperationException. Note that some operations may be + * safe, such as putAll(m) where m is empty, even if the operation would + * normally fail with a non-empty argument. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see HashMap + * @see TreeMap + * @see Hashtable + * @see SortedMap + * @see Collection + * @see Set + * @since 1.2 + * @status updated to 1.4 + */ +public interface Map +{ + /** + * Remove all entries from this Map (optional operation). + * + * @throws UnsupportedOperationException if clear is not supported + */ + void clear(); + + /** + * Returns true if this contains a mapping for the given key. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys + */ + boolean containsKey(Object key); + + /** + * Returns true if this contains at least one mapping with the given value. + * In other words, returns true if a value v exists where + * <code>(value == null ? v == null : value.equals(v))</code>. This usually + * requires linear time. + * + * @param value the value to search for + * @return true if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + boolean containsValue(Object value); + + /** + * Returns a set view of the mappings in this Map. Each element in the + * set is a Map.Entry. The set is backed by the map, so that changes in + * one show up in the other. Modifications made while an iterator is + * in progress cause undefined behavior. If the set supports removal, + * these methods remove the underlying mapping from the map: + * <code>Iterator.remove</code>, <code>Set.remove</code>, + * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. + * Element addition, via <code>add</code> or <code>addAll</code>, is + * not supported via this set. + * + * @return the set view of all mapping entries + * @see Map.Entry + */ + Set entrySet(); + + /** + * Compares the specified object with this map for equality. Returns + * <code>true</code> if the other object is a Map with the same mappings, + * that is,<br> + * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code> + * This allows comparison of maps, regardless of implementation. + * + * @param o the object to be compared + * @return true if the object equals this map + * @see Set#equals(Object) + */ + boolean equals(Object o); + + /** + * Returns the value mapped by the given key. Returns <code>null</code> if + * there is no mapping. However, in Maps that accept null values, you + * must rely on <code>containsKey</code> to determine if a mapping exists. + * + * @param key the key to look up + * @return the value associated with the key, or null if key not in map + * @throws ClassCastException if the key is an inappropriate type + * @throws NullPointerException if this map does not accept null keys + * @see #containsKey(Object) + */ + Object get(Object key); + + /** + * Associates the given key to the given value (optional operation). If the + * map already contains the key, its value is replaced. Be aware that in + * a map that permits <code>null</code> values, a null return does not + * always imply that the mapping was created. + * + * @param key the key to map + * @param value the value to be mapped + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if either the key or the value is null, + * and the map forbids null keys or values + * @see #containsKey(Object) + */ + Object put(Object key, Object value); + + /** + * Returns the hash code for this map. This is the sum of all hashcodes + * for each Map.Entry object in entrySet. This allows comparison of maps, + * regardless of implementation, and satisfies the contract of + * Object.hashCode. + * + * @return the hash code + * @see Map.Entry#hashCode() + */ + int hashCode(); + + /** + * Returns true if the map contains no mappings. + * + * @return true if the map is empty + */ + boolean isEmpty(); + + /** + * Returns a set view of the keys in this Map. The set is backed by the + * map, so that changes in one show up in the other. Modifications made + * while an iterator is in progress cause undefined behavior. If the set + * supports removal, these methods remove the underlying mapping from + * the map: <code>Iterator.remove</code>, <code>Set.remove</code>, + * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. + * Element addition, via <code>add</code> or <code>addAll</code>, is + * not supported via this set. + * + * @return the set view of all keys + */ + Set keySet(); + + /** + * Copies all entries of the given map to this one (optional operation). If + * the map already contains a key, its value is replaced. + * + * @param m the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if a key or value is of the wrong type + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values, or + * if <code>m</code> is null. + * @see #put(Object, Object) + */ + void putAll(Map m); + + /** + * Removes the mapping for this key if present (optional operation). If + * the key is not present, this returns null. Note that maps which permit + * null values may also return null if the key was removed. + * + * @param key the key to remove + * @return the value the key mapped to, or null if not present. + * @throws UnsupportedOperationException if deletion is unsupported + * @throws NullPointerException if the key is null and this map doesn't + * support null keys. + * @throws ClassCastException if the type of the key is not a valid type + * for this map. + */ + Object remove(Object key); + + /** + * Returns the number of key-value mappings in the map. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. + * + * @return the number of mappings + */ + int size(); + + /** + * Returns a collection (or bag) view of the values in this Map. The + * collection is backed by the map, so that changes in one show up in + * the other. Modifications made while an iterator is in progress cause + * undefined behavior. If the collection supports removal, these methods + * remove the underlying mapping from the map: <code>Iterator.remove</code>, + * <code>Collection.remove</code>, <code>removeAll</code>, + * <code>retainAll</code>, and <code>clear</code>. Element addition, via + * <code>add</code> or <code>addAll</code>, is not supported via this + * collection. + * + * @return the collection view of all values + */ + Collection values(); + + /** + * A map entry (key-value pair). The Map.entrySet() method returns a set + * view of these objects; there is no other valid way to come across them. + * These objects are only valid for the duration of an iteration; in other + * words, if you mess with one after modifying the map, you are asking + * for undefined behavior. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Map#entrySet() + * @since 1.2 + * @status updated to 1.4 + */ + interface Entry + { + /** + * Get the key corresponding to this entry. + * + * @return the key + */ + Object getKey(); + + /** + * Get the value corresponding to this entry. If you already called + * Iterator.remove(), this is undefined. + * + * @return the value + */ + Object getValue(); + + /** + * Replaces the value with the specified object (optional operation). + * This writes through to the map, and is undefined if you already + * called Iterator.remove(). + * + * @param value the new value to store + * @return the old value + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the value is of the wrong type + * @throws IllegalArgumentException if something about the value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null values + */ + Object setValue(Object value); + + + /** + * Returns the hash code of the entry. This is defined as the + * exclusive-or of the hashcodes of the key and value (using 0 for + * <code>null</code>). In other words, this must be: + * +<p><pre>(getKey() == null ? 0 : getKey().hashCode()) +^ (getValue() == null ? 0 : getValue().hashCode())</pre> + * + * @return the hash code + */ + int hashCode(); + + /** + * Compares the specified object with this entry. Returns true only if + * the object is a mapping of identical key and value. In other words, + * this must be: + * +<p><pre>(o instanceof Map.Entry) +&& (getKey() == null ? ((HashMap) o).getKey() == null + : getKey().equals(((HashMap) o).getKey())) +&& (getValue() == null ? ((HashMap) o).getValue() == null + : getValue().equals(((HashMap) o).getValue()))</pre> + * + * @param o the object to compare + * + * @return <code>true</code> if it is equal + */ + boolean equals(Object o); + } +} diff --git a/libjava/classpath/java/util/MissingResourceException.java b/libjava/classpath/java/util/MissingResourceException.java new file mode 100644 index 00000000000..26640de90df --- /dev/null +++ b/libjava/classpath/java/util/MissingResourceException.java @@ -0,0 +1,105 @@ +/* MissingResourceException.java -- thrown for a missing resource + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This exception is thrown when a resource is missing. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see ResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public class MissingResourceException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4876345176062000401L; + + /** + * The name of the resource bundle requested by user. + * + * @serial the class name of the resource bundle + */ + private final String className; + + /** + * The key of the resource in the bundle requested by user. + * + * @serial the name of the resouce + */ + private final String key; + + /** + * Creates a new exception, with the specified parameters. + * + * @param s the detail message + * @param className the name of the resource bundle + * @param key the key of the missing resource + */ + public MissingResourceException(String s, String className, String key) + { + super(s); + this.className = className; + this.key = key; + } + + /** + * Gets the name of the resource bundle, for which a resource is missing. + * + * @return the name of the resource bundle + */ + public String getClassName() + { + return className; + } + + /** + * Gets the key of the resource that is missing bundle, this is an empty + * string if the whole resource bundle is missing. + * + * @return the name of the resource bundle + */ + public String getKey() + { + return key; + } +} diff --git a/libjava/classpath/java/util/NoSuchElementException.java b/libjava/classpath/java/util/NoSuchElementException.java new file mode 100644 index 00000000000..5e1a2176d62 --- /dev/null +++ b/libjava/classpath/java/util/NoSuchElementException.java @@ -0,0 +1,88 @@ +/* NoSuchElementException.java -- Attempt to access element that does not exist + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * Exception thrown when an attempt is made to access an element that does not + * exist. This exception is thrown by the Enumeration, Iterator and + * ListIterator classes if the nextElement, next or previous method goes + * beyond the end of the list of elements that are being accessed. It is also + * thrown by Vector and Stack when attempting to access the first or last + * element of an empty collection. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Enumeration + * @see Iterator + * @see ListIterator + * @see Enumeration#nextElement() + * @see Iterator#next() + * @see ListIterator#previous() + * @since 1.0 + * @status updated to 1.4 + */ +public class NoSuchElementException extends RuntimeException +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 6769829250639411880L; + + /** + * Constructs a NoSuchElementException with no detail message. + */ + public NoSuchElementException() + { + } + + /** + * Constructs a NoSuchElementException with a detail message. + * + * @param detail the detail message for the exception + */ + public NoSuchElementException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/Observable.java b/libjava/classpath/java/util/Observable.java new file mode 100644 index 00000000000..4c2cddb5496 --- /dev/null +++ b/libjava/classpath/java/util/Observable.java @@ -0,0 +1,180 @@ +/* Observable.java -- an object to be observed + Copyright (C) 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class represents an object which is observable. Other objects may + * register their intent to be notified when this object changes; and when + * this object does change, it will trigger the <code>update</code> method + * of each observer. + * + * Note that the <code>notifyObservers()</code> method of this class is + * unrelated to the <code>notify()</code> of Object. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Observer + * @status updated to 1.4 + */ +public class Observable +{ + /** Tracks whether this object has changed. */ + private boolean changed; + + /* List of the Observers registered as interested in this Observable. */ + private LinkedHashSet observers; + + /** + * Constructs an Observable with zero Observers. + */ + public Observable() + { + observers = new LinkedHashSet(); + } + + /** + * Adds an Observer. If the observer was already added this method does + * nothing. + * + * @param observer Observer to add + * @throws NullPointerException if observer is null + */ + public synchronized void addObserver(Observer observer) + { + observers.add(observer); + } + + /** + * Reset this Observable's state to unchanged. This is called automatically + * by <code>notifyObservers</code> once all observers have been notified. + * + * @see #notifyObservers() + */ + protected synchronized void clearChanged() + { + changed = false; + } + + /** + * Returns the number of observers for this object. + * + * @return number of Observers for this + */ + public synchronized int countObservers() + { + return observers.size(); + } + + /** + * Deletes an Observer of this Observable. + * + * @param victim Observer to delete + */ + public synchronized void deleteObserver(Observer victim) + { + observers.remove(victim); + } + + /** + * Deletes all Observers of this Observable. + */ + public synchronized void deleteObservers() + { + observers.clear(); + } + + /** + * True if <code>setChanged</code> has been called more recently than + * <code>clearChanged</code>. + * + * @return whether or not this Observable has changed + */ + public synchronized boolean hasChanged() + { + return changed; + } + + /** + * If the Observable has actually changed then tell all Observers about it, + * then reset state to unchanged. + * + * @see #notifyObservers(Object) + * @see Observer#update(Observable, Object) + */ + public void notifyObservers() + { + notifyObservers(null); + } + + /** + * If the Observable has actually changed then tell all Observers about it, + * then reset state to unchanged. Note that though the order of + * notification is unspecified in subclasses, in Observable it is in the + * order of registration. + * + * @param obj argument to Observer's update method + * @see Observer#update(Observable, Object) + */ + public void notifyObservers(Object obj) + { + if (! hasChanged()) + return; + // Create clone inside monitor, as that is relatively fast and still + // important to keep threadsafe, but update observers outside of the + // lock since update() can call arbitrary code. + Set s; + synchronized (this) + { + s = (Set) observers.clone(); + } + int i = s.size(); + Iterator iter = s.iterator(); + while (--i >= 0) + ((Observer) iter.next()).update(this, obj); + clearChanged(); + } + + /** + * Marks this Observable as having changed. + */ + protected synchronized void setChanged() + { + changed = true; + } +} diff --git a/libjava/classpath/java/util/Observer.java b/libjava/classpath/java/util/Observer.java new file mode 100644 index 00000000000..c59a0ca6c68 --- /dev/null +++ b/libjava/classpath/java/util/Observer.java @@ -0,0 +1,60 @@ +/* Observer.java -- an object that will be informed of changes in an Observable + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface that is implemented when a class wants to be informed of changes + * in Observable objects. + * + * @author Warren Levy (warrenl@cygnus.com) + * @see Observable + * @status updated to 1.4 + */ +public interface Observer +{ + /** + * This method is called whenever the observable object changes, and has + * called <code>notifyObservers</code>. The Observable object can pass + * arbitrary information in the second parameter. + * + * @param observable the Observable object that changed + * @param arg arbitrary information, usually relating to the change + */ + void update(Observable observable, Object arg); +} diff --git a/libjava/classpath/java/util/Properties.java b/libjava/classpath/java/util/Properties.java new file mode 100644 index 00000000000..c7c19b3a380 --- /dev/null +++ b/libjava/classpath/java/util/Properties.java @@ -0,0 +1,574 @@ +/* Properties.java -- a set of persistent properties + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * A set of persistent properties, which can be saved or loaded from a stream. + * A property list may also contain defaults, searched if the main list + * does not contain a property for a given key. + * + * An example of a properties file for the german language is given + * here. This extends the example given in ListResourceBundle. + * Create a file MyResource_de.properties with the following contents + * and put it in the CLASSPATH. (The character + * <code>\</code><code>u00e4</code> is the german umlaut) + * + * +<pre>s1=3 +s2=MeineDisk +s3=3. M\<code></code>u00e4rz 96 +s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}. +s5=0 +s6=keine Dateien +s7=1 +s8=eine Datei +s9=2 +s10={0,number} Dateien +s11=Das Formatieren schlug fehl mit folgender Exception: {0} +s12=FEHLER +s13=Ergebnis +s14=Dialog +s15=Auswahlkriterium +s16=1,3</pre> + * + * <p>Although this is a sub class of a hash table, you should never + * insert anything other than strings to this property, or several + * methods, that need string keys and values, will fail. To ensure + * this, you should use the <code>get/setProperty</code> method instead + * of <code>get/put</code>. + * + * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with + * a single <code>u</code> for any character which cannot be represented. + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see PropertyResourceBundle + * @status updated to 1.4 + */ +public class Properties extends Hashtable +{ + // WARNING: Properties is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The property list that contains default values for any keys not + * in this property list. + * + * @serial the default properties + */ + protected Properties defaults; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4112578634029874840L; + + /** + * Creates a new empty property list with no default values. + */ + public Properties() + { + } + + /** + * Create a new empty property list with the specified default values. + * + * @param defaults a Properties object containing the default values + */ + public Properties(Properties defaults) + { + this.defaults = defaults; + } + + /** + * Adds the given key/value pair to this properties. This calls + * the hashtable method put. + * + * @param key the key for this property + * @param value the value for this property + * @return The old value for the given key + * @see #getProperty(String) + * @since 1.2 + */ + public Object setProperty(String key, String value) + { + return put(key, value); + } + + /** + * Reads a property list from an input stream. The stream should + * have the following format: <br> + * + * An empty line or a line starting with <code>#</code> or + * <code>!</code> is ignored. An backslash (<code>\</code>) at the + * end of the line makes the line continueing on the next line + * (but make sure there is no whitespace after the backslash). + * Otherwise, each line describes a key/value pair. <br> + * + * The chars up to the first whitespace, = or : are the key. You + * can include this caracters in the key, if you precede them with + * a backslash (<code>\</code>). The key is followed by optional + * whitespaces, optionally one <code>=</code> or <code>:</code>, + * and optionally some more whitespaces. The rest of the line is + * the resource belonging to the key. <br> + * + * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a + * space), and unicode characters with the + * <code>\\u</code><em>xxxx</em> notation are detected, and + * converted to the corresponding single character. <br> + * + * +<pre># This is a comment +key = value +k\:5 \ a string starting with space and ending with newline\n +# This is a multiline specification; note that the value contains +# no white space. +weekdays: Sunday,Monday,Tuesday,Wednesday,\\ + Thursday,Friday,Saturday +# The safest way to include a space at the end of a value: +label = Name:\\u0020</pre> + * + * @param inStream the input stream + * @throws IOException if an error occurred when reading the input + * @throws NullPointerException if in is null + */ + public void load(InputStream inStream) throws IOException + { + // The spec says that the file must be encoded using ISO-8859-1. + BufferedReader reader = + new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1")); + String line; + + while ((line = reader.readLine()) != null) + { + char c = 0; + int pos = 0; + // Leading whitespaces must be deleted first. + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + + // If empty line or begins with a comment character, skip this line. + if ((line.length() - pos) == 0 + || line.charAt(pos) == '#' || line.charAt(pos) == '!') + continue; + + // The characters up to the next Whitespace, ':', or '=' + // describe the key. But look for escape sequences. + StringBuffer key = new StringBuffer(); + while (pos < line.length() + && ! Character.isWhitespace(c = line.charAt(pos++)) + && c != '=' && c != ':') + { + if (c == '\\') + { + if (pos == line.length()) + { + // The line continues on the next line. + line = reader.readLine(); + pos = 0; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + } + else + { + c = line.charAt(pos++); + switch (c) + { + case 'n': + key.append('\n'); + break; + case 't': + key.append('\t'); + break; + case 'r': + key.append('\r'); + break; + case 'u': + if (pos + 4 <= line.length()) + { + char uni = (char) Integer.parseInt + (line.substring(pos, pos + 4), 16); + key.append(uni); + pos += 4; + } // else throw exception? + break; + default: + key.append(c); + break; + } + } + } + else + key.append(c); + } + + boolean isDelim = (c == ':' || c == '='); + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + + if (! isDelim && (c == ':' || c == '=')) + { + pos++; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + } + + StringBuffer element = new StringBuffer(line.length() - pos); + while (pos < line.length()) + { + c = line.charAt(pos++); + if (c == '\\') + { + if (pos == line.length()) + { + // The line continues on the next line. + line = reader.readLine(); + + // We might have seen a backslash at the end of + // the file. The JDK ignores the backslash in + // this case, so we follow for compatibility. + if (line == null) + break; + + pos = 0; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + element.ensureCapacity(line.length() - pos + + element.length()); + } + else + { + c = line.charAt(pos++); + switch (c) + { + case 'n': + element.append('\n'); + break; + case 't': + element.append('\t'); + break; + case 'r': + element.append('\r'); + break; + case 'u': + if (pos + 4 <= line.length()) + { + char uni = (char) Integer.parseInt + (line.substring(pos, pos + 4), 16); + element.append(uni); + pos += 4; + } // else throw exception? + break; + default: + element.append(c); + break; + } + } + } + else + element.append(c); + } + put(key.toString(), element.toString()); + } + } + + /** + * Calls <code>store(OutputStream out, String header)</code> and + * ignores the IOException that may be thrown. + * + * @param out the stream to write to + * @param header a description of the property list + * @throws ClassCastException if this property contains any key or + * value that are not strings + * @deprecated use {@link #store(OutputStream, String)} instead + */ + public void save(OutputStream out, String header) + { + try + { + store(out, header); + } + catch (IOException ex) + { + } + } + + /** + * Writes the key/value pairs to the given output stream, in a format + * suitable for <code>load</code>.<br> + * + * If header is not null, this method writes a comment containing + * the header as first line to the stream. The next line (or first + * line if header is null) contains a comment with the current date. + * Afterwards the key/value pairs are written to the stream in the + * following format.<br> + * + * Each line has the form <code>key = value</code>. Newlines, + * Returns and tabs are written as <code>\n,\t,\r</code> resp. + * The characters <code>\, !, #, =</code> and <code>:</code> are + * preceeded by a backslash. Spaces are preceded with a backslash, + * if and only if they are at the beginning of the key. Characters + * that are not in the ascii range 33 to 127 are written in the + * <code>\</code><code>u</code>xxxx Form.<br> + * + * Following the listing, the output stream is flushed but left open. + * + * @param out the output stream + * @param header the header written in the first line, may be null + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @throws IOException if writing to the stream fails + * @throws NullPointerException if out is null + * @since 1.2 + */ + public void store(OutputStream out, String header) throws IOException + { + // The spec says that the file must be encoded using ISO-8859-1. + PrintWriter writer + = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1")); + if (header != null) + writer.println("#" + header); + writer.println ("#" + Calendar.getInstance ().getTime ()); + + Iterator iter = entrySet ().iterator (); + int i = size (); + StringBuffer s = new StringBuffer (); // Reuse the same buffer. + while (--i >= 0) + { + Map.Entry entry = (Map.Entry) iter.next (); + formatForOutput ((String) entry.getKey (), s, true); + s.append ('='); + formatForOutput ((String) entry.getValue (), s, false); + writer.println (s); + } + + writer.flush (); + } + + /** + * Gets the property with the specified key in this property list. + * If the key is not found, the default property list is searched. + * If the property is not found in the default, null is returned. + * + * @param key The key for this property + * @return the value for the given key, or null if not found + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @see #defaults + * @see #setProperty(String, String) + * @see #getProperty(String, String) + */ + public String getProperty(String key) + { + return getProperty(key, null); + } + + /** + * Gets the property with the specified key in this property list. If + * the key is not found, the default property list is searched. If the + * property is not found in the default, the specified defaultValue is + * returned. + * + * @param key The key for this property + * @param defaultValue A default value + * @return The value for the given key + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @see #defaults + * @see #setProperty(String, String) + */ + public String getProperty(String key, String defaultValue) + { + Properties prop = this; + // Eliminate tail recursion. + do + { + String value = (String) prop.get(key); + if (value != null) + return value; + prop = prop.defaults; + } + while (prop != null); + return defaultValue; + } + + /** + * Returns an enumeration of all keys in this property list, including + * the keys in the default property list. + * + * @return an Enumeration of all defined keys + */ + public Enumeration propertyNames() + { + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + Properties prop = this; + Set s = new HashSet(); + // Eliminate tail recursion. + do + { + s.addAll(prop.keySet()); + prop = prop.defaults; + } + while (prop != null); + return Collections.enumeration(s); + } + + /** + * Prints the key/value pairs to the given print stream. This is + * mainly useful for debugging purposes. + * + * @param out the print stream, where the key/value pairs are written to + * @throws ClassCastException if this property contains a key or a + * value that isn't a string + * @see #list(PrintWriter) + */ + public void list(PrintStream out) + { + PrintWriter writer = new PrintWriter (out); + list (writer); + } + + /** + * Prints the key/value pairs to the given print writer. This is + * mainly useful for debugging purposes. + * + * @param out the print writer where the key/value pairs are written to + * @throws ClassCastException if this property contains a key or a + * value that isn't a string + * @see #list(PrintStream) + * @since 1.1 + */ + public void list(PrintWriter out) + { + out.println ("-- listing properties --"); + + Iterator iter = entrySet ().iterator (); + int i = size (); + while (--i >= 0) + { + Map.Entry entry = (Map.Entry) iter.next (); + out.print ((String) entry.getKey () + "="); + + // JDK 1.3/1.4 restrict the printed value, but not the key, + // to 40 characters, including the truncating ellipsis. + String s = (String ) entry.getValue (); + if (s != null && s.length () > 40) + out.println (s.substring (0, 37) + "..."); + else + out.println (s); + } + out.flush (); + } + + /** + * Formats a key or value for output in a properties file. + * See store for a description of the format. + * + * @param str the string to format + * @param buffer the buffer to add it to + * @param key true if all ' ' must be escaped for the key, false if only + * leading spaces must be escaped for the value + * @see #store(OutputStream, String) + */ + private void formatForOutput(String str, StringBuffer buffer, boolean key) + { + if (key) + { + buffer.setLength(0); + buffer.ensureCapacity(str.length()); + } + else + buffer.ensureCapacity(buffer.length() + str.length()); + boolean head = true; + int size = str.length(); + for (int i = 0; i < size; i++) + { + char c = str.charAt(i); + switch (c) + { + case '\n': + buffer.append("\\n"); + break; + case '\r': + buffer.append("\\r"); + break; + case '\t': + buffer.append("\\t"); + break; + case ' ': + buffer.append(head ? "\\ " : " "); + break; + case '\\': + case '!': + case '#': + case '=': + case ':': + buffer.append('\\').append(c); + break; + default: + if (c < ' ' || c > '~') + { + String hex = Integer.toHexString(c); + buffer.append("\\u0000".substring(0, 6 - hex.length())); + buffer.append(hex); + } + else + buffer.append(c); + } + if (c != ' ') + head = key; + } + } +} // class Properties diff --git a/libjava/classpath/java/util/PropertyPermission.java b/libjava/classpath/java/util/PropertyPermission.java new file mode 100644 index 00000000000..d1bdbd1d058 --- /dev/null +++ b/libjava/classpath/java/util/PropertyPermission.java @@ -0,0 +1,271 @@ +/* PropertyPermission.java -- permission to get and set System properties + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.security.BasicPermission; +import java.security.Permission; +import java.security.PermissionCollection; + +/** + * This class represents the permission to access and modify a property.<br> + * + * The name is the name of the property, e.g. xxx. You can also + * use an asterisk "*" as described in BasicPermission.<br> + * + * The action string is a comma-separated list of keywords. There are + * two possible actions: + * <dl> + * <dt>read</dt> + * <dd>Allows to read the property via <code>System.getProperty</code>.</dd> + * <dt>write</dt> + * <dd>Allows to write the property via <code>System.setProperty</code>.</dd> + * </dl> + * + * The action string is case insensitive (it is converted to lower case). + * + * @see Permission + * @see BasicPermission + * @see SecurityManager + * @author Jochen Hoenicke + * @since 1.2 + * @status updated to 1.4 + */ +public final class PropertyPermission extends BasicPermission +{ + /** + * PropertyPermission uses a more efficient representation than the + * serialized form; this documents the difference. + * + * @serialField action String the action string + */ + private static final ObjectStreamField[] serialPersistentFields = + { + new ObjectStreamField("action", String.class) + }; + + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 885438825399942851L; + + /** Permission to read. */ + private static final int READ = 1; + /** Permission to write. */ + private static final int WRITE = 2; + + /** The set of actions permitted. */ + // Package visible for use by PropertyPermissionCollection. + transient int actions; + + /** + * The String forms of the actions permitted. + */ + private static final String actionStrings[] = + { + "", "read", "write", "read,write" + }; + + /** + * Constructs a PropertyPermission with the specified property. Possible + * actions are read and write, comma-separated and case-insensitive. + * + * @param name the name of the property + * @param actions the action string + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name string contains an + * illegal wildcard or actions string contains an illegal action + * (this includes a null actions string) + */ + public PropertyPermission(String name, String actions) + { + super(name); + if (actions == null) + throw new IllegalArgumentException(); + setActions(actions); + } + + /** + * Parse the action string and convert actions from external to internal + * form. This will set the internal actions field. + * + * @param str the action string + * @throws IllegalArgumentException if actions string contains an + * illegal action + */ + private void setActions(String str) + { + // Initialising the class java.util.Locale ... + // tries to initialise the Locale.defaultLocale static + // which calls System.getProperty, + // which calls SecurityManager.checkPropertiesAccess, + // which creates a PropertyPermission with action "read,write", + // which calls setActions("read,write"). + // If we now were to call toLowerCase on 'str', + // this would call Locale.getDefault() which returns null + // because Locale.defaultLocale hasn't been set yet + // then toLowerCase will fail with a null pointer exception. + // + // The solution is to take a punt on 'str' being lower case, and + // test accordingly. If that fails, we convert 'str' to lower case + // and try the tests again. + if ("read".equals(str)) + actions = READ; + else if ("write".equals(str)) + actions = WRITE; + else if ("read,write".equals(str) || "write,read".equals(str)) + actions = READ | WRITE; + else + { + String lstr = str.toLowerCase(); + if ("read".equals(lstr)) + actions = READ; + else if ("write".equals(lstr)) + actions = WRITE; + else if ("read,write".equals(lstr) || "write,read".equals(lstr)) + actions = READ | WRITE; + else + throw new IllegalArgumentException("illegal action " + str); + } + } + + /** + * Reads an object from the stream. This converts the external to the + * internal representation. + * + * @param s the stream to read from + * @throws IOException if the stream fails + * @throws ClassNotFoundException if reserialization fails + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + ObjectInputStream.GetField fields = s.readFields(); + setActions((String) fields.get("actions", null)); + } + + /** + * Writes an object to the stream. This converts the internal to the + * external representation. + * + * @param s the stram to write to + * @throws IOException if the stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("actions", getActions()); + s.writeFields(); + } + + /** + * Check if this permission implies p. This returns true iff all of + * the following conditions are true: + * <ul> + * <li> p is a PropertyPermission </li> + * <li> this.getName() implies p.getName(), + * e.g. <code>java.*</code> implies <code>java.home</code> </li> + * <li> this.getActions is a subset of p.getActions </li> + * </ul> + * + * @param p the permission to check + * @return true if this permission implies p + */ + public boolean implies(Permission p) + { + // BasicPermission checks for name and type. + if (super.implies(p)) + { + // We have to check the actions. + PropertyPermission pp = (PropertyPermission) p; + return (pp.actions & ~actions) == 0; + } + return false; + } + + /** + * Check to see whether this object is the same as another + * PropertyPermission object; this is true if it has the same name and + * actions. + * + * @param obj the other object + * @return true if the two are equivalent + */ + public boolean equals(Object obj) + { + return super.equals(obj) && actions == ((PropertyPermission) obj).actions; + } + + /** + * Returns the hash code for this permission. It is equivalent to + * <code>getName().hashCode()</code>. + * + * @return the hash code + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Returns the action string. Note that this may differ from the string + * given at the constructor: The actions are converted to lowercase and + * may be reordered. + * + * @return one of "read", "write", or "read,write" + */ + public String getActions() + { + return actionStrings[actions]; + } + + /** + * Returns a permission collection suitable to take + * PropertyPermission objects. + * + * @return a new empty PermissionCollection + */ + public PermissionCollection newPermissionCollection() + { + return new PropertyPermissionCollection(); + } +} diff --git a/libjava/classpath/java/util/PropertyPermissionCollection.java b/libjava/classpath/java/util/PropertyPermissionCollection.java new file mode 100644 index 00000000000..c95fa4e66bf --- /dev/null +++ b/libjava/classpath/java/util/PropertyPermissionCollection.java @@ -0,0 +1,166 @@ +/* PropertyPermissionCollection.java -- a collection of PropertyPermissions + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.security.Permission; +import java.security.PermissionCollection; + +/** + * This class provides the implementation for + * <code>PropertyPermission.newPermissionCollection()</code>. It only accepts + * PropertyPermissions, and correctly implements <code>implies</code>. It + * is synchronized, as specified in the superclass. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status an undocumented class, but this matches Sun's serialization + */ +class PropertyPermissionCollection extends PermissionCollection +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7015263904581634791L; + + /** + * The permissions. + * + * @serial the table of permissions in the collection + */ + private final Hashtable permissions = new Hashtable(); + + /** + * A flag to detect if "*" is in the collection. + * + * @serial true if "*" is in the collection + */ + private boolean all_allowed; + + /** + * Adds a PropertyPermission to this collection. + * + * @param permission the permission to add + * @throws IllegalArgumentException if permission is not a PropertyPermission + * @throws SecurityException if collection is read-only + */ + public void add(Permission permission) + { + if (isReadOnly()) + throw new SecurityException("readonly"); + if (! (permission instanceof PropertyPermission)) + throw new IllegalArgumentException(); + PropertyPermission pp = (PropertyPermission) permission; + String name = pp.getName(); + if (name.equals("*")) + all_allowed = true; + PropertyPermission old = (PropertyPermission) permissions.get(name); + if (old != null) + { + if ((pp.actions | old.actions) == old.actions) + pp = old; // Old implies pp. + else if ((pp.actions | old.actions) != pp.actions) + // Here pp doesn't imply old; the only case left is both actions. + pp = new PropertyPermission(name, "read,write"); + } + permissions.put(name, pp); + } + + /** + * Returns true if this collection implies the given permission. This even + * returns true for this case: + * + * <pre> + * collection.add(new PropertyPermission("a.*", "read")); + * collection.add(new PropertyPermission("a.b.*", "write")); + * collection.implies(new PropertyPermission("a.b.c", "read,write")); + * </pre> + * + * @param permission the permission to check + * @return true if it is implied by this + */ + public boolean implies(Permission permission) + { + if (! (permission instanceof PropertyPermission)) + return false; + PropertyPermission toImply = (PropertyPermission) permission; + int actions = toImply.actions; + + if (all_allowed) + { + int all_actions = ((PropertyPermission) permissions.get("*")).actions; + actions &= ~all_actions; + if (actions == 0) + return true; + } + + String name = toImply.getName(); + if (name.equals("*")) + return false; + + int prefixLength = name.length(); + if (name.endsWith("*")) + prefixLength -= 2; + + while (true) + { + PropertyPermission forName = + (PropertyPermission) permissions.get(name); + if (forName != null) + { + actions &= ~forName.actions; + if (actions == 0) + return true; + } + + prefixLength = name.lastIndexOf('.', prefixLength - 1); + if (prefixLength < 0) + return false; + name = name.substring(0, prefixLength + 1) + '*'; + } + } + + /** + * Enumerate over the collection. + * + * @return an enumeration of the collection contents + */ + public Enumeration elements() + { + return permissions.elements(); + } +} diff --git a/libjava/classpath/java/util/PropertyResourceBundle.java b/libjava/classpath/java/util/PropertyResourceBundle.java new file mode 100644 index 00000000000..aaff0766a35 --- /dev/null +++ b/libjava/classpath/java/util/PropertyResourceBundle.java @@ -0,0 +1,152 @@ +/* PropertyResourceBundle -- a resource bundle built from a Property file + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This class is a concrete <code>ResourceBundle</code> that gets it + * resources from a property file. This implies that the resources are + * strings. For more information about resource bundles see the class + * <code>ResourceBundle</code>. + * + * You should not use this class directly, or subclass it, but you get + * an object of this class automatically when you call + * <code>ResourceBundle.getBundle()</code> and there is a properties + * file. + * + * If there is also a class for this resource and the same locale, the + * class will be chosen. The properties file should have the name of the + * resource bundle, appended with the locale (e.g. <code>_de</code> and the + * extension <code>.properties</code>. The file should have the same format + * as for <code>Properties.load()</code> + * + * An example of a properties file for the german language is given + * here. This extends the example given in ListResourceBundle. + * Create a file MyResource_de.properties with the following contents + * and put it in the CLASSPATH. (The char <code>\u00e4</code> is the + * german umlaut) + * + * +<pre> +s1=3 +s2=MeineDisk +s3=3. M\u00e4rz 96 +s4=Die Diskette ''{1}'' enth\u00e4lt {0} in {2}. +s5=0 +s6=keine Dateien +s7=1 +s8=eine Datei +s9=2 +s10={0,number} Dateien +s11=Die Formatierung warf eine Exception: {0} +s12=FEHLER +s13=Ergebnis +s14=Dialog +s15=Auswahlkriterium +s16=1,3 +</pre> + * + * @author Jochen Hoenicke + * @see ResourceBundle + * @see ListResourceBundle + * @see Properties#load(InputStream) + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyResourceBundle extends ResourceBundle +{ + /** The properties file this bundle is based on. */ + private Properties properties; + + /** + * Creates a new property resource bundle. + * + * @param stream an input stream, where the resources are read from + * @throws NullPointerException if stream is null + * @throws IOException if reading the stream fails + */ + public PropertyResourceBundle(InputStream stream) throws IOException + { + properties = new Properties(); + properties.load(stream); + } + + /** + * Called by <code>getObject</code> when a resource is needed. This + * returns the resource given by the key. + * + * @param key the key of the resource + * @return the resource for the key, or null if it doesn't exist + */ + public Object handleGetObject(String key) + { + return properties.getProperty(key); + } + + /** + * This method should return all keys for which a resource exists. + * + * @return an enumeration of the keys + */ + public Enumeration getKeys() + { + if (parent == null) + return properties.propertyNames(); + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + Set s = new HashSet(); + Enumeration e = properties.propertyNames(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + ResourceBundle bundle = parent; + // Eliminate tail recursion. + do + { + e = bundle.getKeys(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + bundle = bundle.parent; + } + while (bundle != null); + return Collections.enumeration(s); + } +} // class PropertyResourceBundle diff --git a/libjava/classpath/java/util/Random.java b/libjava/classpath/java/util/Random.java new file mode 100644 index 00000000000..bc005075140 --- /dev/null +++ b/libjava/classpath/java/util/Random.java @@ -0,0 +1,429 @@ +/* Random.java -- a pseudo-random number generator + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * This class generates pseudorandom numbers. It uses the same + * algorithm as the original JDK-class, so that your programs behave + * exactly the same way, if started with the same seed. + * + * The algorithm is described in <em>The Art of Computer Programming, + * Volume 2</em> by Donald Knuth in Section 3.2.1. It is a 48-bit seed, + * linear congruential formula. + * + * If two instances of this class are created with the same seed and + * the same calls to these classes are made, they behave exactly the + * same way. This should be even true for foreign implementations + * (like this), so every port must use the same algorithm as described + * here. + * + * If you want to implement your own pseudorandom algorithm, you + * should extend this class and overload the <code>next()</code> and + * <code>setSeed(long)</code> method. In that case the above + * paragraph doesn't apply to you. + * + * This class shouldn't be used for security sensitive purposes (like + * generating passwords or encryption keys. See <code>SecureRandom</code> + * in package <code>java.security</code> for this purpose. + * + * For simple random doubles between 0.0 and 1.0, you may consider using + * Math.random instead. + * + * @see java.security.SecureRandom + * @see Math#random() + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class Random implements Serializable +{ + /** + * True if the next nextGaussian is available. This is used by + * nextGaussian, which generates two gaussian numbers by one call, + * and returns the second on the second call. + * + * @serial whether nextNextGaussian is available + * @see #nextGaussian() + * @see #nextNextGaussian + */ + private boolean haveNextNextGaussian; + + /** + * The next nextGaussian, when available. This is used by nextGaussian, + * which generates two gaussian numbers by one call, and returns the + * second on the second call. + * + * @serial the second gaussian of a pair + * @see #nextGaussian() + * @see #haveNextNextGaussian + */ + private double nextNextGaussian; + + /** + * The seed. This is the number set by setSeed and which is used + * in next. + * + * @serial the internal state of this generator + * @see #next() + */ + private long seed; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3905348978240129619L; + + /** + * Creates a new pseudorandom number generator. The seed is initialized + * to the current time, as if by + * <code>setSeed(System.currentTimeMillis());</code>. + * + * @see System#currentTimeMillis() + */ + public Random() + { + this(System.currentTimeMillis()); + } + + /** + * Creates a new pseudorandom number generator, starting with the + * specified seed, using <code>setSeed(seed);</code>. + * + * @param seed the initial seed + */ + public Random(long seed) + { + setSeed(seed); + } + + /** + * Sets the seed for this pseudorandom number generator. As described + * above, two instances of the same random class, starting with the + * same seed, should produce the same results, if the same methods + * are called. The implementation for java.util.Random is: + * +<pre>public synchronized void setSeed(long seed) +{ + this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1); + haveNextNextGaussian = false; +}</pre> + * + * @param seed the new seed + */ + public synchronized void setSeed(long seed) + { + this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1); + haveNextNextGaussian = false; + } + + /** + * Generates the next pseudorandom number. This returns + * an int value whose <code>bits</code> low order bits are + * independent chosen random bits (0 and 1 are equally likely). + * The implementation for java.util.Random is: + * +<pre>protected synchronized int next(int bits) +{ + seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); + return (int) (seed >>> (48 - bits)); +}</pre> + * + * @param bits the number of random bits to generate, in the range 1..32 + * @return the next pseudorandom value + * @since 1.1 + */ + protected synchronized int next(int bits) + { + seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); + return (int) (seed >>> (48 - bits)); + } + + /** + * Fills an array of bytes with random numbers. All possible values + * are (approximately) equally likely. + * The JDK documentation gives no implementation, but it seems to be: + * +<pre>public void nextBytes(byte[] bytes) +{ + for (int i = 0; i < bytes.length; i += 4) + { + int random = next(32); + for (int j = 0; i + j < bytes.length && j < 4; j++) + { + bytes[i+j] = (byte) (random & 0xff) + random >>= 8; + } + } +}</pre> + * + * @param bytes the byte array that should be filled + * @throws NullPointerException if bytes is null + * @since 1.1 + */ + public void nextBytes(byte[] bytes) + { + int random; + // Do a little bit unrolling of the above algorithm. + int max = bytes.length & ~0x3; + for (int i = 0; i < max; i += 4) + { + random = next(32); + bytes[i] = (byte) random; + bytes[i + 1] = (byte) (random >> 8); + bytes[i + 2] = (byte) (random >> 16); + bytes[i + 3] = (byte) (random >> 24); + } + if (max < bytes.length) + { + random = next(32); + for (int j = max; j < bytes.length; j++) + { + bytes[j] = (byte) random; + random >>= 8; + } + } + } + + /** + * Generates the next pseudorandom number. This returns + * an int value whose 32 bits are independent chosen random bits + * (0 and 1 are equally likely). The implementation for + * java.util.Random is: + * +<pre>public int nextInt() +{ + return next(32); +}</pre> + * + * @return the next pseudorandom value + */ + public int nextInt() + { + return next(32); + } + + /** + * Generates the next pseudorandom number. This returns + * a value between 0(inclusive) and <code>n</code>(exclusive), and + * each value has the same likelihodd (1/<code>n</code>). + * (0 and 1 are equally likely). The implementation for + * java.util.Random is: + * +<pre> +public int nextInt(int n) +{ + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + + if ((n & -n) == n) // i.e., n is a power of 2 + return (int)((n * (long) next(31)) >> 31); + + int bits, val; + do + { + bits = next(31); + val = bits % n; + } + while(bits - val + (n-1) < 0); + + return val; +}</pre> + * + * <p>This algorithm would return every value with exactly the same + * probability, if the next()-method would be a perfect random number + * generator. + * + * The loop at the bottom only accepts a value, if the random + * number was between 0 and the highest number less then 1<<31, + * which is divisible by n. The probability for this is high for small + * n, and the worst case is 1/2 (for n=(1<<30)+1). + * + * The special treatment for n = power of 2, selects the high bits of + * the random number (the loop at the bottom would select the low order + * bits). This is done, because the low order bits of linear congruential + * number generators (like the one used in this class) are known to be + * ``less random'' than the high order bits. + * + * @param n the upper bound + * @throws IllegalArgumentException if the given upper bound is negative + * @return the next pseudorandom value + * @since 1.2 + */ + public int nextInt(int n) + { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + if ((n & -n) == n) // i.e., n is a power of 2 + return (int) ((n * (long) next(31)) >> 31); + int bits, val; + do + { + bits = next(31); + val = bits % n; + } + while (bits - val + (n - 1) < 0); + return val; + } + + /** + * Generates the next pseudorandom long number. All bits of this + * long are independently chosen and 0 and 1 have equal likelihood. + * The implementation for java.util.Random is: + * +<pre>public long nextLong() +{ + return ((long) next(32) << 32) + next(32); +}</pre> + * + * @return the next pseudorandom value + */ + public long nextLong() + { + return ((long) next(32) << 32) + next(32); + } + + /** + * Generates the next pseudorandom boolean. True and false have + * the same probability. The implementation is: + * +<pre>public boolean nextBoolean() +{ + return next(1) != 0; +}</pre> + * + * @return the next pseudorandom boolean + * @since 1.2 + */ + public boolean nextBoolean() + { + return next(1) != 0; + } + + /** + * Generates the next pseudorandom float uniformly distributed + * between 0.0f (inclusive) and 1.0f (exclusive). The + * implementation is as follows. + * +<pre>public float nextFloat() +{ + return next(24) / ((float)(1 << 24)); +}</pre> + * + * @return the next pseudorandom float + */ + public float nextFloat() + { + return next(24) / (float) (1 << 24); + } + + /** + * Generates the next pseudorandom double uniformly distributed + * between 0.0 (inclusive) and 1.0 (exclusive). The + * implementation is as follows. + * +<pre>public double nextDouble() +{ + return (((long) next(26) << 27) + next(27)) / (double)(1L << 53); +}</pre> + * + * @return the next pseudorandom double + */ + public double nextDouble() + { + return (((long) next(26) << 27) + next(27)) / (double) (1L << 53); + } + + /** + * Generates the next pseudorandom, Gaussian (normally) distributed + * double value, with mean 0.0 and standard deviation 1.0. + * The algorithm is as follows. + * +<pre>public synchronized double nextGaussian() +{ + if (haveNextNextGaussian) + { + haveNextNextGaussian = false; + return nextNextGaussian; + } + else + { + double v1, v2, s; + do + { + v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + s = v1 * v1 + v2 * v2; + } + while (s >= 1); + + double norm = Math.sqrt(-2 * Math.log(s) / s); + nextNextGaussian = v2 * norm; + haveNextNextGaussian = true; + return v1 * norm; + } +}</pre> + * + * <p>This is described in section 3.4.1 of <em>The Art of Computer + * Programming, Volume 2</em> by Donald Knuth. + * + * @return the next pseudorandom Gaussian distributed double + */ + public synchronized double nextGaussian() + { + if (haveNextNextGaussian) + { + haveNextNextGaussian = false; + return nextNextGaussian; + } + double v1, v2, s; + do + { + v1 = 2 * nextDouble() - 1; // Between -1.0 and 1.0. + v2 = 2 * nextDouble() - 1; // Between -1.0 and 1.0. + s = v1 * v1 + v2 * v2; + } + while (s >= 1); + double norm = Math.sqrt(-2 * Math.log(s) / s); + nextNextGaussian = v2 * norm; + haveNextNextGaussian = true; + return v1 * norm; + } +} diff --git a/libjava/classpath/java/util/RandomAccess.java b/libjava/classpath/java/util/RandomAccess.java new file mode 100644 index 00000000000..054266a1c74 --- /dev/null +++ b/libjava/classpath/java/util/RandomAccess.java @@ -0,0 +1,64 @@ +/* RandomAccess.java -- A tagging interface that lists can use to tailor + operations to the correct algorithm + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Marker interface used to inform <code>List</code> implementations that + * they support fast (usually constant time) random access. This allows + * generic list algorithms to tailor their behavior based on the list + * type. + * <p> + * + * For example, some sorts are n*log(n) on an array, but decay to quadratic + * time on a linked list. As a rule of thumb, this interface should be + * used is this loop:<br> + * <code>for (int i = 0, n = list.size(); i < n; i++) list.get(i);</code> + * <br>runs faster than this loop:<br> + * <code>for (Iterator i = list.iterator(); i.hasNext(); ) i.next();</code> + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @since 1.4 + * @status updated to 1.4 + */ +public interface RandomAccess +{ + // Tagging interface only. +} diff --git a/libjava/classpath/java/util/ResourceBundle.java b/libjava/classpath/java/util/ResourceBundle.java new file mode 100644 index 00000000000..91007e9b1aa --- /dev/null +++ b/libjava/classpath/java/util/ResourceBundle.java @@ -0,0 +1,580 @@ +/* ResourceBundle -- aids in loading resource bundles + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.classpath.VMStackWalker; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A resource bundle contains locale-specific data. If you need localized + * data, you can load a resource bundle that matches the locale with + * <code>getBundle</code>. Now you can get your object by calling + * <code>getObject</code> or <code>getString</code> on that bundle. + * + * <p>When a bundle is demanded for a specific locale, the ResourceBundle + * is searched in following order (<i>def. language</i> stands for the + * two letter ISO language code of the default locale (see + * <code>Locale.getDefault()</code>). + * +<pre>baseName_<i>language code</i>_<i>country code</i>_<i>variant</i> +baseName_<i>language code</i>_<i>country code</i> +baseName_<i>language code</i> +baseName_<i>def. language</i>_<i>def. country</i>_<i>def. variant</i> +baseName_<i>def. language</i>_<i>def. country</i> +baseName_<i>def. language</i> +baseName</pre> + * + * <p>A bundle is backed up by less specific bundles (omitting variant, country + * or language). But it is not backed up by the default language locale. + * + * <p>If you provide a bundle for a given locale, say + * <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for + * all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and + * <code>Bundle</code>. + * + * <p>When a bundle is searched, we look first for a class with the given + * name, then for a file with <code>.properties</code> extension in the + * classpath. The name must be a fully qualified classname (with dots as + * path separators). + * + * <p>(Note: This implementation always backs up the class with a properties + * file if that is existing, but you shouldn't rely on this, if you want to + * be compatible to the standard JDK.) + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see Locale + * @see ListResourceBundle + * @see PropertyResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ResourceBundle +{ + /** + * The parent bundle. This is consulted when you call getObject and there + * is no such resource in the current bundle. This field may be null. + */ + protected ResourceBundle parent; + + /** + * The locale of this resource bundle. You can read this with + * <code>getLocale</code> and it is automatically set in + * <code>getBundle</code>. + */ + private Locale locale; + + /** + * The resource bundle cache. + */ + private static Map bundleCache; + + /** + * The last default Locale we saw. If this ever changes then we have to + * reset our caches. + */ + private static Locale lastDefaultLocale; + + /** + * The `empty' locale is created once in order to optimize + * tryBundle(). + */ + private static final Locale emptyLocale = new Locale(""); + + /** + * The constructor. It does nothing special. + */ + public ResourceBundle() + { + } + + /** + * Get a String from this resource bundle. Since most localized Objects + * are Strings, this method provides a convenient way to get them without + * casting. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + * @throws ClassCastException if resource is not a string + */ + public final String getString(String key) + { + return (String) getObject(key); + } + + /** + * Get an array of Strings from this resource bundle. This method + * provides a convenient way to get it without casting. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + * @throws ClassCastException if resource is not a string + */ + public final String[] getStringArray(String key) + { + return (String[]) getObject(key); + } + + /** + * Get an object from this resource bundle. This will call + * <code>handleGetObject</code> for this resource and all of its parents, + * until it finds a non-null resource. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + */ + public final Object getObject(String key) + { + for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent) + { + Object o = bundle.handleGetObject(key); + if (o != null) + return o; + } + + String className = getClass().getName(); + throw new MissingResourceException("Key '" + key + + "'not found in Bundle: " + + className, className, key); + } + + /** + * Return the actual locale of this bundle. You can use it after calling + * getBundle, to know if the bundle for the desired locale was loaded or + * if the fall back was used. + * + * @return the bundle's locale + */ + public Locale getLocale() + { + return locale; + } + + /** + * Set the parent of this bundle. The parent is consulted when you call + * getObject and there is no such resource in the current bundle. + * + * @param parent the parent of this bundle + */ + protected void setParent(ResourceBundle parent) + { + this.parent = parent; + } + + /** + * Get the appropriate ResourceBundle for the default locale. This is like + * calling <code>getBundle(baseName, Locale.getDefault(), + * getClass().getClassLoader()</code>, except that any security check of + * getClassLoader won't fail. + * + * @param baseName the name of the ResourceBundle + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if baseName is null + */ + public static ResourceBundle getBundle(String baseName) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl == null) + cl = ClassLoader.getSystemClassLoader(); + return getBundle(baseName, Locale.getDefault(), cl); + } + + /** + * Get the appropriate ResourceBundle for the given locale. This is like + * calling <code>getBundle(baseName, locale, + * getClass().getClassLoader()</code>, except that any security check of + * getClassLoader won't fail. + * + * @param baseName the name of the ResourceBundle + * @param locale A locale + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if baseName or locale is null + */ + public static ResourceBundle getBundle(String baseName, Locale locale) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl == null) + cl = ClassLoader.getSystemClassLoader(); + return getBundle(baseName, locale, cl); + } + + /** Cache key for the ResourceBundle cache. Resource bundles are keyed + by the combination of bundle name, locale, and class loader. */ + private static class BundleKey + { + String baseName; + Locale locale; + ClassLoader classLoader; + int hashcode; + + BundleKey() {} + + BundleKey(String s, Locale l, ClassLoader cl) + { + set(s, l, cl); + } + + void set(String s, Locale l, ClassLoader cl) + { + baseName = s; + locale = l; + classLoader = cl; + hashcode = baseName.hashCode() ^ locale.hashCode() ^ + classLoader.hashCode(); + } + + public int hashCode() + { + return hashcode; + } + + public boolean equals(Object o) + { + if (! (o instanceof BundleKey)) + return false; + BundleKey key = (BundleKey) o; + return hashcode == key.hashcode && + baseName.equals(key.baseName) && + locale.equals(key.locale) && + classLoader.equals(key.classLoader); + } + } + + /** A cache lookup key. This avoids having to a new one for every + * getBundle() call. */ + private static BundleKey lookupKey = new BundleKey(); + + /** Singleton cache entry to represent previous failed lookups. */ + private static Object nullEntry = new Object(); + + /** + * Get the appropriate ResourceBundle for the given locale. The following + * strategy is used: + * + * <p>A sequence of candidate bundle names are generated, and tested in + * this order, where the suffix 1 means the string from the specified + * locale, and the suffix 2 means the string from the default locale:</p> + * + * <ul> + * <li>baseName + "_" + language1 + "_" + country1 + "_" + variant1</li> + * <li>baseName + "_" + language1 + "_" + country1</li> + * <li>baseName + "_" + language1</li> + * <li>baseName + "_" + language2 + "_" + country2 + "_" + variant2</li> + * <li>baseName + "_" + language2 + "_" + country2</li> + * <li>baseName + "_" + language2</li> + * <li>baseName</li> + * </ul> + * + * <p>In the sequence, entries with an empty string are ignored. Next, + * <code>getBundle</code> tries to instantiate the resource bundle:</p> + * + * <ul> + * <li>First, an attempt is made to load a class in the specified classloader + * which is a subclass of ResourceBundle, and which has a public constructor + * with no arguments, via reflection.</li> + * <li>Next, a search is made for a property resource file, by replacing + * '.' with '/' and appending ".properties", and using + * ClassLoader.getResource(). If a file is found, then a + * PropertyResourceBundle is created from the file's contents.</li> + * </ul> + * If no resource bundle was found, a MissingResourceException is thrown. + * + * <p>Next, the parent chain is implemented. The remaining candidate names + * in the above sequence are tested in a similar manner, and if any results + * in a resource bundle, it is assigned as the parent of the first bundle + * using the <code>setParent</code> method (unless the first bundle already + * has a parent).</p> + * + * <p>For example, suppose the following class and property files are + * provided: MyResources.class, MyResources_fr_CH.properties, + * MyResources_fr_CH.class, MyResources_fr.properties, + * MyResources_en.properties, and MyResources_es_ES.class. The contents of + * all files are valid (that is, public non-abstract subclasses of + * ResourceBundle with public nullary constructors for the ".class" files, + * syntactically correct ".properties" files). The default locale is + * Locale("en", "UK").</p> + * + * <p>Calling getBundle with the shown locale argument values instantiates + * resource bundles from the following sources:</p> + * + * <ul> + * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent + * MyResources_fr.properties, parent MyResources.class</li> + * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent + * MyResources.class</li> + * <li>Locale("de", "DE"): result MyResources_en.properties, parent + * MyResources.class</li> + * <li>Locale("en", "US"): result MyResources_en.properties, parent + * MyResources.class</li> + * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent + * MyResources.class</li> + * </ul> + * + * <p>The file MyResources_fr_CH.properties is never used because it is hidden + * by MyResources_fr_CH.class.</p> + * + * @param baseName the name of the ResourceBundle + * @param locale A locale + * @param classLoader a ClassLoader + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if any argument is null + * @since 1.2 + */ + // This method is synchronized so that the cache is properly + // handled. + public static synchronized ResourceBundle getBundle + (String baseName, Locale locale, ClassLoader classLoader) + { + // If the default locale changed since the last time we were called, + // all cache entries are invalidated. + Locale defaultLocale = Locale.getDefault(); + if (defaultLocale != lastDefaultLocale) + { + bundleCache = new HashMap(); + lastDefaultLocale = defaultLocale; + } + + // This will throw NullPointerException if any arguments are null. + lookupKey.set(baseName, locale, classLoader); + + Object obj = bundleCache.get(lookupKey); + ResourceBundle rb = null; + + if (obj instanceof ResourceBundle) + { + return (ResourceBundle) obj; + } + else if (obj == nullEntry) + { + // Lookup has failed previously. Fall through. + } + else + { + // First, look for a bundle for the specified locale. We don't want + // the base bundle this time. + boolean wantBase = locale.equals(defaultLocale); + ResourceBundle bundle = tryBundle(baseName, locale, classLoader, + wantBase); + + // Try the default locale if neccessary. + if (bundle == null && !locale.equals(defaultLocale)) + bundle = tryBundle(baseName, defaultLocale, classLoader, true); + + BundleKey key = new BundleKey(baseName, locale, classLoader); + if (bundle == null) + { + // Cache the fact that this lookup has previously failed. + bundleCache.put(key, nullEntry); + } + else + { + // Cache the result and return it. + bundleCache.put(key, bundle); + return bundle; + } + } + + throw new MissingResourceException("Bundle " + baseName + " not found", + baseName, ""); + } + + /** + * Override this method to provide the resource for a keys. This gets + * called by <code>getObject</code>. If you don't have a resource + * for the given key, you should return null instead throwing a + * MissingResourceException. You don't have to ask the parent, getObject() + * already does this; nor should you throw a MissingResourceException. + * + * @param key the key of the resource + * @return the resource for the key, or null if not in bundle + * @throws NullPointerException if key is null + */ + protected abstract Object handleGetObject(String key); + + /** + * This method should return all keys for which a resource exists; you + * should include the enumeration of any parent's keys, after filtering out + * duplicates. + * + * @return an enumeration of the keys + */ + public abstract Enumeration getKeys(); + + /** + * Tries to load a class or a property file with the specified name. + * + * @param localizedName the name + * @param classloader the classloader + * @return the resource bundle if it was loaded, otherwise the backup + */ + private static ResourceBundle tryBundle(String localizedName, + ClassLoader classloader) + { + ResourceBundle bundle = null; + try + { + Class rbClass; + if (classloader == null) + rbClass = Class.forName(localizedName); + else + rbClass = classloader.loadClass(localizedName); + // Note that we do the check up front instead of catching + // ClassCastException. The reason for this is that some crazy + // programs (Eclipse) have classes that do not extend + // ResourceBundle but that have the same name as a property + // bundle; in fact Eclipse relies on ResourceBundle not + // instantiating these classes. + if (ResourceBundle.class.isAssignableFrom(rbClass)) + bundle = (ResourceBundle) rbClass.newInstance(); + } + catch (IllegalAccessException ex) {} + catch (InstantiationException ex) {} + catch (ClassNotFoundException ex) {} + + if (bundle == null) + { + try + { + InputStream is; + String resourceName + = localizedName.replace('.', '/') + ".properties"; + if (classloader == null) + is = ClassLoader.getSystemResourceAsStream(resourceName); + else + is = classloader.getResourceAsStream(resourceName); + if (is != null) + bundle = new PropertyResourceBundle(is); + } + catch (IOException ex) + { + MissingResourceException mre = new MissingResourceException + ("Failed to load bundle: " + localizedName, localizedName, ""); + mre.initCause(ex); + throw mre; + } + } + + return bundle; + } + + /** + * Tries to load a the bundle for a given locale, also loads the backup + * locales with the same language. + * + * @param baseName the raw bundle name, without locale qualifiers + * @param locale the locale + * @param classloader the classloader + * @param bundle the backup (parent) bundle + * @param wantBase whether a resource bundle made only from the base name + * (with no locale information attached) should be returned. + * @return the resource bundle if it was loaded, otherwise the backup + */ + private static ResourceBundle tryBundle(String baseName, Locale locale, + ClassLoader classLoader, + boolean wantBase) + { + String language = locale.getLanguage(); + String country = locale.getCountry(); + String variant = locale.getVariant(); + + int baseLen = baseName.length(); + + // Build up a StringBuffer containing the complete bundle name, fully + // qualified by locale. + StringBuffer sb = new StringBuffer(baseLen + variant.length() + 7); + + sb.append(baseName); + + if (language.length() > 0) + { + sb.append('_'); + sb.append(language); + + if (country.length() > 0) + { + sb.append('_'); + sb.append(country); + + if (variant.length() > 0) + { + sb.append('_'); + sb.append(variant); + } + } + } + + // Now try to load bundles, starting with the most specialized name. + // Build up the parent chain as we go. + String bundleName = sb.toString(); + ResourceBundle first = null; // The most specialized bundle. + ResourceBundle last = null; // The least specialized bundle. + + while (true) + { + ResourceBundle foundBundle = tryBundle(bundleName, classLoader); + if (foundBundle != null) + { + if (first == null) + first = foundBundle; + if (last != null) + last.parent = foundBundle; + foundBundle.locale = locale; + last = foundBundle; + } + int idx = bundleName.lastIndexOf('_'); + // Try the non-localized base name only if we already have a + // localized child bundle, or wantBase is true. + if (idx > baseLen || (idx == baseLen && (first != null || wantBase))) + bundleName = bundleName.substring(0, idx); + else + break; + } + + return first; + } +} diff --git a/libjava/classpath/java/util/Set.java b/libjava/classpath/java/util/Set.java new file mode 100644 index 00000000000..839959e620c --- /dev/null +++ b/libjava/classpath/java/util/Set.java @@ -0,0 +1,264 @@ +/* Set.java -- A collection that prohibits duplicates + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A collection that contains no duplicates. In other words, for two set + * elements e1 and e2, <code>e1.equals(e2)</code> returns false. There + * are additional stipulations on <code>add</code>, <code>equals</code> + * and <code>hashCode</code>, as well as the requirements that constructors + * do not permit duplicate elements. The Set interface is incompatible with + * List; you cannot implement both simultaneously. + * <p> + * + * Note: Be careful about using mutable objects in sets. In particular, + * if a mutable object changes to become equal to another set element, you + * have violated the contract. As a special case of this, a Set is not + * allowed to be an element of itself, without risking undefined behavior. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see SortedSet + * @see HashSet + * @see TreeSet + * @see LinkedHashSet + * @see AbstractSet + * @see Collections#singleton(Object) + * @see Collections#EMPTY_SET + * @since 1.2 + * @status updated to 1.4 + */ +public interface Set extends Collection +{ + /** + * Adds the specified element to the set if it is not already present + * (optional operation). In particular, the comparison algorithm is + * <code>o == null ? e == null : o.equals(e)</code>. Sets need not permit + * all values, and may document what exceptions will be thrown if + * a value is not permitted. + * + * @param o the object to add + * @return true if the object was not previously in the set + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the class of o prevents it from being added + * @throws IllegalArgumentException if some aspect of o prevents it from + * being added + * @throws NullPointerException if null is not permitted in this set + */ + boolean add(Object o); + + /** + * Adds all of the elements of the given collection to this set (optional + * operation). If the argument is also a Set, this returns the mathematical + * <i>union</i> of the two. The behavior is unspecified if the set is + * modified while this is taking place. + * + * @param c the collection to add + * @return true if the set changed as a result + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the class of an element prevents it from + * being added + * @throws IllegalArgumentException if something about an element prevents + * it from being added + * @throws NullPointerException if null is not permitted in this set, or + * if the argument c is null + * @see #add(Object) + */ + boolean addAll(Collection c); + + /** + * Removes all elements from this set (optional operation). This set will + * be empty afterwords, unless an exception occurs. + * + * @throws UnsupportedOperationException if this operation is not allowed + */ + void clear(); + + /** + * Returns true if the set contains the specified element. In other words, + * this looks for <code>o == null ? e == null : o.equals(e)</code>. + * + * @param o the object to look for + * @return true if it is found in the set + * @throws ClassCastException if the type of o is not a valid type + * for this set. + * @throws NullPointerException if o is null and this set doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Returns true if this set contains all elements in the specified + * collection. If the argument is also a set, this is the <i>subset</i> + * relationship. + * + * @param c the collection to check membership in + * @return true if all elements in this set are in c + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support null values. + * @see #contains(Object) + */ + boolean containsAll(Collection c); + + /** + * Compares the specified object to this for equality. For sets, the object + * must be a set, the two must have the same size, and every element in + * one must be in the other. + * + * @param o the object to compare to + * @return true if it is an equal set + */ + boolean equals(Object o); + + /** + * Returns the hash code for this set. In order to satisfy the contract of + * equals, this is the sum of the hashcode of all elements in the set. + * + * @return the sum of the hashcodes of all set elements + * @see #equals(Object) + */ + int hashCode(); + + /** + * Returns true if the set contains no elements. + * + * @return true if the set is empty + */ + boolean isEmpty(); + + /** + * Returns an iterator over the set. The iterator has no specific order, + * unless further specified. + * + * @return a set iterator + */ + Iterator iterator(); + + /** + * Removes the specified element from this set (optional operation). If + * an element e exists, <code>o == null ? e == null : o.equals(e)</code>, + * it is removed from the set. + * + * @param o the object to remove + * @return true if the set changed (an object was removed) + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the type of o is not a valid type + * for this set. + * @throws NullPointerException if o is null and this set doesn't allow + * the removal of a null value. + */ + boolean remove(Object o); + + /** + * Removes from this set all elements contained in the specified collection + * (optional operation). If the argument is a set, this returns the + * <i>asymmetric set difference</i> of the two sets. + * + * @param c the collection to remove from this set + * @return true if this set changed as a result + * @throws UnsupportedOperationException if this operation is not allowed + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support removing null values. + * @see #remove(Object) + */ + boolean removeAll(Collection c); + + /** + * Retains only the elements in this set that are also in the specified + * collection (optional operation). If the argument is also a set, this + * performs the <i>intersection</i> of the two sets. + * + * @param c the collection to keep + * @return true if this set was modified + * @throws UnsupportedOperationException if this operation is not allowed + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support retaining null values. + * @see #remove(Object) + */ + boolean retainAll(Collection c); + + /** + * Returns the number of elements in the set. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is + * the <i>cardinality</i> of the set. + * + * @return the number of elements + */ + int size(); + + /** + * Returns an array containing the elements of this set. If the set + * makes a guarantee about iteration order, the array has the same + * order. The array is distinct from the set; modifying one does not + * affect the other. + * + * @return an array of this set's elements + * @see #toArray(Object[]) + */ + Object[] toArray(); + + /** + * Returns an array containing the elements of this set, of the same runtime + * type of the argument. If the given set is large enough, it is reused, + * and null is inserted in the first unused slot. Otherwise, reflection + * is used to build a new array. If the set makes a guarantee about iteration + * order, the array has the same order. The array is distinct from the set; + * modifying one does not affect the other. + * + * @param a the array to determine the return type; if it is big enough + * it is used and returned + * @return an array holding the elements of the set + * @throws ArrayStoreException if the runtime type of a is not a supertype + * of all elements in the set + * @throws NullPointerException if a is null + * @see #toArray() + */ + Object[] toArray(Object[] a); +} diff --git a/libjava/classpath/java/util/SimpleTimeZone.java b/libjava/classpath/java/util/SimpleTimeZone.java new file mode 100644 index 00000000000..995ccea84ca --- /dev/null +++ b/libjava/classpath/java/util/SimpleTimeZone.java @@ -0,0 +1,1077 @@ +/* java.util.SimpleTimeZone + Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + + +/** + * This class represents a simple time zone offset and handles + * daylight savings. It can only handle one daylight savings rule, so + * it can't represent historical changes. + * + * This object is tightly bound to the Gregorian calendar. It assumes + * a regular seven days week, and the month lengths are that of the + * Gregorian Calendar. It can only handle daylight savings for years + * lying in the AD era. + * + * @see Calendar + * @see GregorianCalendar + * @author Jochen Hoenicke + */ +public class SimpleTimeZone extends TimeZone +{ + /** + * The raw time zone offset in milliseconds to GMT, ignoring + * daylight savings. + * @serial + */ + private int rawOffset; + + /** + * True, if this timezone uses daylight savings, false otherwise. + * @serial + */ + private boolean useDaylight; + + /** + * The daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an hour. + * @serial + * @since JDK1.1.4 + */ + private int dstSavings = 60 * 60 * 1000; + + /** + * The first year, in which daylight savings rules applies. + * @serial + */ + private int startYear; + private static final int DOM_MODE = 1; + private static final int DOW_IN_MONTH_MODE = 2; + private static final int DOW_GE_DOM_MODE = 3; + private static final int DOW_LE_DOM_MODE = 4; + + /** + * The mode of the start rule. This takes one of the following values: + * <dl> + * <dt>DOM_MODE (1)</dt> + * <dd> startDay contains the day in month of the start date, + * startDayOfWeek is unused. </dd> + * <dt>DOW_IN_MONTH_MODE (2)</dt> + * <dd> The startDay gives the day of week in month, and + * startDayOfWeek the day of week. For example startDay=2 and + * startDayOfWeek=Calender.SUNDAY specifies that the change is on + * the second sunday in that month. You must make sure, that this + * day always exists (ie. don't specify the 5th sunday). + * </dd> + * <dt>DOW_GE_DOM_MODE (3)</dt> + * <dd> The start is on the first startDayOfWeek on or after + * startDay. For example startDay=13 and + * startDayOfWeek=Calendar.FRIDAY specifies that the daylight + * savings start on the first FRIDAY on or after the 13th of that + * Month. Make sure that the change is always in the given month, or + * the result is undefined. + * </dd> + * <dt>DOW_LE_DOM_MONTH (4)</dt> + * <dd> The start is on the first startDayOfWeek on or before the + * startDay. Make sure that the change is always in the given + * month, or the result is undefined. + </dd> + * </dl> + * @serial */ + private int startMode; + + /** + * The month in which daylight savings start. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int startMonth; + + /** + * This variable can have different meanings. See startMode for details + * @see #startMode + * @serial + */ + private int startDay; + + /** + * This variable specifies the day of week the change takes place. If + * startMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode + */ + private int startDayOfWeek; + + /** + * This variable specifies the time of change to daylight savings. + * This time is given in milliseconds after midnight local + * standard time. + * @serial + */ + private int startTime; + + /** + * This variable specifies the mode that startTime is specified in. By + * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. For + * startTime, STANDARD_TIME and WALL_TIME are equivalent. + * @serial + */ + private int startTimeMode = WALL_TIME; + + /** + * The month in which daylight savings ends. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int endMonth; + + /** + * This variable gives the mode for the end of daylight savings rule. + * It can take the same values as startMode. + * @serial + * @see #startMode + */ + private int endMode; + + /** + * This variable can have different meanings. See startMode for details + * @serial + * @see #startMode + */ + private int endDay; + + /** + * This variable specifies the day of week the change takes place. If + * endMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode + */ + private int endDayOfWeek; + + /** + * This variable specifies the time of change back to standard time. + * This time is given in milliseconds after midnight local + * standard time. + * @serial + */ + private int endTime; + + /** + * This variable specifies the mode that endTime is specified in. By + * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. + * @serial + */ + private int endTimeMode = WALL_TIME; + + /** + * This variable points to a deprecated array from JDK 1.1. It is + * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. + * The array contains the lengths of the months in the year and is + * assigned from a private static final field to avoid allocating + * the array for every instance of the object. + * Note that static final fields are not serialized. + * @serial + */ + private byte[] monthLength = monthArr; + private static final byte[] monthArr = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, + 31, 30, 31 + }; + + /** + * The version of the serialized data on the stream. + * <dl> + * <dt>0 or not present on stream</dt> + * <dd> JDK 1.1.3 or earlier, only provides this fields: + * rawOffset, startDay, startDayOfWeek, startMonth, startTime, + * startYear, endDay, endDayOfWeek, endMonth, endTime + * </dd> + * <dd> JDK 1.1.4 or later. This includes three new fields, namely + * startMode, endMode and dstSavings. And there is a optional section + * as described in writeObject. + * </dd> + * </dl> + * + * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old + * version. + * + * When streaming out this class it is always written in the latest + * version. + * @serial + * @since JDK1.1.4 + */ + private int serialVersionOnStream = 2; + private static final long serialVersionUID = -403250971215465050L; + + /** + * Constant to indicate that start and end times are specified in standard + * time, without adjusting for daylight savings. + */ + public static final int STANDARD_TIME = 1; + + /** + * Constant to indicate that start and end times are specified in wall + * time, adjusting for daylight savings. This is the default. + */ + public static final int WALL_TIME = 0; + + /** + * Constant to indicate that start and end times are specified in UTC. + */ + public static final int UTC_TIME = 2; + + /** + * Create a <code>SimpleTimeZone</code> with the given time offset + * from GMT and without daylight savings. + * @param rawOffset the time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + */ + public SimpleTimeZone(int rawOffset, String id) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = false; + startYear = 0; + } + + /** + * Create a <code>SimpleTimeZone</code> with the given time offset + * from GMT and with daylight savings. The start/end parameters + * can have different meaning (replace WEEKDAY with a real day of + * week). Only the first two meanings were supported by earlier + * versions of jdk. + * + * <dl> + * <dt><code>day > 0, dayOfWeek = Calendar.WEEKDAY</code></dt> + * <dd>The start/end of daylight savings is on the <code>day</code>-th + * <code>WEEKDAY</code> in the given month. </dd> + * <dt><code>day < 0, dayOfWeek = Calendar.WEEKDAY</code></dt> + * <dd>The start/end of daylight savings is on the <code>-day</code>-th + * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd> + * <dt><code>day > 0, dayOfWeek = 0</code></dt> + * <dd>The start/end of daylight is on the <code>day</code>-th day of + * the month. </dd> + * <dt><code>day > 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> + * <dd>The start/end of daylight is on the first WEEKDAY on or after + * the <code>day</code>-th day of the month. You must make sure that + * this day lies in the same month. </dd> + * <dt><code>day < 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> + * <dd>The start/end of daylight is on the first WEEKDAY on or + * <i>before</i> the <code>-day</code>-th day of the month. You + * must make sure that this day lies in the same month. </dd> + * </dl> + * + * If you give a non existing month, a day that is zero, or too big, + * or a dayOfWeek that is too big, the result is undefined. + * + * The start rule must have a different month than the end rule. + * This restriction shouldn't hurt for all possible time zones. + * + * @param rawOffset The time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + * @param startMonth The start month of daylight savings; use the + * constants in Calendar. + * @param startDayOfWeekInMonth A day in month or a day of week number, as + * described above. + * @param startDayOfWeek The start rule day of week; see above. + * @param startTime A time in millis in standard time. + * @param endMonth The end month of daylight savings; use the + * constants in Calendar. + * @param endDayOfWeekInMonth A day in month or a day of week number, as + * described above. + * @param endDayOfWeek The end rule day of week; see above. + * @param endTime A time in millis in standard time. + * @throws IllegalArgumentException if parameters are invalid or out of + * range. + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = true; + + setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); + setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + if (startMonth == endMonth) + throw new IllegalArgumentException("startMonth and endMonth must be different"); + this.startYear = 0; + } + + /** + * This constructs a new SimpleTimeZone that supports a daylight savings + * rule. The parameter are the same as for the constructor above, except + * there is the additional dstSavaings parameter. + * + * @param dstSavings the amount of savings for daylight savings + * time in milliseconds. This must be positive. + * @since 1.2 + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime, int dstSavings) + { + this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, + startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + + this.dstSavings = dstSavings; + } + + /** + * This constructs a new SimpleTimeZone that supports a daylight savings + * rule. The parameter are the same as for the constructor above, except + * there are the additional startTimeMode, endTimeMode, and dstSavings + * parameters. + * + * @param startTimeMode the mode that start times are specified in. One of + * WALL_TIME, STANDARD_TIME, or UTC_TIME. + * @param endTimeMode the mode that end times are specified in. One of + * WALL_TIME, STANDARD_TIME, or UTC_TIME. + * @param dstSavings the amount of savings for daylight savings + * time in milliseconds. This must be positive. + * @throws IllegalArgumentException if parameters are invalid or out of + * range. + * @since 1.4 + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int startTimeMode, int endMonth, + int endDayOfWeekInMonth, int endDayOfWeek, + int endTime, int endTimeMode, int dstSavings) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = true; + + if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) + throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); + if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) + throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); + this.startTimeMode = startTimeMode; + this.endTimeMode = endTimeMode; + + setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); + setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + if (startMonth == endMonth) + throw new IllegalArgumentException("startMonth and endMonth must be different"); + this.startYear = 0; + + this.dstSavings = dstSavings; + } + + /** + * Sets the first year, where daylight savings applies. The daylight + * savings rule never apply for years in the BC era. Note that this + * is gregorian calendar specific. + * @param year the start year. + */ + public void setStartYear(int year) + { + startYear = year; + useDaylight = true; + } + + /** + * Checks if the month, day, dayOfWeek arguments are in range and + * returns the mode of the rule. + * @param month the month parameter as in the constructor + * @param day the day parameter as in the constructor + * @param dayOfWeek the day of week parameter as in the constructor + * @return the mode of this rule see startMode. + * @exception IllegalArgumentException if parameters are out of range. + * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) + * @see #startMode + */ + private int checkRule(int month, int day, int dayOfWeek) + { + if (month < 0 || month > 11) + throw new IllegalArgumentException("month out of range"); + + int daysInMonth = getDaysInMonth(month, 1); + if (dayOfWeek == 0) + { + if (day <= 0 || day > daysInMonth) + throw new IllegalArgumentException("day out of range"); + return DOM_MODE; + } + else if (dayOfWeek > 0) + { + if (Math.abs(day) > (daysInMonth + 6) / 7) + throw new IllegalArgumentException("dayOfWeekInMonth out of range"); + if (dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + return DOW_IN_MONTH_MODE; + } + else + { + if (day == 0 || Math.abs(day) > daysInMonth) + throw new IllegalArgumentException("day out of range"); + if (dayOfWeek < -Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (day < 0) + return DOW_LE_DOM_MODE; + else + return DOW_GE_DOM_MODE; + } + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with <code>setEndRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param dayOfWeek The day of week where daylight savings start. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @see SimpleTimeZone + */ + public void setStartRule(int month, int day, int dayOfWeek, int time) + { + this.startMode = checkRule(month, day, dayOfWeek); + this.startMonth = month; + this.startDay = day; + this.startDayOfWeek = Math.abs(dayOfWeek); + if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) + this.startTime = time; + else + // Convert from UTC to STANDARD + this.startTime = time + this.rawOffset; + useDaylight = true; + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with <code>setEndRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * Note that this API isn't incredibly well specified. It appears that the + * after flag must override the parameters, since normally, the day and + * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or + * before mode is chosen. But if after == true, this implementation + * overrides the signs of the other arguments. And if dayOfWeek == 0, it + * falls back to the behavior in the other APIs. I guess this should be + * checked against Sun's implementation. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param dayOfWeek The day of week where daylight savings start. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @param after If true, day and dayOfWeek specify first day of week on or + * after day, else first day of week on or before. + * @since 1.2 + * @see SimpleTimeZone + */ + public void setStartRule(int month, int day, int dayOfWeek, int time, + boolean after) + { + // FIXME: XXX: Validate that checkRule and offset processing work with on + // or before mode. + this.startDay = after ? Math.abs(day) : -Math.abs(day); + this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); + this.startMode = (dayOfWeek != 0) + ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) + : checkRule(month, day, dayOfWeek); + this.startDay = Math.abs(this.startDay); + this.startDayOfWeek = Math.abs(this.startDayOfWeek); + + this.startMonth = month; + + if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) + this.startTime = time; + else + // Convert from UTC to STANDARD + this.startTime = time + this.rawOffset; + useDaylight = true; + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with <code>setEndRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @see SimpleTimeZone + * @since 1.2 + */ + public void setStartRule(int month, int day, int time) + { + setStartRule(month, day, 0, time); + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with <code>setStartRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param dayOfWeek A day of week, when daylight savings ends. + * @param time A time in millis in standard time. + * @see #setStartRule(int, int, int, int) + */ + public void setEndRule(int month, int day, int dayOfWeek, int time) + { + this.endMode = checkRule(month, day, dayOfWeek); + this.endMonth = month; + this.endDay = day; + this.endDayOfWeek = Math.abs(dayOfWeek); + if (this.endTimeMode == WALL_TIME) + this.endTime = time; + else if (this.endTimeMode == STANDARD_TIME) + // Convert from STANDARD to DST + this.endTime = time + this.dstSavings; + else + // Convert from UTC to DST + this.endTime = time + this.rawOffset + this.dstSavings; + useDaylight = true; + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with <code>setStartRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * Note that this API isn't incredibly well specified. It appears that the + * after flag must override the parameters, since normally, the day and + * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or + * before mode is chosen. But if after == true, this implementation + * overrides the signs of the other arguments. And if dayOfWeek == 0, it + * falls back to the behavior in the other APIs. I guess this should be + * checked against Sun's implementation. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param dayOfWeek A day of week, when daylight savings ends. + * @param time A time in millis in standard time. + * @param after If true, day and dayOfWeek specify first day of week on or + * after day, else first day of week on or before. + * @since 1.2 + * @see #setStartRule(int, int, int, int, boolean) + */ + public void setEndRule(int month, int day, int dayOfWeek, int time, + boolean after) + { + // FIXME: XXX: Validate that checkRule and offset processing work with on + // or before mode. + this.endDay = after ? Math.abs(day) : -Math.abs(day); + this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); + this.endMode = (dayOfWeek != 0) + ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) + : checkRule(month, day, dayOfWeek); + this.endDay = Math.abs(this.endDay); + this.endDayOfWeek = Math.abs(endDayOfWeek); + + this.endMonth = month; + + if (this.endTimeMode == WALL_TIME) + this.endTime = time; + else if (this.endTimeMode == STANDARD_TIME) + // Convert from STANDARD to DST + this.endTime = time + this.dstSavings; + else + // Convert from UTC to DST + this.endTime = time + this.rawOffset + this.dstSavings; + useDaylight = true; + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with <code>setStartRule</code> or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param time A time in millis in standard time. + * @see #setStartRule(int, int, int) + */ + public void setEndRule(int month, int day, int time) + { + setEndRule(month, day, 0, time); + } + + /** + * Gets the time zone offset, for current date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * + * In the standard JDK the results given by this method may result in + * inaccurate results at the end of February or the beginning of March. + * To avoid this, you should use Calendar instead: + * <code>offset = cal.get(Calendar.ZONE_OFFSET) + * + cal.get(Calendar.DST_OFFSET);</code> + * + * This version doesn't suffer this inaccuracy. + * + * The arguments don't follow the approach for setting start and end rules. + * The day must be a positive number and dayOfWeek must be a positive value + * from Calendar. dayOfWeek is redundant, but must match the other values + * or an inaccurate result may be returned. + * + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week; this must match the other fields. + * @param millis the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. + * @throws IllegalArgumentException if arguments are incorrect. + */ + public int getOffset(int era, int year, int month, int day, int dayOfWeek, + int millis) + { + int daysInMonth = getDaysInMonth(month, year); + if (day < 1 || day > daysInMonth) + throw new IllegalArgumentException("day out of range"); + if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (month < Calendar.JANUARY || month > Calendar.DECEMBER) + throw new IllegalArgumentException("month out of range:" + month); + + // This method is called by Calendar, so we mustn't use that class. + int daylightSavings = 0; + if (useDaylight && era == GregorianCalendar.AD && year >= startYear) + { + // This does only work for Gregorian calendars :-( + // This is mainly because setStartYear doesn't take an era. + boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, + startMode, startMonth, startDay, + startDayOfWeek, startTime); + boolean beforeEnd = isBefore(year, month, day, dayOfWeek, + millis + dstSavings, + endMode, endMonth, endDay, endDayOfWeek, + endTime); + + if (startMonth < endMonth) + // use daylight savings, if the date is after the start of + // savings, and before the end of savings. + daylightSavings = afterStart && beforeEnd ? dstSavings : 0; + else + // use daylight savings, if the date is before the end of + // savings, or after the start of savings. + daylightSavings = beforeEnd || afterStart ? dstSavings : 0; + } + return rawOffset + daylightSavings; + } + + /** + * Returns the time zone offset to GMT in milliseconds, ignoring + * day light savings. + * @return the time zone offset. + */ + public int getRawOffset() + { + return rawOffset; + } + + /** + * Sets the standard time zone offset to GMT. + * @param rawOffset The time offset from GMT in milliseconds. + */ + public void setRawOffset(int rawOffset) + { + this.rawOffset = rawOffset; + } + + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * @return the daylight savings offset in milliseconds. + * + * @since 1.2 + */ + public int getDSTSavings() + { + return dstSavings; + } + + /** + * Sets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. + * + * @param dstSavings the daylight savings offset in milliseconds. + * + * @since 1.2 + */ + public void setDSTSavings(int dstSavings) + { + if (dstSavings <= 0) + throw new IllegalArgumentException("illegal value for dstSavings"); + + this.dstSavings = dstSavings; + } + + /** + * Returns if this time zone uses daylight savings time. + * @return true, if we use daylight savings time, false otherwise. + */ + public boolean useDaylightTime() + { + return useDaylight; + } + + /** + * Returns the number of days in the given month. + * Uses gregorian rules prior to 1582 (The default and earliest cutover) + * @param month The month, zero based; use one of the Calendar constants. + * @param year The year. + */ + private int getDaysInMonth(int month, int year) + { + if (month == Calendar.FEBRUARY) + { + if ((year & 3) != 0) + return 28; + + // Assume default Gregorian cutover, + // all years prior to this must be Julian + if (year < 1582) + return 29; + + // Gregorian rules + return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28; + } + else + return monthArr[month]; + } + + /** + * Checks if the date given in calXXXX, is before the change between + * dst and standard time. + * @param calYear the year of the date to check (for leap day checking). + * @param calMonth the month of the date to check. + * @param calDay the day of month of the date to check. + * @param calDayOfWeek the day of week of the date to check. + * @param calMillis the millis of day of the date to check (standard time). + * @param mode the change mode; same semantic as startMode. + * @param month the change month; same semantic as startMonth. + * @param day the change day; same semantic as startDay. + * @param dayOfWeek the change day of week; + * @param millis the change time in millis since midnight standard time. + * same semantic as startDayOfWeek. + * @return true, if cal is before the change, false if cal is on + * or after the change. + */ + private boolean isBefore(int calYear, int calMonth, int calDayOfMonth, + int calDayOfWeek, int calMillis, int mode, + int month, int day, int dayOfWeek, int millis) + { + // This method is called by Calendar, so we mustn't use that class. + // We have to do all calculations by hand. + // check the months: + // XXX - this is not correct: + // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may + // be in a different month. + if (calMonth != month) + return calMonth < month; + + // check the day: + switch (mode) + { + case DOM_MODE: + if (calDayOfMonth != day) + return calDayOfMonth < day; + break; + case DOW_IN_MONTH_MODE: + { + // This computes the day of month of the day of type + // "dayOfWeek" that lies in the same (sunday based) week as cal. + calDayOfMonth += (dayOfWeek - calDayOfWeek); + + // Now we convert it to 7 based number (to get a one based offset + // after dividing by 7). If we count from the end of the + // month, we get want a -7 based number counting the days from + // the end: + if (day < 0) + calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; + else + calDayOfMonth += 6; + + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 7 8 9 10 11 12 -36-35-34-33-32-31 + // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 + // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 + // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 + // 34 35 36 -9 -8 -7 + // Now we calculate the day of week in month: + int week = calDayOfMonth / 7; + + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 + // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 + // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 + // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 + // 4 5 5 -1 -1 -1 + if (week != day) + return week < day; + + if (calDayOfWeek != dayOfWeek) + return calDayOfWeek < dayOfWeek; + + // daylight savings starts/ends on the given day. + break; + } + case DOW_LE_DOM_MODE: + // The greatest sunday before or equal December, 12 + // is the same as smallest sunday after or equal December, 6. + day = Math.abs(day) - 6; + case DOW_GE_DOM_MODE: + // Calculate the day of month of the day of type + // "dayOfWeek" that lies before (or on) the given date. + calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek + - dayOfWeek; + if (calDayOfMonth < day) + return true; + if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) + return false; + + // now we have the same day + break; + } + + // the millis decides: + return (calMillis < millis); + } + + /** + * Determines if the given date is in daylight savings time. + * @return true, if it is in daylight savings time, false otherwise. + */ + public boolean inDaylightTime(Date date) + { + Calendar cal = Calendar.getInstance(this); + cal.setTime(date); + return (cal.get(Calendar.DST_OFFSET) != 0); + } + + /** + * Generates the hashCode for the SimpleDateFormat object. It is + * the rawOffset, possibly, if useDaylightSavings is true, xored + * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. + */ + public synchronized int hashCode() + { + return rawOffset + ^ (useDaylight + ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth + ^ endDay ^ endDayOfWeek ^ endTime : 0); + } + + public synchronized boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof SimpleTimeZone)) + return false; + SimpleTimeZone zone = (SimpleTimeZone) o; + if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID()) + || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) + return false; + if (! useDaylight) + return true; + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); + } + + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the other object is a SimpleTimeZone, + * the raw offsets and useDaylight are identical and if useDaylight + * is true, also the start and end datas are identical. + * @return true if this zone uses the same rule. + */ + public boolean hasSameRules(TimeZone other) + { + if (this == other) + return true; + if (! (other instanceof SimpleTimeZone)) + return false; + SimpleTimeZone zone = (SimpleTimeZone) other; + if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset + || useDaylight != zone.useDaylight) + return false; + if (! useDaylight) + return true; + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); + } + + /** + * Returns a string representation of this SimpleTimeZone object. + * @return a string representation of this SimpleTimeZone object. + */ + public String toString() + { + // the test for useDaylight is an incompatibility to jdk1.2, but + // I think this shouldn't hurt. + return getClass().getName() + "[" + "id=" + getID() + ",offset=" + + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight=" + + useDaylight + + (useDaylight + ? ",startYear=" + startYear + ",startMode=" + startMode + + ",startMonth=" + startMonth + ",startDay=" + startDay + + ",startDayOfWeek=" + startDayOfWeek + ",startTime=" + + startTime + ",startTimeMode=" + startTimeMode + ",endMode=" + + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay + + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime + + ",endTimeMode=" + endTimeMode : "") + "]"; + } + + /** + * Reads a serialized simple time zone from stream. + * @see #writeObject + */ + private void readObject(java.io.ObjectInputStream input) + throws java.io.IOException, ClassNotFoundException + { + input.defaultReadObject(); + if (serialVersionOnStream == 0) + { + // initialize the new fields to default values. + dstSavings = 60 * 60 * 1000; + endMode = DOW_IN_MONTH_MODE; + startMode = DOW_IN_MONTH_MODE; + startTimeMode = WALL_TIME; + endTimeMode = WALL_TIME; + serialVersionOnStream = 2; + } + else + { + int length = input.readInt(); + byte[] byteArray = new byte[length]; + input.read(byteArray, 0, length); + if (length >= 4) + { + // Lets hope that Sun does extensions to the serialized + // form in a sane manner. + startDay = byteArray[0]; + startDayOfWeek = byteArray[1]; + endDay = byteArray[2]; + endDayOfWeek = byteArray[3]; + } + } + } + + /** + * Serializes this object to a stream. @serialdata The object is + * first written in the old JDK 1.1 format, so that it can be read + * by by the old classes. This means, that the + * <code>start/endDay(OfWeek)</code>-Fields are written in the + * DOW_IN_MONTH_MODE rule, since this was the only supported rule + * in 1.1. + * + * In the optional section, we write first the length of an byte + * array as int and afterwards the byte array itself. The byte + * array contains in this release four elements, namely the real + * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. + * These fields are needed, because for compatibility reasons only + * approximative values are written to the required section, as + * described above. + */ + private void writeObject(java.io.ObjectOutputStream output) + throws java.io.IOException + { + byte[] byteArray = new byte[] + { + (byte) startDay, (byte) startDayOfWeek, (byte) endDay, + (byte) endDayOfWeek + }; + + /* calculate the approximation for JDK 1.1 */ + switch (startMode) + { + case DOM_MODE: + startDayOfWeek = Calendar.SUNDAY; // random day of week + + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + startDay = (startDay + 6) / 7; + } + switch (endMode) + { + case DOM_MODE: + endDayOfWeek = Calendar.SUNDAY; + + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + endDay = (endDay + 6) / 7; + } + + // the required part: + output.defaultWriteObject(); + // the optional part: + output.writeInt(byteArray.length); + output.write(byteArray, 0, byteArray.length); + } +} diff --git a/libjava/classpath/java/util/SortedMap.java b/libjava/classpath/java/util/SortedMap.java new file mode 100644 index 00000000000..acfbd0d3639 --- /dev/null +++ b/libjava/classpath/java/util/SortedMap.java @@ -0,0 +1,173 @@ +/* SortedMap.java -- A map that makes guarantees about the order of its keys + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A map which guarantees its key's iteration order. The entries in the + * map are related by the <i>natural ordering</i> of the keys if they + * are Comparable, or by the provided Comparator. Additional operations + * take advantage of the sorted nature of the map. + * <p> + * + * All keys entered in the map must be mutually comparable; in other words, + * <code>k1.compareTo(k2)</code> or <code>comparator.compare(k1, k2)</code> + * must not throw a ClassCastException. The ordering must be <i>consistent + * with equals</i> (see {@link Comparator} for this definition), if the + * map is to obey the general contract of the Map interface. If not, + * the results are well-defined, but probably not what you wanted. + * <p> + * + * It is recommended that all implementing classes provide four constructors: + * 1) one that takes no arguments and builds an empty map sorted by natural + * order of the keys; 2) one that takes a Comparator for the sorting order; + * 3) one that takes a Map and sorts according to the natural order of its + * keys; and 4) one that takes a SortedMap and sorts by the same comparator. + * Unfortunately, the Java language does not provide a way to enforce this. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see TreeMap + * @see SortedSet + * @see Comparable + * @see Comparator + * @see Collection + * @see ClassCastException + * @since 1.2 + * @status updated to 1.4 + */ +public interface SortedMap extends Map +{ + /** + * Returns the comparator used in sorting this map, or null if it is + * the keys' natural ordering. + * + * @return the sorting comparator + */ + Comparator comparator(); + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key + * @throws NoSuchElementException if this map is empty. + */ + Object firstKey(); + + /** + * Returns a view of the portion of the map strictly less than toKey. The + * view is backed by this map, so changes in one show up in the other. + * The submap supports all optional operations of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its successor + * object in to toKey. For example, for Integers, you could request + * <code>headMap(new Integer(limit.intValue() + 1))</code>. + * + * @param toKey the exclusive upper range of the submap + * @return the submap + * @throws ClassCastException if toKey is not comparable to the map contents + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range + * @throws NullPointerException if toKey is null but the map does not allow + * null keys + */ + SortedMap headMap(Object toKey); + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key + * @throws NoSuchElementException if this map is empty. + */ + Object lastKey(); + + /** + * Returns a view of the portion of the map greater than or equal to + * fromKey, and strictly less than toKey. The view is backed by this map, + * so changes in one show up in the other. The submap supports all + * optional operations of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * <code>subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromKey the inclusive lower range of the submap + * @param toKey the exclusive upper range of the submap + * @return the submap + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys + */ + SortedMap subMap(Object fromKey, Object toKey); + + /** + * Returns a view of the portion of the map greater than or equal to + * fromKey. The view is backed by this map, so changes in one show up + * in the other. The submap supports all optional operations of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, fromKey, is + * included; if you do not want this value to be included, pass its successor object in + * to fromKey. For example, for Integers, you could request + * <code>tailMap(new Integer(limit.intValue() + 1))</code>. + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not allow + * null keys + */ + SortedMap tailMap(Object fromKey); +} diff --git a/libjava/classpath/java/util/SortedSet.java b/libjava/classpath/java/util/SortedSet.java new file mode 100644 index 00000000000..48a24a8e0f9 --- /dev/null +++ b/libjava/classpath/java/util/SortedSet.java @@ -0,0 +1,176 @@ +/* SortedSet.java -- A set that makes guarantees about the order of its + elements + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A set which guarantees its iteration order. The elements in the set + * are related by the <i>natural ordering</i> if they are Comparable, or + * by the provided Comparator. Additional operations take advantage of + * the sorted nature of the set. + * <p> + * + * All elements entered in the set must be mutually comparable; in other words, + * <code>k1.compareTo(k2)</code> or <code>comparator.compare(k1, k2)</code> + * must not throw a ClassCastException. The ordering must be <i>consistent + * with equals</i> (see {@link Comparator} for this definition), if the + * set is to obey the general contract of the Set interface. If not, + * the results are well-defined, but probably not what you wanted. + * <p> + * + * It is recommended that all implementing classes provide four constructors: + * 1) one that takes no arguments and builds an empty set sorted by natural + * order of the elements; 2) one that takes a Comparator for the sorting order; + * 3) one that takes a Set and sorts according to the natural order of its + * elements; and 4) one that takes a SortedSet and sorts by the same + * comparator. Unfortunately, the Java language does not provide a way to + * enforce this. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Set + * @see TreeSet + * @see SortedMap + * @see Collection + * @see Comparable + * @see Comparator + * @see ClassCastException + * @since 1.2 + * @status updated to 1.4 + */ +public interface SortedSet extends Set +{ + /** + * Returns the comparator used in sorting this set, or null if it is + * the elements' natural ordering. + * + * @return the sorting comparator + */ + Comparator comparator(); + + /** + * Returns the first (lowest sorted) element in the set. + * + * @return the first element + * @throws NoSuchElementException if the set is empty. + */ + Object first(); + + /** + * Returns a view of the portion of the set strictly less than toElement. The + * view is backed by this set, so changes in one show up in the other. + * The subset supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of toElement. Note that the endpoint, toElement, + * is not included; if you want this value included, pass its successor object in to + * toElement. For example, for Integers, you could request + * <code>headSet(new Integer(limit.intValue() + 1))</code>. + * + * @param toElement the exclusive upper range of the subset + * @return the subset + * @throws ClassCastException if toElement is not comparable to the set + * contents + * @throws IllegalArgumentException if this is a subSet, and toElement is out + * of range + * @throws NullPointerException if toElement is null but the set does not + * allow null elements + */ + SortedSet headSet(Object toElement); + + /** + * Returns the last (highest sorted) element in the set. + * + * @return the last element + * @throws NoSuchElementException if the set is empty. + */ + Object last(); + + /** + * Returns a view of the portion of the set greater than or equal to + * fromElement, and strictly less than toElement. The view is backed by + * this set, so changes in one show up in the other. The subset supports all + * optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement and toElement. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you can request + * <code>subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromElement the inclusive lower range of the subset + * @param toElement the exclusive upper range of the subset + * @return the subset + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements + */ + SortedSet subSet(Object fromElement, Object toElement); + + /** + * Returns a view of the portion of the set greater than or equal to + * fromElement. The view is backed by this set, so changes in one show up + * in the other. The subset supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement. Note that the endpoint, + * fromElement, is included; if you do not want this value to be included, pass its + * successor object in to fromElement. For example, for Integers, you could request + * <code>tailSet(new Integer(limit.intValue() + 1))</code>. + * + * @param fromElement the inclusive lower range of the subset + * @return the subset + * @throws ClassCastException if fromElement is not comparable to the set + * contents + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements + */ + SortedSet tailSet(Object fromElement); +} diff --git a/libjava/classpath/java/util/Stack.java b/libjava/classpath/java/util/Stack.java new file mode 100644 index 00000000000..730ce177cd1 --- /dev/null +++ b/libjava/classpath/java/util/Stack.java @@ -0,0 +1,158 @@ +/* Stack.java - Class that provides a Last In First Out (LIFO) + datatype, known more commonly as a Stack + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct + +/** + * Stack provides a Last In First Out (LIFO) data type, commonly known + * as a Stack. Stack itself extends Vector and provides the additional + * methods for stack manipulation (push, pop, peek). You can also seek for + * the 1-based position of an element on the stack. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @see AbstractList + * @see LinkedList + * @since 1.0 + * @status updated to 1.4 + */ +public class Stack extends Vector +{ + // We could use Vector methods internally for the following methods, + // but have used Vector fields directly for efficiency (i.e. this + // often reduces out duplicate bounds checking). + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1224463164541339165L; + + /** + * This constructor creates a new Stack, initially empty + */ + public Stack() + { + } + + /** + * Pushes an Object onto the top of the stack. This method is effectively + * the same as addElement(item). + * + * @param item the Object to push onto the stack + * @return the Object pushed onto the stack + * @see Vector#addElement(Object) + */ + public Object push(Object item) + { + // When growing the Stack, use the Vector routines in case more + // memory is needed. + // Note: spec indicates that this method *always* returns obj passed in! + + addElement(item); + return item; + } + + /** + * Pops an item from the stack and returns it. The item popped is + * removed from the Stack. + * + * @return the Object popped from the stack + * @throws EmptyStackException if the stack is empty + */ + public synchronized Object pop() + { + if (elementCount == 0) + throw new EmptyStackException(); + + modCount++; + Object obj = elementData[--elementCount]; + + // Set topmost element to null to assist the gc in cleanup. + elementData[elementCount] = null; + return obj; + } + + /** + * Returns the top Object on the stack without removing it. + * + * @return the top Object on the stack + * @throws EmptyStackException if the stack is empty + */ + public synchronized Object peek() + { + if (elementCount == 0) + throw new EmptyStackException(); + + return elementData[elementCount - 1]; + } + + /** + * Tests if the stack is empty. + * + * @return true if the stack contains no items, false otherwise + */ + public synchronized boolean empty() + { + return elementCount == 0; + } + + /** + * Returns the position of an Object on the stack, with the top + * most Object being at position 1, and each Object deeper in the + * stack at depth + 1. + * + * @param o The object to search for + * @return The 1 based depth of the Object, or -1 if the Object + * is not on the stack + */ + public synchronized int search(Object o) + { + int i = elementCount; + while (--i >= 0) + if (equals(o, elementData[i])) + return elementCount - i; + return -1; + } +} diff --git a/libjava/classpath/java/util/StringTokenizer.java b/libjava/classpath/java/util/StringTokenizer.java new file mode 100644 index 00000000000..dcc192c855a --- /dev/null +++ b/libjava/classpath/java/util/StringTokenizer.java @@ -0,0 +1,268 @@ +/* StringTokenizer -- breaks a String into tokens + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class splits a string into tokens. The caller can set on which + * delimiters the string should be split and if the delimiters should be + * returned. This is much simpler than {@link java.io.StreamTokenizer}. + * + * <p>You may change the delimiter set on the fly by calling + * nextToken(String). But the semantic is quite difficult; it even + * depends on calling <code>hasMoreTokens()</code>. You should call + * <code>hasMoreTokens()</code> before, otherwise the old delimiters + * after the last token are candidates for being returned. + * + * <p>If you want to get the delimiters, you have to use the three argument + * constructor. The delimiters are returned as token consisting of a + * single character. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see java.io.StreamTokenizer + * @status updated to 1.4 + */ +public class StringTokenizer implements Enumeration +{ + // WARNING: StringTokenizer is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The position in the str, where we currently are. + */ + private int pos; + + /** + * The string that should be split into tokens. + */ + private final String str; + + /** + * The length of the string. + */ + private final int len; + + /** + * The string containing the delimiter characters. + */ + private String delim; + + /** + * Tells, if we should return the delimiters. + */ + private final boolean retDelims; + + /** + * Creates a new StringTokenizer for the string <code>str</code>, + * that should split on the default delimiter set (space, tab, + * newline, return and formfeed), and which doesn't return the + * delimiters. + * + * @param str The string to split + * @throws NullPointerException if str is null + */ + public StringTokenizer(String str) + { + this(str, " \t\n\r\f", false); + } + + /** + * Create a new StringTokenizer, that splits the given string on + * the given delimiter characters. It doesn't return the delimiter + * characters. + * + * @param str the string to split + * @param delim a string containing all delimiter characters + * @throws NullPointerException if either argument is null + */ + public StringTokenizer(String str, String delim) + { + this(str, delim, false); + } + + /** + * Create a new StringTokenizer, that splits the given string on + * the given delimiter characters. If you set + * <code>returnDelims</code> to <code>true</code>, the delimiter + * characters are returned as tokens of their own. The delimiter + * tokens always consist of a single character. + * + * @param str the string to split + * @param delim a string containing all delimiter characters + * @param returnDelims tells, if you want to get the delimiters + * @throws NullPointerException if str or delim is null + */ + public StringTokenizer(String str, String delim, boolean returnDelims) + { + len = str.length(); + this.str = str; + // The toString() hack causes the NullPointerException. + this.delim = delim.toString(); + this.retDelims = returnDelims; + this.pos = 0; + } + + /** + * Tells if there are more tokens. + * + * @return true if the next call of nextToken() will succeed + */ + public boolean hasMoreTokens() + { + if (! retDelims) + { + while (pos < len && delim.indexOf(str.charAt(pos)) >= 0) + pos++; + } + return pos < len; + } + + /** + * Returns the nextToken, changing the delimiter set to the given + * <code>delim</code>. The change of the delimiter set is + * permanent, ie. the next call of nextToken(), uses the same + * delimiter set. + * + * @param delim a string containing the new delimiter characters + * @return the next token with respect to the new delimiter characters + * @throws NoSuchElementException if there are no more tokens + * @throws NullPointerException if delim is null + */ + public String nextToken(String delim) throws NoSuchElementException + { + this.delim = delim; + return nextToken(); + } + + /** + * Returns the nextToken of the string. + * + * @return the next token with respect to the current delimiter characters + * @throws NoSuchElementException if there are no more tokens + */ + public String nextToken() throws NoSuchElementException + { + if (pos < len && delim.indexOf(str.charAt(pos)) >= 0) + { + if (retDelims) + return str.substring(pos, ++pos); + while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0); + } + if (pos < len) + { + int start = pos; + while (++pos < len && delim.indexOf(str.charAt(pos)) < 0); + + return str.substring(start, pos); + } + throw new NoSuchElementException(); + } + + /** + * This does the same as hasMoreTokens. This is the + * <code>Enumeration</code> interface method. + * + * @return true, if the next call of nextElement() will succeed + * @see #hasMoreTokens() + */ + public boolean hasMoreElements() + { + return hasMoreTokens(); + } + + /** + * This does the same as nextTokens. This is the + * <code>Enumeration</code> interface method. + * + * @return the next token with respect to the current delimiter characters + * @throws NoSuchElementException if there are no more tokens + * @see #nextToken() + */ + public Object nextElement() throws NoSuchElementException + { + return nextToken(); + } + + /** + * This counts the number of remaining tokens in the string, with + * respect to the current delimiter set. + * + * @return the number of times <code>nextTokens()</code> will succeed + * @see #nextToken() + */ + public int countTokens() + { + int count = 0; + int delimiterCount = 0; + boolean tokenFound = false; // Set when a non-delimiter is found + int tmpPos = pos; + + // Note for efficiency, we count up the delimiters rather than check + // retDelims every time we encounter one. That way, we can + // just do the conditional once at the end of the method + while (tmpPos < len) + { + if (delim.indexOf(str.charAt(tmpPos++)) >= 0) + { + if (tokenFound) + { + // Got to the end of a token + count++; + tokenFound = false; + } + delimiterCount++; // Increment for this delimiter + } + else + { + tokenFound = true; + // Get to the end of the token + while (tmpPos < len + && delim.indexOf(str.charAt(tmpPos)) < 0) + ++tmpPos; + } + } + + // Make sure to count the last token + if (tokenFound) + count++; + + // if counting delmiters add them into the token count + return retDelims ? count + delimiterCount : count; + } +} // class StringTokenizer diff --git a/libjava/classpath/java/util/TimeZone.java b/libjava/classpath/java/util/TimeZone.java new file mode 100644 index 00000000000..99df11df5f9 --- /dev/null +++ b/libjava/classpath/java/util/TimeZone.java @@ -0,0 +1,1523 @@ +/* java.util.TimeZone + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormatSymbols; + +/** + * This class represents a time zone offset and handles daylight savings. + * + * You can get the default time zone with <code>getDefault</code>. + * This represents the time zone where program is running. + * + * Another way to create a time zone is <code>getTimeZone</code>, where + * you can give an identifier as parameter. For instance, the identifier + * of the Central European Time zone is "CET". + * + * With the <code>getAvailableIDs</code> method, you can get all the + * supported time zone identifiers. + * + * @see Calendar + * @see SimpleTimeZone + * @author Jochen Hoenicke + */ +public abstract class TimeZone implements java.io.Serializable, Cloneable +{ + + /** + * Constant used to indicate that a short timezone abbreviation should + * be returned, such as "EST" + */ + public static final int SHORT = 0; + + /** + * Constant used to indicate that a long timezone name should be + * returned, such as "Eastern Standard Time". + */ + public static final int LONG = 1; + + /** + * The time zone identifier, e.g. PST. + */ + private String ID; + + /** + * The default time zone, as returned by getDefault. + */ + private static TimeZone defaultZone0; + + /** + * Tries to get the default TimeZone for this system if not already + * set. It will call <code>getDefaultTimeZone(String)</code> with + * the result of <code>System.getProperty("user.timezone")</code>. + * If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>. + * If that also fails GMT is returned. + */ + private static synchronized TimeZone defaultZone() + { + /* Look up default timezone */ + if (defaultZone0 == null) + { + defaultZone0 = (TimeZone) AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + TimeZone zone = null; + + // Prefer System property user.timezone. + String tzid = System.getProperty("user.timezone"); + if (tzid != null && !tzid.equals("")) + zone = getDefaultTimeZone(tzid); + + // Try platfom specific way. + if (zone == null) + zone = VMTimeZone.getDefaultTimeZoneId(); + + // Fall back on GMT. + if (zone == null) + zone = (TimeZone) timezones().get("GMT"); + + return zone; + } + }); + } + + return defaultZone0; + } + + private static final long serialVersionUID = 3581463369166924961L; + + /** + * HashMap for timezones by ID. + */ + private static HashMap timezones0; + /* initialize this static field lazily to overhead if + * it is not needed: + */ + // Package-private to avoid a trampoline. + static synchronized HashMap timezones() + { + if (timezones0 == null) + { + HashMap timezones = new HashMap(); + timezones0 = timezones; + + TimeZone tz; + // Automatically generated by scripts/timezones.pl + // XXX - Should we read this data from a file? + tz = new SimpleTimeZone(-11000 * 3600, "MIT"); + timezones0.put("MIT", tz); + timezones0.put("Pacific/Apia", tz); + timezones0.put("Pacific/Midway", tz); + timezones0.put("Pacific/Niue", tz); + timezones0.put("Pacific/Pago_Pago", tz); + tz = new SimpleTimeZone + (-10000 * 3600, "America/Adak", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Adak", tz); + tz = new SimpleTimeZone(-10000 * 3600, "HST"); + timezones0.put("HST", tz); + timezones0.put("Pacific/Fakaofo", tz); + timezones0.put("Pacific/Honolulu", tz); + timezones0.put("Pacific/Johnston", tz); + timezones0.put("Pacific/Rarotonga", tz); + timezones0.put("Pacific/Tahiti", tz); + tz = new SimpleTimeZone(-9500 * 3600, "Pacific/Marquesas"); + timezones0.put("Pacific/Marquesas", tz); + tz = new SimpleTimeZone + (-9000 * 3600, "AST", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("AST", tz); + timezones0.put("America/Anchorage", tz); + timezones0.put("America/Juneau", tz); + timezones0.put("America/Nome", tz); + timezones0.put("America/Yakutat", tz); + tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier"); + timezones0.put("Pacific/Gambier", tz); + tz = new SimpleTimeZone + (-8000 * 3600, "PST", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("PST", tz); + timezones0.put("PST8PDT", tz); + timezones0.put("America/Dawson", tz); + timezones0.put("America/Los_Angeles", tz); + timezones0.put("America/Tijuana", tz); + timezones0.put("America/Vancouver", tz); + timezones0.put("America/Whitehorse", tz); + timezones0.put("US/Pacific-New", tz); + tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn"); + timezones0.put("Pacific/Pitcairn", tz); + tz = new SimpleTimeZone + (-7000 * 3600, "MST", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("MST", tz); + timezones0.put("MST7MDT", tz); + timezones0.put("America/Boise", tz); + timezones0.put("America/Chihuahua", tz); + timezones0.put("America/Denver", tz); + timezones0.put("America/Edmonton", tz); + timezones0.put("America/Inuvik", tz); + timezones0.put("America/Mazatlan", tz); + timezones0.put("America/Shiprock", tz); + timezones0.put("America/Yellowknife", tz); + tz = new SimpleTimeZone(-7000 * 3600, "MST7"); + timezones0.put("MST7", tz); + timezones0.put("PNT", tz); + timezones0.put("America/Dawson_Creek", tz); + timezones0.put("America/Hermosillo", tz); + timezones0.put("America/Phoenix", tz); + tz = new SimpleTimeZone + (-6000 * 3600, "CST", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("CST", tz); + timezones0.put("CST6CDT", tz); + timezones0.put("America/Cambridge_Bay", tz); + timezones0.put("America/Cancun", tz); + timezones0.put("America/Chicago", tz); + timezones0.put("America/Menominee", tz); + timezones0.put("America/Merida", tz); + timezones0.put("America/Mexico_City", tz); + timezones0.put("America/Monterrey", tz); + timezones0.put("America/Rainy_River", tz); + timezones0.put("America/Winnipeg", tz); + tz = new SimpleTimeZone(-6000 * 3600, "America/Belize"); + timezones0.put("America/Belize", tz); + timezones0.put("America/Costa_Rica", tz); + timezones0.put("America/El_Salvador", tz); + timezones0.put("America/Guatemala", tz); + timezones0.put("America/Managua", tz); + timezones0.put("America/Regina", tz); + timezones0.put("America/Swift_Current", tz); + timezones0.put("America/Tegucigalpa", tz); + timezones0.put("Pacific/Galapagos", tz); + tz = new SimpleTimeZone + (-6000 * 3600, "Pacific/Easter", + Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600, + Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600); + timezones0.put("Pacific/Easter", tz); + tz = new SimpleTimeZone + (-5000 * 3600, "America/Grand_Turk", + Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Grand_Turk", tz); + timezones0.put("America/Havana", tz); + tz = new SimpleTimeZone(-5000 * 3600, "EST5"); + timezones0.put("EST5", tz); + timezones0.put("IET", tz); + timezones0.put("America/Bogota", tz); + timezones0.put("America/Cayman", tz); + timezones0.put("America/Eirunepe", tz); + timezones0.put("America/Guayaquil", tz); + timezones0.put("America/Indiana/Indianapolis", tz); + timezones0.put("America/Indiana/Knox", tz); + timezones0.put("America/Indiana/Marengo", tz); + timezones0.put("America/Indiana/Vevay", tz); + timezones0.put("America/Indianapolis", tz); + timezones0.put("America/Iqaluit", tz); + timezones0.put("America/Jamaica", tz); + timezones0.put("America/Lima", tz); + timezones0.put("America/Panama", tz); + timezones0.put("America/Pangnirtung", tz); + timezones0.put("America/Port-au-Prince", tz); + timezones0.put("America/Porto_Acre", tz); + timezones0.put("America/Rankin_Inlet", tz); + tz = new SimpleTimeZone + (-5000 * 3600, "EST", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("EST", tz); + timezones0.put("EST5EDT", tz); + timezones0.put("America/Detroit", tz); + timezones0.put("America/Kentucky/Louisville", tz); + timezones0.put("America/Kentucky/Monticello", tz); + timezones0.put("America/Louisville", tz); + timezones0.put("America/Montreal", tz); + timezones0.put("America/Nassau", tz); + timezones0.put("America/New_York", tz); + timezones0.put("America/Nipigon", tz); + timezones0.put("America/Thunder_Bay", tz); + tz = new SimpleTimeZone(-4000 * 3600, "PRT"); + timezones0.put("PRT", tz); + timezones0.put("America/Anguilla", tz); + timezones0.put("America/Antigua", tz); + timezones0.put("America/Aruba", tz); + timezones0.put("America/Barbados", tz); + timezones0.put("America/Boa_Vista", tz); + timezones0.put("America/Caracas", tz); + timezones0.put("America/Curacao", tz); + timezones0.put("America/Dominica", tz); + timezones0.put("America/Grenada", tz); + timezones0.put("America/Guadeloupe", tz); + timezones0.put("America/Guyana", tz); + timezones0.put("America/La_Paz", tz); + timezones0.put("America/Manaus", tz); + timezones0.put("America/Martinique", tz); + timezones0.put("America/Montserrat", tz); + timezones0.put("America/Port_of_Spain", tz); + timezones0.put("America/Porto_Velho", tz); + timezones0.put("America/Puerto_Rico", tz); + timezones0.put("America/Santo_Domingo", tz); + timezones0.put("America/St_Kitts", tz); + timezones0.put("America/St_Lucia", tz); + timezones0.put("America/St_Thomas", tz); + timezones0.put("America/St_Vincent", tz); + timezones0.put("America/Tortola", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Asuncion", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Asuncion", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Cuiaba", + Calendar.OCTOBER, 2, Calendar.SUNDAY, 0 * 3600, + Calendar.FEBRUARY, 3, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Cuiaba", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Goose_Bay", + Calendar.APRIL, 1, Calendar.SUNDAY, 60000, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 60000); + timezones0.put("America/Goose_Bay", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Glace_Bay", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Glace_Bay", tz); + timezones0.put("America/Halifax", tz); + timezones0.put("America/Thule", tz); + timezones0.put("Atlantic/Bermuda", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Santiago", + Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600, + Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Santiago", tz); + timezones0.put("Antarctica/Palmer", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "Atlantic/Stanley", + Calendar.SEPTEMBER, 2, Calendar.SUNDAY, 0 * 3600, + Calendar.APRIL, 16, -Calendar.SUNDAY, 0 * 3600); + timezones0.put("Atlantic/Stanley", tz); + tz = new SimpleTimeZone + (-3500 * 3600, "CNT", + Calendar.APRIL, 1, Calendar.SUNDAY, 60000, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 60000); + timezones0.put("CNT", tz); + timezones0.put("America/St_Johns", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Araguaina", + Calendar.OCTOBER, 2, Calendar.SUNDAY, 0 * 3600, + Calendar.FEBRUARY, 3, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Araguaina", tz); + timezones0.put("America/Sao_Paulo", tz); + tz = new SimpleTimeZone(-3000 * 3600, "AGT"); + timezones0.put("AGT", tz); + timezones0.put("America/Belem", tz); + timezones0.put("America/Buenos_Aires", tz); + timezones0.put("America/Catamarca", tz); + timezones0.put("America/Cayenne", tz); + timezones0.put("America/Cordoba", tz); + timezones0.put("America/Fortaleza", tz); + timezones0.put("America/Jujuy", tz); + timezones0.put("America/Maceio", tz); + timezones0.put("America/Mendoza", tz); + timezones0.put("America/Montevideo", tz); + timezones0.put("America/Paramaribo", tz); + timezones0.put("America/Recife", tz); + timezones0.put("America/Rosario", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Godthab", + Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600, + Calendar.OCTOBER, 30, -Calendar.SATURDAY, 22000 * 3600); + timezones0.put("America/Godthab", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Miquelon", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Miquelon", tz); + tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha"); + timezones0.put("America/Noronha", tz); + timezones0.put("Atlantic/South_Georgia", tz); + tz = new SimpleTimeZone + (-1000 * 3600, "America/Scoresbysund", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Scoresbysund", tz); + timezones0.put("Atlantic/Azores", tz); + tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde"); + timezones0.put("Atlantic/Cape_Verde", tz); + timezones0.put("Atlantic/Jan_Mayen", tz); + tz = new SimpleTimeZone(0 * 3600, "GMT"); + timezones0.put("GMT", tz); + timezones0.put("UTC", tz); + timezones0.put("Africa/Abidjan", tz); + timezones0.put("Africa/Accra", tz); + timezones0.put("Africa/Bamako", tz); + timezones0.put("Africa/Banjul", tz); + timezones0.put("Africa/Bissau", tz); + timezones0.put("Africa/Casablanca", tz); + timezones0.put("Africa/Conakry", tz); + timezones0.put("Africa/Dakar", tz); + timezones0.put("Africa/El_Aaiun", tz); + timezones0.put("Africa/Freetown", tz); + timezones0.put("Africa/Lome", tz); + timezones0.put("Africa/Monrovia", tz); + timezones0.put("Africa/Nouakchott", tz); + timezones0.put("Africa/Ouagadougou", tz); + timezones0.put("Africa/Sao_Tome", tz); + timezones0.put("Africa/Timbuktu", tz); + timezones0.put("Atlantic/Reykjavik", tz); + timezones0.put("Atlantic/St_Helena", tz); + timezones0.put("Europe/Belfast", tz); + timezones0.put("Europe/Dublin", tz); + timezones0.put("Europe/London", tz); + tz = new SimpleTimeZone + (0 * 3600, "WET", + Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600); + timezones0.put("WET", tz); + timezones0.put("Atlantic/Canary", tz); + timezones0.put("Atlantic/Faeroe", tz); + timezones0.put("Atlantic/Madeira", tz); + timezones0.put("Europe/Lisbon", tz); + tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers"); + timezones0.put("Africa/Algiers", tz); + timezones0.put("Africa/Bangui", tz); + timezones0.put("Africa/Brazzaville", tz); + timezones0.put("Africa/Douala", tz); + timezones0.put("Africa/Kinshasa", tz); + timezones0.put("Africa/Lagos", tz); + timezones0.put("Africa/Libreville", tz); + timezones0.put("Africa/Luanda", tz); + timezones0.put("Africa/Malabo", tz); + timezones0.put("Africa/Ndjamena", tz); + timezones0.put("Africa/Niamey", tz); + timezones0.put("Africa/Porto-Novo", tz); + timezones0.put("Africa/Tunis", tz); + tz = new SimpleTimeZone + (1000 * 3600, "Africa/Windhoek", + Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Africa/Windhoek", tz); + tz = new SimpleTimeZone + (1000 * 3600, "CET", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("CET", tz); + timezones0.put("CEST", tz); + timezones0.put("ECT", tz); + timezones0.put("MET", tz); + timezones0.put("Africa/Ceuta", tz); + timezones0.put("Arctic/Longyearbyen", tz); + timezones0.put("Europe/Amsterdam", tz); + timezones0.put("Europe/Andorra", tz); + timezones0.put("Europe/Belgrade", tz); + timezones0.put("Europe/Berlin", tz); + timezones0.put("Europe/Bratislava", tz); + timezones0.put("Europe/Brussels", tz); + timezones0.put("Europe/Budapest", tz); + timezones0.put("Europe/Copenhagen", tz); + timezones0.put("Europe/Gibraltar", tz); + timezones0.put("Europe/Ljubljana", tz); + timezones0.put("Europe/Luxembourg", tz); + timezones0.put("Europe/Madrid", tz); + timezones0.put("Europe/Malta", tz); + timezones0.put("Europe/Monaco", tz); + timezones0.put("Europe/Oslo", tz); + timezones0.put("Europe/Paris", tz); + timezones0.put("Europe/Prague", tz); + timezones0.put("Europe/Rome", tz); + timezones0.put("Europe/San_Marino", tz); + timezones0.put("Europe/Sarajevo", tz); + timezones0.put("Europe/Skopje", tz); + timezones0.put("Europe/Stockholm", tz); + timezones0.put("Europe/Tirane", tz); + timezones0.put("Europe/Vaduz", tz); + timezones0.put("Europe/Vatican", tz); + timezones0.put("Europe/Vienna", tz); + timezones0.put("Europe/Warsaw", tz); + timezones0.put("Europe/Zagreb", tz); + timezones0.put("Europe/Zurich", tz); + tz = new SimpleTimeZone + (2000 * 3600, "ART", + Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600, + Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 23000 * 3600); + timezones0.put("ART", tz); + timezones0.put("Africa/Cairo", tz); + tz = new SimpleTimeZone(2000 * 3600, "CAT"); + timezones0.put("CAT", tz); + timezones0.put("Africa/Blantyre", tz); + timezones0.put("Africa/Bujumbura", tz); + timezones0.put("Africa/Gaborone", tz); + timezones0.put("Africa/Harare", tz); + timezones0.put("Africa/Johannesburg", tz); + timezones0.put("Africa/Kigali", tz); + timezones0.put("Africa/Lubumbashi", tz); + timezones0.put("Africa/Lusaka", tz); + timezones0.put("Africa/Maputo", tz); + timezones0.put("Africa/Maseru", tz); + timezones0.put("Africa/Mbabane", tz); + timezones0.put("Africa/Tripoli", tz); + timezones0.put("Europe/Riga", tz); + timezones0.put("Europe/Tallinn", tz); + timezones0.put("Europe/Vilnius", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Amman", + Calendar.MARCH, -1, Calendar.THURSDAY, 0 * 3600, + Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 0 * 3600); + timezones0.put("Asia/Amman", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Beirut", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("Asia/Beirut", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Damascus", + Calendar.APRIL, 1, 0, 0 * 3600, + Calendar.OCTOBER, 1, 0, 0 * 3600); + timezones0.put("Asia/Damascus", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Gaza", + Calendar.APRIL, 3, Calendar.FRIDAY, 0 * 3600, + Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600); + timezones0.put("Asia/Gaza", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Jerusalem", + Calendar.APRIL, 1, 0, 1000 * 3600, + Calendar.OCTOBER, 1, 0, 1000 * 3600); + timezones0.put("Asia/Jerusalem", tz); + tz = new SimpleTimeZone + (2000 * 3600, "EET", + Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("EET", tz); + timezones0.put("Asia/Istanbul", tz); + timezones0.put("Asia/Nicosia", tz); + timezones0.put("Europe/Athens", tz); + timezones0.put("Europe/Bucharest", tz); + timezones0.put("Europe/Chisinau", tz); + timezones0.put("Europe/Helsinki", tz); + timezones0.put("Europe/Istanbul", tz); + timezones0.put("Europe/Kiev", tz); + timezones0.put("Europe/Nicosia", tz); + timezones0.put("Europe/Simferopol", tz); + timezones0.put("Europe/Sofia", tz); + timezones0.put("Europe/Uzhgorod", tz); + timezones0.put("Europe/Zaporozhye", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Europe/Kaliningrad", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Europe/Kaliningrad", tz); + timezones0.put("Europe/Minsk", tz); + tz = new SimpleTimeZone + (3000 * 3600, "Asia/Baghdad", + Calendar.APRIL, 1, 0, 3000 * 3600, + Calendar.OCTOBER, 1, 0, 3000 * 3600); + timezones0.put("Asia/Baghdad", tz); + tz = new SimpleTimeZone + (3000 * 3600, "Europe/Moscow", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Europe/Moscow", tz); + timezones0.put("Europe/Tiraspol", tz); + tz = new SimpleTimeZone(3000 * 3600, "EAT"); + timezones0.put("EAT", tz); + timezones0.put("Africa/Addis_Ababa", tz); + timezones0.put("Africa/Asmera", tz); + timezones0.put("Africa/Dar_es_Salaam", tz); + timezones0.put("Africa/Djibouti", tz); + timezones0.put("Africa/Kampala", tz); + timezones0.put("Africa/Khartoum", tz); + timezones0.put("Africa/Mogadishu", tz); + timezones0.put("Africa/Nairobi", tz); + timezones0.put("Antarctica/Syowa", tz); + timezones0.put("Asia/Aden", tz); + timezones0.put("Asia/Bahrain", tz); + timezones0.put("Asia/Kuwait", tz); + timezones0.put("Asia/Qatar", tz); + timezones0.put("Asia/Riyadh", tz); + timezones0.put("Indian/Antananarivo", tz); + timezones0.put("Indian/Comoro", tz); + timezones0.put("Indian/Mayotte", tz); + tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran"); + timezones0.put("Asia/Tehran", tz); + tz = new SimpleTimeZone + (4000 * 3600, "Asia/Baku", + Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600); + timezones0.put("Asia/Baku", tz); + tz = new SimpleTimeZone + (4000 * 3600, "Asia/Aqtau", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("Asia/Aqtau", tz); + timezones0.put("Asia/Tbilisi", tz); + tz = new SimpleTimeZone + (4000 * 3600, "Asia/Yerevan", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Yerevan", tz); + timezones0.put("Europe/Samara", tz); + tz = new SimpleTimeZone(4000 * 3600, "NET"); + timezones0.put("NET", tz); + timezones0.put("Asia/Dubai", tz); + timezones0.put("Asia/Muscat", tz); + timezones0.put("Indian/Mahe", tz); + timezones0.put("Indian/Mauritius", tz); + timezones0.put("Indian/Reunion", tz); + tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul"); + timezones0.put("Asia/Kabul", tz); + tz = new SimpleTimeZone + (5000 * 3600, "Asia/Aqtobe", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("Asia/Aqtobe", tz); + tz = new SimpleTimeZone + (5000 * 3600, "Asia/Bishkek", + Calendar.MARCH, -1, Calendar.SUNDAY, 2500 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2500 * 3600); + timezones0.put("Asia/Bishkek", tz); + tz = new SimpleTimeZone + (5000 * 3600, "Asia/Yekaterinburg", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Yekaterinburg", tz); + tz = new SimpleTimeZone(5000 * 3600, "PLT"); + timezones0.put("PLT", tz); + timezones0.put("Asia/Ashgabat", tz); + timezones0.put("Asia/Dushanbe", tz); + timezones0.put("Asia/Karachi", tz); + timezones0.put("Asia/Samarkand", tz); + timezones0.put("Asia/Tashkent", tz); + timezones0.put("Indian/Chagos", tz); + timezones0.put("Indian/Kerguelen", tz); + timezones0.put("Indian/Maldives", tz); + tz = new SimpleTimeZone(5500 * 3600, "IST"); + timezones0.put("IST", tz); + timezones0.put("Asia/Calcutta", tz); + tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu"); + timezones0.put("Asia/Katmandu", tz); + tz = new SimpleTimeZone(6000 * 3600, "BST"); + timezones0.put("BST", tz); + timezones0.put("Antarctica/Mawson", tz); + timezones0.put("Asia/Colombo", tz); + timezones0.put("Asia/Dhaka", tz); + timezones0.put("Asia/Thimphu", tz); + tz = new SimpleTimeZone + (6000 * 3600, "Asia/Almaty", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("Asia/Almaty", tz); + tz = new SimpleTimeZone + (6000 * 3600, "Asia/Novosibirsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Novosibirsk", tz); + timezones0.put("Asia/Omsk", tz); + tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon"); + timezones0.put("Asia/Rangoon", tz); + timezones0.put("Indian/Cocos", tz); + tz = new SimpleTimeZone(7000 * 3600, "VST"); + timezones0.put("VST", tz); + timezones0.put("Antarctica/Davis", tz); + timezones0.put("Asia/Bangkok", tz); + timezones0.put("Asia/Hovd", tz); + timezones0.put("Asia/Jakarta", tz); + timezones0.put("Asia/Phnom_Penh", tz); + timezones0.put("Asia/Saigon", tz); + timezones0.put("Asia/Vientiane", tz); + timezones0.put("Indian/Christmas", tz); + tz = new SimpleTimeZone + (7000 * 3600, "Asia/Krasnoyarsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Krasnoyarsk", tz); + tz = new SimpleTimeZone(8000 * 3600, "CTT"); + timezones0.put("CTT", tz); + timezones0.put("Antarctica/Casey", tz); + timezones0.put("Asia/Brunei", tz); + timezones0.put("Asia/Chungking", tz); + timezones0.put("Asia/Harbin", tz); + timezones0.put("Asia/Hong_Kong", tz); + timezones0.put("Asia/Kashgar", tz); + timezones0.put("Asia/Kuala_Lumpur", tz); + timezones0.put("Asia/Kuching", tz); + timezones0.put("Asia/Macao", tz); + timezones0.put("Asia/Manila", tz); + timezones0.put("Asia/Shanghai", tz); + timezones0.put("Asia/Singapore", tz); + timezones0.put("Asia/Taipei", tz); + timezones0.put("Asia/Ujung_Pandang", tz); + timezones0.put("Asia/Ulaanbaatar", tz); + timezones0.put("Asia/Urumqi", tz); + timezones0.put("Australia/Perth", tz); + tz = new SimpleTimeZone + (8000 * 3600, "Asia/Irkutsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Irkutsk", tz); + tz = new SimpleTimeZone(9000 * 3600, "JST"); + timezones0.put("JST", tz); + timezones0.put("Asia/Dili", tz); + timezones0.put("Asia/Jayapura", tz); + timezones0.put("Asia/Pyongyang", tz); + timezones0.put("Asia/Seoul", tz); + timezones0.put("Asia/Tokyo", tz); + timezones0.put("Pacific/Palau", tz); + tz = new SimpleTimeZone + (9000 * 3600, "Asia/Yakutsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Yakutsk", tz); + tz = new SimpleTimeZone + (9500 * 3600, "Australia/Adelaide", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Australia/Adelaide", tz); + timezones0.put("Australia/Broken_Hill", tz); + tz = new SimpleTimeZone(9500 * 3600, "ACT"); + timezones0.put("ACT", tz); + timezones0.put("Australia/Darwin", tz); + tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville"); + timezones0.put("Antarctica/DumontDUrville", tz); + timezones0.put("Australia/Brisbane", tz); + timezones0.put("Australia/Lindeman", tz); + timezones0.put("Pacific/Guam", tz); + timezones0.put("Pacific/Port_Moresby", tz); + timezones0.put("Pacific/Saipan", tz); + timezones0.put("Pacific/Truk", tz); + timezones0.put("Pacific/Yap", tz); + tz = new SimpleTimeZone + (10000 * 3600, "Asia/Vladivostok", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Vladivostok", tz); + tz = new SimpleTimeZone + (10000 * 3600, "Australia/Hobart", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Australia/Hobart", tz); + tz = new SimpleTimeZone + (10000 * 3600, "AET", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("AET", tz); + timezones0.put("Australia/Melbourne", tz); + timezones0.put("Australia/Sydney", tz); + tz = new SimpleTimeZone + (10500 * 3600, "Australia/Lord_Howe", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600); + timezones0.put("Australia/Lord_Howe", tz); + tz = new SimpleTimeZone + (11000 * 3600, "Asia/Magadan", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Magadan", tz); + tz = new SimpleTimeZone(11000 * 3600, "SST"); + timezones0.put("SST", tz); + timezones0.put("Pacific/Efate", tz); + timezones0.put("Pacific/Guadalcanal", tz); + timezones0.put("Pacific/Kosrae", tz); + timezones0.put("Pacific/Noumea", tz); + timezones0.put("Pacific/Ponape", tz); + tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk"); + timezones0.put("Pacific/Norfolk", tz); + tz = new SimpleTimeZone + (12000 * 3600, "NST", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, 3, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("NST", tz); + timezones0.put("Antarctica/McMurdo", tz); + timezones0.put("Antarctica/South_Pole", tz); + timezones0.put("Pacific/Auckland", tz); + tz = new SimpleTimeZone + (12000 * 3600, "Asia/Anadyr", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Asia/Anadyr", tz); + timezones0.put("Asia/Kamchatka", tz); + tz = new SimpleTimeZone(12000 * 3600, "Pacific/Fiji"); + timezones0.put("Pacific/Fiji", tz); + timezones0.put("Pacific/Funafuti", tz); + timezones0.put("Pacific/Kwajalein", tz); + timezones0.put("Pacific/Majuro", tz); + timezones0.put("Pacific/Nauru", tz); + timezones0.put("Pacific/Tarawa", tz); + timezones0.put("Pacific/Wake", tz); + timezones0.put("Pacific/Wallis", tz); + tz = new SimpleTimeZone + (12750 * 3600, "Pacific/Chatham", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600, + Calendar.MARCH, 3, Calendar.SUNDAY, 2750 * 3600); + timezones0.put("Pacific/Chatham", tz); + tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury"); + timezones0.put("Pacific/Enderbury", tz); + timezones0.put("Pacific/Tongatapu", tz); + tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati"); + timezones0.put("Pacific/Kiritimati", tz); + } + return timezones0; + } + + /** + * Maps a time zone name (with optional GMT offset and daylight time + * zone name) to one of the known time zones. This method called + * with the result of <code>System.getProperty("user.timezone")</code> + * or <code>getDefaultTimeZoneId()</code>. Note that giving one of + * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is + * preferred. + * The time zone name can be given as follows: + * <code>(standard zone name)[(GMT offset)[(DST zone name)[DST offset]]] + * </code> + * <p> + * If only a (standard zone name) is given (no numbers in the + * String) then it gets mapped directly to the TimeZone with that + * name, if that fails null is returned. + * <p> + * Alternately, a POSIX-style TZ string can be given, defining the time zone: + * <code>std offset dst offset,date/time,date/time</code> + * See the glibc manual, or the man page for <code>tzset</code> for details + * of this format. + * <p> + * A GMT offset is the offset to add to the local time to get GMT. + * If a (GMT offset) is included (either in seconds or hours) then + * an attempt is made to find a TimeZone name matching both the name + * and the offset (that doesn't observe daylight time, if the + * timezone observes daylight time then you must include a daylight + * time zone name after the offset), if that fails then a TimeZone + * with the given GMT offset is returned (whether or not the + * TimeZone observes daylight time is ignored), if that also fails + * the GMT TimeZone is returned. + * <p> + * If the String ends with (GMT offset)(daylight time zone name) + * then an attempt is made to find a TimeZone with the given name and + * GMT offset that also observes (the daylight time zone name is not + * currently used in any other way), if that fails a TimeZone with + * the given GMT offset that observes daylight time is returned, if + * that also fails the GMT TimeZone is returned. + * <p> + * Examples: In Chicago, the time zone id could be "CST6CDT", but + * the preferred name would be "America/Chicago". In Indianapolis + * (which does not have Daylight Savings Time) the string could be + * "EST5", but the preferred name would be "America/Indianapolis". + * The standard time zone name for The Netherlands is "Europe/Amsterdam", + * but can also be given as "CET-1CEST". + */ + static TimeZone getDefaultTimeZone(String sysTimeZoneId) + { + String stdName = null; + String dstName; + int stdOffs; + int dstOffs; + try + { + int idLength = sysTimeZoneId.length(); + + int index = 0; + int prevIndex; + char c; + + // get std + do + c = sysTimeZoneId.charAt(index++); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && index < idLength); + + if (index >= idLength) + return (TimeZone)timezones().get(sysTimeZoneId); + + stdName = sysTimeZoneId.substring(0, --index); + prevIndex = index; + + // get the std offset + do + c = sysTimeZoneId.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + { // convert the dst string to a millis number + String offset = sysTimeZoneId.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + stdOffs = parseTime(offset.substring(1)); + else + stdOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + stdOffs = -stdOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + stdOffs = -stdOffs; + } + + // Done yet? (Format: std offset) + if (index >= idLength) + { + // Do we have an existing timezone with that name and offset? + TimeZone tz = (TimeZone) timezones().get(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs) + return tz; + + // Custom then. + return new SimpleTimeZone(stdOffs, stdName); + } + + // get dst + do + c = sysTimeZoneId.charAt(index++); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && index < idLength); + + // Done yet? (Format: std offset dst) + if (index >= idLength) + { + // Do we have an existing timezone with that name and offset + // which has DST? + TimeZone tz = (TimeZone) timezones().get(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()) + return tz; + + // Custom then. + return new SimpleTimeZone(stdOffs, stdName); + } + + // get the dst offset + dstName = sysTimeZoneId.substring(prevIndex, --index); + prevIndex = index; + do + c = sysTimeZoneId.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + { // convert the dst string to a millis number + String offset = sysTimeZoneId.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + dstOffs = parseTime(offset.substring(1)); + else + dstOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + dstOffs = -dstOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + dstOffs = -dstOffs; + } + + // Done yet? (Format: std offset dst offset) + // FIXME: We don't support DST without a rule given. Should we? + if (index >= idLength) + { + // Time Zone existing with same name, dst and offsets? + TimeZone tz = (TimeZone) timezones().get(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs && tz.useDaylightTime() + && tz.getDSTSavings() == (dstOffs - stdOffs)) + return tz; + + return new SimpleTimeZone(stdOffs, stdName); + } + + // get the DST rule + if (sysTimeZoneId.charAt(index) == ',' + || sysTimeZoneId.charAt(index) == ';') + { + index++; + int offs = index; + while (sysTimeZoneId.charAt(index) != ',' + && sysTimeZoneId.charAt(index) != ';') + index++; + String startTime = sysTimeZoneId.substring(offs, index); + index++; + String endTime = sysTimeZoneId.substring(index); + + index = startTime.indexOf('/'); + int startMillis; + int endMillis; + String startDate; + String endDate; + if (index != -1) + { + startDate = startTime.substring(0, index); + startMillis = parseTime(startTime.substring(index + 1)); + } + else + { + startDate = startTime; + // if time isn't given, default to 2:00:00 AM. + startMillis = 2 * 60 * 60 * 1000; + } + index = endTime.indexOf('/'); + if (index != -1) + { + endDate = endTime.substring(0, index); + endMillis = parseTime(endTime.substring(index + 1)); + } + else + { + endDate = endTime; + // if time isn't given, default to 2:00:00 AM. + endMillis = 2 * 60 * 60 * 1000; + } + + int[] start = getDateParams(startDate); + int[] end = getDateParams(endDate); + return new SimpleTimeZone(stdOffs, stdName, start[0], start[1], + start[2], startMillis, end[0], end[1], + end[2], endMillis, (dstOffs - stdOffs)); + } + } + + // FIXME: Produce a warning here? + catch (IndexOutOfBoundsException _) + { + } + catch (NumberFormatException _) + { + } + + return null; + } + + /** + * Parses and returns the params for a POSIX TZ date field, + * in the format int[]{ month, day, dayOfWeek }, following the + * SimpleTimeZone constructor rules. + */ + private static int[] getDateParams(String date) + { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int month; + + if (date.charAt(0) == 'M' || date.charAt(0) == 'm') + { + int day; + + // Month, week of month, day of week + month = Integer.parseInt(date.substring(1, date.indexOf('.'))); + int week = Integer.parseInt(date.substring(date.indexOf('.') + 1, + date.lastIndexOf('.'))); + int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.') + + 1)); + if (week == 5) + day = -1; // last day of month is -1 in java, 5 in TZ + else + // first day of week starting on or after. + day = (week - 1) * 7 + 1; + + dayOfWeek++; // Java day of week is one-based, Sunday is first day. + month--; // Java month is zero-based. + return new int[] { month, day, dayOfWeek }; + } + + // julian day, either zero-based 0<=n<=365 (incl feb 29) + // or one-based 1<=n<=365 (no feb 29) + int julianDay; // Julian day, + + if (date.charAt(0) != 'J' || date.charAt(0) != 'j') + { + julianDay = Integer.parseInt(date.substring(1)); + julianDay++; // make 1-based + // Adjust day count to include feb 29. + dayCount = new int[] + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 + }; + } + else + // 1-based julian day + julianDay = Integer.parseInt(date); + + int i = 11; + while (i > 0) + if (dayCount[i] < julianDay) + break; + else + i--; + julianDay -= dayCount[i]; + month = i; + return new int[] { month, julianDay, 0 }; + } + + /** + * Parses a time field hh[:mm[:ss]], returning the result + * in milliseconds. No leading sign. + */ + private static int parseTime(String time) + { + int millis = 0; + int i = 0; + + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i)); + if (i >= time.length()) + return millis; + + int iprev = ++i; + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + if (i >= time.length()) + return millis; + + millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i)); + millis += 1000 * Integer.parseInt(time.substring(++i)); + return millis; + } + + /** + * Gets the time zone offset, for current date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week + * @param milliseconds the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. + */ + public abstract int getOffset(int era, int year, int month, + int day, int dayOfWeek, int milliseconds); + + /** + * Get the time zone offset for the specified date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * @param date the date represented in millisecends + * since January 1, 1970 00:00:00 GMT. + * @since 1.4 + */ + public int getOffset(long date) + { + return (inDaylightTime(new Date(date)) + ? getRawOffset() + getDSTSavings() + : getRawOffset()); + } + + /** + * Gets the time zone offset, ignoring daylight savings. This is + * the offset to add to UTC to get the local time. + * @return the time zone offset in milliseconds. + */ + public abstract int getRawOffset(); + + /** + * Sets the time zone offset, ignoring daylight savings. This is + * the offset to add to UTC to get the local time. + * @param offsetMillis the time zone offset to GMT. + */ + public abstract void setRawOffset(int offsetMillis); + + /** + * Gets the identifier of this time zone. For instance, PST for + * Pacific Standard Time. + * @returns the ID of this time zone. + */ + public String getID() + { + return ID; + } + + /** + * Sets the identifier of this time zone. For instance, PST for + * Pacific Standard Time. + * @param id the new time zone ID. + * @throws NullPointerException if <code>id</code> is <code>null</code> + */ + public void setID(String id) + { + if (id == null) + throw new NullPointerException(); + + this.ID = id; + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be the long + * description of the timezone in the current locale. The name + * displayed will assume daylight savings time is not in effect. + * + * @return The name of the time zone. + */ + public final String getDisplayName() + { + return (getDisplayName(false, LONG, Locale.getDefault())); + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be the long + * description of the timezone in the specified locale. The name + * displayed will assume daylight savings time is not in effect. + * + * @param locale The locale for this timezone name. + * + * @return The name of the time zone. + */ + public final String getDisplayName(Locale locale) + { + return (getDisplayName(false, LONG, locale)); + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be of the + * specified type in the current locale. + * + * @param dst Whether or not daylight savings time is in effect. + * @param style <code>LONG</code> for a long name, <code>SHORT</code> for + * a short abbreviation. + * + * @return The name of the time zone. + */ + public final String getDisplayName(boolean dst, int style) + { + return (getDisplayName(dst, style, Locale.getDefault())); + } + + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be of the + * specified type in the specified locale. + * + * @param dst Whether or not daylight savings time is in effect. + * @param style <code>LONG</code> for a long name, <code>SHORT</code> for + * a short abbreviation. + * @param locale The locale for this timezone name. + * + * @return The name of the time zone. + */ + public String getDisplayName(boolean dst, int style, Locale locale) + { + DateFormatSymbols dfs; + try + { + dfs = new DateFormatSymbols(locale); + + // The format of the value returned is defined by us. + String[][]zoneinfo = dfs.getZoneStrings(); + for (int i = 0; i < zoneinfo.length; i++) + { + if (zoneinfo[i][0].equals(getID())) + { + if (!dst) + { + if (style == SHORT) + return (zoneinfo[i][2]); + else + return (zoneinfo[i][1]); + } + else + { + if (style == SHORT) + return (zoneinfo[i][4]); + else + return (zoneinfo[i][3]); + } + } + } + } + catch (MissingResourceException e) + { + } + + return getDefaultDisplayName(dst); + } + + private String getDefaultDisplayName(boolean dst) + { + int offset = getRawOffset(); + if (dst && this instanceof SimpleTimeZone) + { + // ugly, but this is a design failure of the API: + // getDisplayName takes a dst parameter even though + // TimeZone knows nothing about daylight saving offsets. + offset += ((SimpleTimeZone) this).getDSTSavings(); + } + + StringBuffer sb = new StringBuffer(9); + sb.append("GMT"); + + offset = offset / (1000 * 60); + int hours = Math.abs(offset) / 60; + int minutes = Math.abs(offset) % 60; + + if (minutes != 0 || hours != 0) + { + sb.append(offset >= 0 ? '+' : '-'); + sb.append((char) ('0' + hours / 10)); + sb.append((char) ('0' + hours % 10)); + sb.append(':'); + sb.append((char) ('0' + minutes / 10)); + sb.append((char) ('0' + minutes % 10)); + } + + return sb.toString(); + } + + /** + * Returns true, if this time zone uses Daylight Savings Time. + */ + public abstract boolean useDaylightTime(); + + /** + * Returns true, if the given date is in Daylight Savings Time in this + * time zone. + * @param date the given Date. + */ + public abstract boolean inDaylightTime(Date date); + + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * <p>The default implementation returns 3600000 milliseconds + * (one hour) if the time zone uses daylight savings time + * (as specified by {@link #useDaylightTime()}), otherwise + * it returns 0. + * @return the daylight savings offset in milliseconds. + * @since 1.4 + */ + public int getDSTSavings () + { + return useDaylightTime () ? 3600000 : 0; + } + + /** + * Gets the TimeZone for the given ID. + * @param ID the time zone identifier. + * @return The time zone for the identifier or GMT, if no such time + * zone exists. + */ + // FIXME: XXX: JCL indicates this and other methods are synchronized. + public static TimeZone getTimeZone(String ID) + { + // First check timezones hash + TimeZone tz = (TimeZone) timezones().get(ID); + if (tz != null) + { + if (tz.getID().equals(ID)) + return tz; + + // We always return a timezone with the requested ID. + // This is the same behaviour as with JDK1.2. + tz = (TimeZone) tz.clone(); + tz.setID(ID); + // We also save the alias, so that we return the same + // object again if getTimeZone is called with the same + // alias. + timezones().put(ID, tz); + return tz; + } + + // See if the ID is really a GMT offset form. + // Note that GMT is in the table so we know it is different. + if (ID.startsWith("GMT")) + { + int pos = 3; + int offset_direction = 1; + + if (ID.charAt(pos) == '-') + { + offset_direction = -1; + pos++; + } + else if (ID.charAt(pos) == '+') + { + pos++; + } + + try + { + int hour, minute; + + String offset_str = ID.substring(pos); + int idx = offset_str.indexOf(":"); + if (idx != -1) + { + hour = Integer.parseInt(offset_str.substring(0, idx)); + minute = Integer.parseInt(offset_str.substring(idx + 1)); + } + else + { + int offset_length = offset_str.length(); + if (offset_length <= 2) + { + // Only hour + hour = Integer.parseInt(offset_str); + minute = 0; + } + else + { + // hour and minute, not separated by colon + hour = Integer.parseInt + (offset_str.substring(0, offset_length - 2)); + minute = Integer.parseInt + (offset_str.substring(offset_length - 2)); + } + } + + return new SimpleTimeZone((hour * (60 * 60 * 1000) + + minute * (60 * 1000)) + * offset_direction, ID); + } + catch (NumberFormatException e) + { + } + } + + // Finally, return GMT per spec + return getTimeZone("GMT"); + } + + /** + * Gets the available IDs according to the given time zone + * offset. + * @param rawOffset the given time zone GMT offset. + * @return An array of IDs, where the time zone has the specified GMT + * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have + * GMT-07:00, but differ in daylight savings behaviour. + */ + public static String[] getAvailableIDs(int rawOffset) + { + int count = 0; + Iterator iter = timezones().entrySet().iterator(); + while (iter.hasNext()) + { + // Don't iterate the values, since we want to count + // doubled values (aliases) + Map.Entry entry = (Map.Entry) iter.next(); + if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset) + count++; + } + + String[] ids = new String[count]; + count = 0; + iter = timezones().entrySet().iterator(); + while (iter.hasNext()) + { + Map.Entry entry = (Map.Entry) iter.next(); + if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset) + ids[count++] = (String) entry.getKey(); + } + return ids; + } + + /** + * Gets all available IDs. + * @return An array of all supported IDs. + */ + public static String[] getAvailableIDs() + { + return (String[]) + timezones().keySet().toArray(new String[timezones().size()]); + } + + /** + * Returns the time zone under which the host is running. This + * can be changed with setDefault. + * + * @return A clone of the current default time zone for this host. + * @see #setDefault + */ + public static TimeZone getDefault() + { + return (TimeZone) defaultZone().clone(); + } + + public static void setDefault(TimeZone zone) + { + // Hmmmm. No Security checks? + defaultZone0 = zone; + } + + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the raw offsets are identical. Subclasses + * should override this method if they use daylight savings. + * @return true if this zone has the same raw offset + */ + public boolean hasSameRules(TimeZone other) + { + return other.getRawOffset() == getRawOffset(); + } + + /** + * Returns a clone of this object. I can't imagine, why this is + * useful for a time zone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + return null; + } + } +} diff --git a/libjava/classpath/java/util/Timer.java b/libjava/classpath/java/util/Timer.java new file mode 100644 index 00000000000..715f06cf641 --- /dev/null +++ b/libjava/classpath/java/util/Timer.java @@ -0,0 +1,615 @@ +/* Timer.java -- Timer that runs TimerTasks at a later time. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + * Timer that can run TimerTasks at a later time. + * TimerTasks can be scheduled for one time execution at some time in the + * future. They can be scheduled to be rescheduled at a time period after the + * task was last executed. Or they can be scheduled to be executed repeatedly + * at a fixed rate. + * <p> + * The normal scheduling will result in a more or less even delay in time + * between successive executions, but the executions could drift in time if + * the task (or other tasks) takes a long time to execute. Fixed delay + * scheduling guarantees more or less that the task will be executed at a + * specific time, but if there is ever a delay in execution then the period + * between successive executions will be shorter. The first method of + * repeated scheduling is preferred for repeated tasks in response to user + * interaction, the second method of repeated scheduling is preferred for tasks + * that act like alarms. + * <p> + * The Timer keeps a binary heap as a task priority queue which means that + * scheduling and serving of a task in a queue of n tasks costs O(log n). + * + * @see TimerTask + * @since 1.3 + * @author Mark Wielaard (mark@klomp.org) + */ +public class Timer +{ + /** + * Priority Task Queue. + * TimerTasks are kept in a binary heap. + * The scheduler calls sleep() on the queue when it has nothing to do or + * has to wait. A sleeping scheduler can be notified by calling interrupt() + * which is automatically called by the enqueue(), cancel() and + * timerFinalized() methods. + */ + private static final class TaskQueue + { + /** Default size of this queue */ + private static final int DEFAULT_SIZE = 32; + + /** Whether to return null when there is nothing in the queue */ + private boolean nullOnEmpty; + + /** + * The heap containing all the scheduled TimerTasks + * sorted by the TimerTask.scheduled field. + * Null when the stop() method has been called. + */ + private TimerTask heap[]; + + /** + * The actual number of elements in the heap + * Can be less then heap.length. + * Note that heap[0] is used as a sentinel. + */ + private int elements; + + /** + * Creates a TaskQueue of default size without any elements in it. + */ + public TaskQueue() + { + heap = new TimerTask[DEFAULT_SIZE]; + elements = 0; + nullOnEmpty = false; + } + + /** + * Adds a TimerTask at the end of the heap. + * Grows the heap if necessary by doubling the heap in size. + */ + private void add(TimerTask task) + { + elements++; + if (elements == heap.length) + { + TimerTask new_heap[] = new TimerTask[heap.length * 2]; + System.arraycopy(heap, 0, new_heap, 0, heap.length); + heap = new_heap; + } + heap[elements] = task; + } + + /** + * Removes the last element from the heap. + * Shrinks the heap in half if + * elements+DEFAULT_SIZE/2 <= heap.length/4. + */ + private void remove() + { + // clear the entry first + heap[elements] = null; + elements--; + if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) + { + TimerTask new_heap[] = new TimerTask[heap.length / 2]; + System.arraycopy(heap, 0, new_heap, 0, elements + 1); + heap = new_heap; + } + } + + /** + * Adds a task to the queue and puts it at the correct place + * in the heap. + */ + public synchronized void enqueue(TimerTask task) + { + // Check if it is legal to add another element + if (heap == null) + { + throw new IllegalStateException + ("cannot enqueue when stop() has been called on queue"); + } + + heap[0] = task; // sentinel + add(task); // put the new task at the end + // Now push the task up in the heap until it has reached its place + int child = elements; + int parent = child / 2; + while (heap[parent].scheduled > task.scheduled) + { + heap[child] = heap[parent]; + child = parent; + parent = child / 2; + } + // This is the correct place for the new task + heap[child] = task; + heap[0] = null; // clear sentinel + // Maybe sched() is waiting for a new element + this.notify(); + } + + /** + * Returns the top element of the queue. + * Can return null when no task is in the queue. + */ + private TimerTask top() + { + if (elements == 0) + { + return null; + } + else + { + return heap[1]; + } + } + + /** + * Returns the top task in the Queue. + * Removes the element from the heap and reorders the heap first. + * Can return null when there is nothing in the queue. + */ + public synchronized TimerTask serve() + { + // The task to return + TimerTask task = null; + + while (task == null) + { + // Get the next task + task = top(); + + // return null when asked to stop + // or if asked to return null when the queue is empty + if ((heap == null) || (task == null && nullOnEmpty)) + { + return null; + } + + // Do we have a task? + if (task != null) + { + // The time to wait until the task should be served + long time = task.scheduled - System.currentTimeMillis(); + if (time > 0) + { + // This task should not yet be served + // So wait until this task is ready + // or something else happens to the queue + task = null; // set to null to make sure we call top() + try + { + this.wait(time); + } + catch (InterruptedException _) + { + } + } + } + else + { + // wait until a task is added + // or something else happens to the queue + try + { + this.wait(); + } + catch (InterruptedException _) + { + } + } + } + + // reconstruct the heap + TimerTask lastTask = heap[elements]; + remove(); + + // drop lastTask at the beginning and move it down the heap + int parent = 1; + int child = 2; + heap[1] = lastTask; + while (child <= elements) + { + if (child < elements) + { + if (heap[child].scheduled > heap[child + 1].scheduled) + { + child++; + } + } + + if (lastTask.scheduled <= heap[child].scheduled) + break; // found the correct place (the parent) - done + + heap[parent] = heap[child]; + parent = child; + child = parent * 2; + } + + // this is the correct new place for the lastTask + heap[parent] = lastTask; + + // return the task + return task; + } + + /** + * When nullOnEmpty is true the serve() method will return null when + * there are no tasks in the queue, otherwise it will wait until + * a new element is added to the queue. It is used to indicate to + * the scheduler that no new tasks will ever be added to the queue. + */ + public synchronized void setNullOnEmpty(boolean nullOnEmpty) + { + this.nullOnEmpty = nullOnEmpty; + this.notify(); + } + + /** + * When this method is called the current and all future calls to + * serve() will return null. It is used to indicate to the Scheduler + * that it should stop executing since no more tasks will come. + */ + public synchronized void stop() + { + this.heap = null; + this.elements = 0; + this.notify(); + } + + } // TaskQueue + + /** + * The scheduler that executes all the tasks on a particular TaskQueue, + * reschedules any repeating tasks and that waits when no task has to be + * executed immediatly. Stops running when canceled or when the parent + * Timer has been finalized and no more tasks have to be executed. + */ + private static final class Scheduler implements Runnable + { + // The priority queue containing all the TimerTasks. + private TaskQueue queue; + + /** + * Creates a new Scheduler that will schedule the tasks on the + * given TaskQueue. + */ + public Scheduler(TaskQueue queue) + { + this.queue = queue; + } + + public void run() + { + TimerTask task; + while ((task = queue.serve()) != null) + { + // If this task has not been canceled + if (task.scheduled >= 0) + { + + // Mark execution time + task.lastExecutionTime = task.scheduled; + + // Repeatable task? + if (task.period < 0) + { + // Last time this task is executed + task.scheduled = -1; + } + + // Run the task + try + { + task.run(); + } + catch (ThreadDeath death) + { + // If an exception escapes, the Timer becomes invalid. + queue.stop(); + throw death; + } + catch (Throwable t) + { + // If an exception escapes, the Timer becomes invalid. + queue.stop(); + } + } + + // Calculate next time and possibly re-enqueue. + if (task.scheduled >= 0) + { + if (task.fixed) + { + task.scheduled += task.period; + } + else + { + task.scheduled = task.period + System.currentTimeMillis(); + } + + try + { + queue.enqueue(task); + } + catch (IllegalStateException ise) + { + // Ignore. Apparently the Timer queue has been stopped. + } + } + } + } + } // Scheduler + + // Number of Timers created. + // Used for creating nice Thread names. + private static int nr; + + // The queue that all the tasks are put in. + // Given to the scheduler + private TaskQueue queue; + + // The Scheduler that does all the real work + private Scheduler scheduler; + + // Used to run the scheduler. + // Also used to checked if the Thread is still running by calling + // thread.isAlive(). Sometimes a Thread is suddenly killed by the system + // (if it belonged to an Applet). + private Thread thread; + + // When cancelled we don't accept any more TimerTasks. + private boolean canceled; + + /** + * Creates a new Timer with a non daemon Thread as Scheduler, with normal + * priority and a default name. + */ + public Timer() + { + this(false); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with normal priority and a default name. + */ + public Timer(boolean daemon) + { + this(daemon, Thread.NORM_PRIORITY); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with the priority given and a default name. + */ + private Timer(boolean daemon, int priority) + { + this(daemon, priority, "Timer-" + (++nr)); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with the priority and name given.E + */ + private Timer(boolean daemon, int priority, String name) + { + canceled = false; + queue = new TaskQueue(); + scheduler = new Scheduler(queue); + thread = new Thread(scheduler, name); + thread.setDaemon(daemon); + thread.setPriority(priority); + thread.start(); + } + + /** + * Cancels the execution of the scheduler. If a task is executing it will + * normally finish execution, but no other tasks will be executed and no + * more tasks can be scheduled. + */ + public void cancel() + { + canceled = true; + queue.stop(); + } + + /** + * Schedules the task at Time time, repeating every period + * milliseconds if period is positive and at a fixed rate if fixed is true. + * + * @exception IllegalArgumentException if time is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + private void schedule(TimerTask task, long time, long period, boolean fixed) + { + if (time < 0) + throw new IllegalArgumentException("negative time"); + + if (task.scheduled == 0 && task.lastExecutionTime == -1) + { + task.scheduled = time; + task.period = period; + task.fixed = fixed; + } + else + { + throw new IllegalStateException + ("task was already scheduled or canceled"); + } + + if (!this.canceled && this.thread != null) + { + queue.enqueue(task); + } + else + { + throw new IllegalStateException + ("timer was canceled or scheduler thread has died"); + } + } + + private static void positiveDelay(long delay) + { + if (delay < 0) + { + throw new IllegalArgumentException("delay is negative"); + } + } + + private static void positivePeriod(long period) + { + if (period < 0) + { + throw new IllegalArgumentException("period is negative"); + } + } + + /** + * Schedules the task at the specified data for one time execution. + * + * @exception IllegalArgumentException if date.getTime() is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date) + { + long time = date.getTime(); + schedule(task, time, -1, false); + } + + /** + * Schedules the task at the specified date and reschedules the task every + * period milliseconds after the last execution of the task finishes until + * this timer or the task is canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, false); + } + + /** + * Schedules the task after the specified delay milliseconds for one time + * execution. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay) + { + positiveDelay(delay); + long time = System.currentTimeMillis() + delay; + schedule(task, time, -1, false); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the + * task every period milliseconds after the last execution of the task + * finishes until this timer or the task is canceled. + * + * @exception IllegalArgumentException if delay or period is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, false); + } + + /** + * Schedules the task at the specified date and reschedules the task at a + * fixed rate every period milliseconds until this timer or the task is + * canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, true); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the task + * at a fixed rate every period milliseconds until this timer or the task + * is canceled. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, true); + } + + /** + * Tells the scheduler that the Timer task died + * so there will be no more new tasks scheduled. + */ + protected void finalize() throws Throwable + { + queue.setNullOnEmpty(true); + } +} diff --git a/libjava/classpath/java/util/TimerTask.java b/libjava/classpath/java/util/TimerTask.java new file mode 100644 index 00000000000..b03118ad0fe --- /dev/null +++ b/libjava/classpath/java/util/TimerTask.java @@ -0,0 +1,145 @@ +/* TimerTask.java -- Task that can be run at a later time if given to a Timer. + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + * Task that can be run at a later time if given to a Timer. + * The TimerTask must implement a run method that will be called by the + * Timer when the task is scheduled for execution. The task can check when + * it should have been scheduled and cancel itself when no longer needed. + * <p> + * Example: + * <pre> + * Timer timer = new Timer(); + * TimerTask task = new TimerTask() { + * public void run() { + * if (this.scheduledExecutionTime() < System.currentTimeMillis() + 500) + * // Do something + * else + * // Complain: We are more then half a second late! + * if (someStopCondition) + * this.cancel(); // This was our last execution + * }; + * timer.scheduleAtFixedRate(task, 1000, 1000); // schedule every second + * </pre> + * <p> + * Note that a TimerTask object is a one shot object and can only given once + * to a Timer. (The Timer will use the TimerTask object for bookkeeping, + * in this implementation). + * <p> + * This class also implements <code>Runnable</code> to make it possible to + * give a TimerTask directly as a target to a <code>Thread</code>. + * + * @see Timer + * @since 1.3 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class TimerTask implements Runnable +{ + /** + * If positive the next time this task should be run. + * If negative this TimerTask is canceled or executed for the last time. + */ + long scheduled; + + /** + * If positive the last time this task was run. + * If negative this TimerTask has not yet been scheduled. + */ + long lastExecutionTime; + + /** + * If positive the number of milliseconds between runs of this task. + * If -1 this task doesn't have to be run more then once. + */ + long period; + + /** + * If true the next time this task should be run is relative to + * the last scheduled time, otherwise it can drift in time. + */ + boolean fixed; + + /** + * Creates a TimerTask and marks it as not yet scheduled. + */ + protected TimerTask() + { + this.scheduled = 0; + this.lastExecutionTime = -1; + } + + /** + * Marks the task as canceled and prevents any further execution. + * Returns true if the task was scheduled for any execution in the future + * and this cancel operation prevents that execution from happening. + * <p> + * A task that has been canceled can never be scheduled again. + * <p> + * In this implementation the TimerTask it is possible that the Timer does + * keep a reference to the TimerTask until the first time the TimerTask + * is actually scheduled. But the reference will disappear immediatly when + * cancel is called from within the TimerTask run method. + */ + public boolean cancel() + { + boolean prevented_execution = (this.scheduled >= 0); + this.scheduled = -1; + return prevented_execution; + } + + /** + * Method that is called when this task is scheduled for execution. + */ + public abstract void run(); + + /** + * Returns the last time this task was scheduled or (when called by the + * task from the run method) the time the current execution of the task + * was scheduled. When the task has not yet run the return value is + * undefined. + * <p> + * Can be used (when the task is scheduled at fixed rate) to see the + * difference between the requested schedule time and the actual time + * that can be found with <code>System.currentTimeMillis()</code>. + */ + public long scheduledExecutionTime() + { + return lastExecutionTime; + } +} diff --git a/libjava/classpath/java/util/TooManyListenersException.java b/libjava/classpath/java/util/TooManyListenersException.java new file mode 100644 index 00000000000..92ad772f2bb --- /dev/null +++ b/libjava/classpath/java/util/TooManyListenersException.java @@ -0,0 +1,78 @@ +/* TooManyListenersException.java -- thrown when a unicast event can't accept + another Listener + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This exception is part of the java event model. It is thrown if an + * event listener is added via the addXyzEventListener method, but the + * object doesn't support any more listeners, e.g. it only supports a + * single event listener. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see EventListener + * @see EventObject + * @since 1.1 + * @status updated to 1.4 + */ +public class TooManyListenersException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5074640544770687831L; + + /** + * Constructs a TooManyListenersException with no detail message. + */ + public TooManyListenersException() + { + } + + /** + * Constructs a TooManyListenersException with a detail message. + * + * @param detail the detail message + */ + public TooManyListenersException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/TreeMap.java b/libjava/classpath/java/util/TreeMap.java new file mode 100644 index 00000000000..1e8c805d908 --- /dev/null +++ b/libjava/classpath/java/util/TreeMap.java @@ -0,0 +1,1781 @@ +/* TreeMap.java -- a class providing a basic Red-Black Tree data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a red-black tree implementation of the SortedMap + * interface. Elements in the Map will be sorted by either a user-provided + * Comparator object, or by the natural ordering of the keys. + * + * The algorithms are adopted from Corman, Leiserson, and Rivest's + * <i>Introduction to Algorithms.</i> TreeMap guarantees O(log n) + * insertion and deletion of elements. That being said, there is a large + * enough constant coefficient in front of that "log n" (overhead involved + * in keeping the tree balanced), that TreeMap may not be the best choice + * for small collections. If something is already sorted, you may want to + * just use a LinkedHashMap to maintain the order while providing O(1) access. + * + * TreeMap is a part of the JDK1.2 Collections API. Null keys are allowed + * only if a Comparator is used which can deal with them; natural ordering + * cannot cope with null. Null values are always allowed. Note that the + * ordering must be <i>consistent with equals</i> to correctly implement + * the Map interface. If this condition is violated, the map is still + * well-behaved, but you may have suprising results when comparing it to + * other maps.<p> + * + * This implementation is not synchronized. If you need to share this between + * multiple threads, do something like:<br> + * <code>SortedMap m + * = Collections.synchronizedSortedMap(new TreeMap(...));</code><p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * <code>ConcurrentModificationException</code> rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see HashMap + * @see Hashtable + * @see LinkedHashMap + * @see Comparable + * @see Comparator + * @see Collection + * @see Collections#synchronizedSortedMap(SortedMap) + * @since 1.2 + * @status updated to 1.4 + */ +public class TreeMap extends AbstractMap + implements SortedMap, Cloneable, Serializable +{ + // Implementation note: + // A red-black tree is a binary search tree with the additional properties + // that all paths to a leaf node visit the same number of black nodes, + // and no red node has red children. To avoid some null-pointer checks, + // we use the special node nil which is always black, has no relatives, + // and has key and value of null (but is not equal to a mapping of null). + + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 919286545866124006L; + + /** + * Color status of a node. Package visible for use by nested classes. + */ + static final int RED = -1, + BLACK = 1; + + /** + * Sentinal node, used to avoid null checks for corner cases and make the + * delete rebalance code simpler. The rebalance code must never assign + * the parent, left, or right of nil, but may safely reassign the color + * to be black. This object must never be used as a key in a TreeMap, or + * it will break bounds checking of a SubMap. + */ + static final Node nil = new Node(null, null, BLACK); + static + { + // Nil is self-referential, so we must initialize it after creation. + nil.parent = nil; + nil.left = nil; + nil.right = nil; + } + + /** + * The root node of this TreeMap. + */ + private transient Node root; + + /** + * The size of this TreeMap. Package visible for use by nested classes. + */ + transient int size; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set entries; + + /** + * Counts the number of modifications this TreeMap has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + * Package visible for use by nested classes. + */ + transient int modCount; + + /** + * This TreeMap's comparator, or null for natural ordering. + * Package visible for use by nested classes. + * @serial the comparator ordering this tree, or null + */ + final Comparator comparator; + + /** + * Class to represent an entry in the tree. Holds a single key-value pair, + * plus pointers to parent and child nodes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class Node extends AbstractMap.BasicMapEntry + { + // All fields package visible for use by nested classes. + /** The color of this node. */ + int color; + + /** The left child node. */ + Node left = nil; + /** The right child node. */ + Node right = nil; + /** The parent node. */ + Node parent = nil; + + /** + * Simple constructor. + * @param key the key + * @param value the value + */ + Node(Object key, Object value, int color) + { + super(key, value); + this.color = color; + } + } + + /** + * Instantiate a new TreeMap with no elements, using the keys' natural + * ordering to sort. All entries in the map must have a key which implements + * Comparable, and which are <i>mutually comparable</i>, otherwise map + * operations may throw a {@link ClassCastException}. Attempts to use + * a null key will throw a {@link NullPointerException}. + * + * @see Comparable + */ + public TreeMap() + { + this((Comparator) null); + } + + /** + * Instantiate a new TreeMap with no elements, using the provided comparator + * to sort. All entries in the map must have keys which are mutually + * comparable by the Comparator, otherwise map operations may throw a + * {@link ClassCastException}. + * + * @param c the sort order for the keys of this map, or null + * for the natural order + */ + public TreeMap(Comparator c) + { + comparator = c; + fabricateTree(0); + } + + /** + * Instantiate a new TreeMap, initializing it with all of the elements in + * the provided Map. The elements will be sorted using the natural + * ordering of the keys. This algorithm runs in n*log(n) time. All entries + * in the map must have keys which implement Comparable and are mutually + * comparable, otherwise map operations may throw a + * {@link ClassCastException}. + * + * @param map a Map, whose entries will be put into this TreeMap + * @throws ClassCastException if the keys in the provided Map are not + * comparable + * @throws NullPointerException if map is null + * @see Comparable + */ + public TreeMap(Map map) + { + this((Comparator) null); + putAll(map); + } + + /** + * Instantiate a new TreeMap, initializing it with all of the elements in + * the provided SortedMap. The elements will be sorted using the same + * comparator as in the provided SortedMap. This runs in linear time. + * + * @param sm a SortedMap, whose entries will be put into this TreeMap + * @throws NullPointerException if sm is null + */ + public TreeMap(SortedMap sm) + { + this(sm.comparator()); + int pos = sm.size(); + Iterator itr = sm.entrySet().iterator(); + + fabricateTree(pos); + Node node = firstNode(); + + while (--pos >= 0) + { + Map.Entry me = (Map.Entry) itr.next(); + node.key = me.getKey(); + node.value = me.getValue(); + node = successor(node); + } + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + if (size > 0) + { + modCount++; + root = nil; + size = 0; + } + } + + /** + * Returns a shallow clone of this TreeMap. The Map itself is cloned, + * but its contents are not. + * + * @return the clone + */ + public Object clone() + { + TreeMap copy = null; + try + { + copy = (TreeMap) super.clone(); + } + catch (CloneNotSupportedException x) + { + } + copy.entries = null; + copy.fabricateTree(size); + + Node node = firstNode(); + Node cnode = copy.firstNode(); + + while (node != nil) + { + cnode.key = node.key; + cnode.value = node.value; + node = successor(node); + cnode = copy.successor(cnode); + } + return copy; + } + + /** + * Return the comparator used to sort this map, or null if it is by + * natural order. + * + * @return the map's comparator + */ + public Comparator comparator() + { + return comparator; + } + + /** + * Returns true if the map contains a mapping for the given key. + * + * @param key the key to look for + * @return true if the key has a mapping + * @throws ClassCastException if key is not comparable to map elements + * @throws NullPointerException if key is null and the comparator is not + * tolerant of nulls + */ + public boolean containsKey(Object key) + { + return getNode(key) != nil; + } + + /** + * Returns true if the map contains at least one mapping to the given value. + * This requires linear time. + * + * @param value the value to look for + * @return true if the value appears in a mapping + */ + public boolean containsValue(Object value) + { + Node node = firstNode(); + while (node != nil) + { + if (equals(value, node.value)) + return true; + node = successor(node); + } + return false; + } + + /** + * Returns a "set view" of this TreeMap's entries. The set is backed by + * the TreeMap, so changes in one show up in the other. The set supports + * element removal, but not element addition.<p> + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the TreeMap in sorted sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + entries = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new TreeIterator(ENTRIES); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Node n = getNode(me.getKey()); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Node n = getNode(me.getKey()); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + }; + return entries; + } + + /** + * Returns the first (lowest) key in the map. + * + * @return the first key + * @throws NoSuchElementException if the map is empty + */ + public Object firstKey() + { + if (root == nil) + throw new NoSuchElementException(); + return firstNode().key; + } + + /** + * Return the value in this TreeMap associated with the supplied key, + * or <code>null</code> if the key maps to nothing. NOTE: Since the value + * could also be null, you must use containsKey to see if this key + * actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @throws ClassCastException if key is not comparable to elements in the map + * @throws NullPointerException if key is null but the comparator does not + * tolerate nulls + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public Object get(Object key) + { + // Exploit fact that nil.value == null. + return getNode(key).value; + } + + /** + * Returns a view of this Map including all entries with keys less than + * <code>toKey</code>. The returned map is backed by the original, so changes + * in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map does not include + * the endpoint; if you want inclusion, pass the successor element. + * + * @param toKey the (exclusive) cutoff point + * @return a view of the map less than the cutoff + * @throws ClassCastException if <code>toKey</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if toKey is null, but the comparator does not + * tolerate null elements + */ + public SortedMap headMap(Object toKey) + { + return new SubMap(nil, toKey); + } + + /** + * Returns a "set view" of this TreeMap's keys. The set is backed by the + * TreeMap, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + keys = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new TreeIterator(KEYS); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public boolean remove(Object key) + { + Node n = getNode(key); + if (n == nil) + return false; + removeNode(n); + return true; + } + }; + return keys; + } + + /** + * Returns the last (highest) key in the map. + * + * @return the last key + * @throws NoSuchElementException if the map is empty + */ + public Object lastKey() + { + if (root == nil) + throw new NoSuchElementException("empty"); + return lastNode().key; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which <code>equals()</code> + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. + * + * @param key the key used to locate the value + * @param value the value to be stored in the Map + * @return the prior mapping of the key, or null if there was none + * @throws ClassCastException if key is not comparable to current map keys + * @throws NullPointerException if key is null, but the comparator does + * not tolerate nulls + * @see #get(Object) + * @see Object#equals(Object) + */ + public Object put(Object key, Object value) + { + Node current = root; + Node parent = nil; + int comparison = 0; + + // Find new node's parent. + while (current != nil) + { + parent = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else // Key already in tree. + return current.setValue(value); + } + + // Set up new node. + Node n = new Node(key, value, RED); + n.parent = parent; + + // Insert node in tree. + modCount++; + size++; + if (parent == nil) + { + // Special case inserting into an empty tree. + root = n; + return null; + } + if (comparison > 0) + parent.right = n; + else + parent.left = n; + + // Rebalance after insert. + insertFixup(n); + return null; + } + + /** + * Copies all elements of the given map into this TreeMap. If this map + * already has a mapping for a key, the new mapping replaces the current + * one. + * + * @param m the map to be added + * @throws ClassCastException if a key in m is not comparable with keys + * in the map + * @throws NullPointerException if a key in m is null, and the comparator + * does not tolerate nulls + */ + public void putAll(Map m) + { + Iterator itr = m.entrySet().iterator(); + int pos = m.size(); + while (--pos >= 0) + { + Map.Entry e = (Map.Entry) itr.next(); + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes from the TreeMap and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the TreeMap remains + * unchanged, and <code>null</code> is returned. NOTE: Since the value + * could also be null, you must use containsKey to see if you are + * actually removing a mapping. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + * @throws ClassCastException if key is not comparable to current map keys + * @throws NullPointerException if key is null, but the comparator does + * not tolerate nulls + */ + public Object remove(Object key) + { + Node n = getNode(key); + if (n == nil) + return null; + // Note: removeNode can alter the contents of n, so save value now. + Object result = n.value; + removeNode(n); + return result; + } + + /** + * Returns the number of key-value mappings currently in this Map. + * + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to <code>fromKey</code> and less than <code>toKey</code> (a + * half-open interval). The returned map is backed by the original, so + * changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoffs. The returned map includes the low + * endpoint but not the high; if you want to reverse this behavior on + * either end, pass in the successor element. + * + * @param fromKey the (inclusive) low cutoff point + * @param toKey the (exclusive) high cutoff point + * @return a view of the map between the cutoffs + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey or toKey is null, but the + * comparator does not tolerate null elements + * @throws IllegalArgumentException if fromKey is greater than toKey + */ + public SortedMap subMap(Object fromKey, Object toKey) + { + return new SubMap(fromKey, toKey); + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to <code>fromKey</code>. The returned map is backed by the + * original, so changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map includes the + * endpoint; if you want to exclude it, pass in the successor element. + * + * @param fromKey the (inclusive) low cutoff point + * @return a view of the map above the cutoff + * @throws ClassCastException if <code>fromKey</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey is null, but the comparator + * does not tolerate null elements + */ + public SortedMap tailMap(Object fromKey) + { + return new SubMap(fromKey, nil); + } + + /** + * Returns a "collection view" (or "bag view") of this TreeMap's values. + * The collection is backed by the TreeMap, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new TreeIterator(VALUES); + } + + public void clear() + { + TreeMap.this.clear(); + } + }; + return values; + } + + /** + * Compares two elements by the set comparator, or by natural ordering. + * Package visible for use by nested classes. + * + * @param o1 the first object + * @param o2 the second object + * @throws ClassCastException if o1 and o2 are not mutually comparable, + * or are not Comparable with natural ordering + * @throws NullPointerException if o1 or o2 is null with natural ordering + */ + final int compare(Object o1, Object o2) + { + return (comparator == null + ? ((Comparable) o1).compareTo(o2) + : comparator.compare(o1, o2)); + } + + /** + * Maintain red-black balance after deleting a node. + * + * @param node the child of the node just deleted, possibly nil + * @param parent the parent of the node just deleted, never nil + */ + private void deleteFixup(Node node, Node parent) + { + // if (parent == nil) + // throw new InternalError(); + // If a black node has been removed, we need to rebalance to avoid + // violating the "same number of black nodes on any path" rule. If + // node is red, we can simply recolor it black and all is well. + while (node != root && node.color == BLACK) + { + if (node == parent.left) + { + // Rebalance left side. + Node sibling = parent.right; + // if (sibling == nil) + // throw new InternalError(); + if (sibling.color == RED) + { + // Case 1: Sibling is red. + // Recolor sibling and parent, and rotate parent left. + sibling.color = BLACK; + parent.color = RED; + rotateLeft(parent); + sibling = parent.right; + } + + if (sibling.left.color == BLACK && sibling.right.color == BLACK) + { + // Case 2: Sibling has no red children. + // Recolor sibling, and move to parent. + sibling.color = RED; + node = parent; + parent = parent.parent; + } + else + { + if (sibling.right.color == BLACK) + { + // Case 3: Sibling has red left child. + // Recolor sibling and left child, rotate sibling right. + sibling.left.color = BLACK; + sibling.color = RED; + rotateRight(sibling); + sibling = parent.right; + } + // Case 4: Sibling has red right child. Recolor sibling, + // right child, and parent, and rotate parent left. + sibling.color = parent.color; + parent.color = BLACK; + sibling.right.color = BLACK; + rotateLeft(parent); + node = root; // Finished. + } + } + else + { + // Symmetric "mirror" of left-side case. + Node sibling = parent.left; + // if (sibling == nil) + // throw new InternalError(); + if (sibling.color == RED) + { + // Case 1: Sibling is red. + // Recolor sibling and parent, and rotate parent right. + sibling.color = BLACK; + parent.color = RED; + rotateRight(parent); + sibling = parent.left; + } + + if (sibling.right.color == BLACK && sibling.left.color == BLACK) + { + // Case 2: Sibling has no red children. + // Recolor sibling, and move to parent. + sibling.color = RED; + node = parent; + parent = parent.parent; + } + else + { + if (sibling.left.color == BLACK) + { + // Case 3: Sibling has red right child. + // Recolor sibling and right child, rotate sibling left. + sibling.right.color = BLACK; + sibling.color = RED; + rotateLeft(sibling); + sibling = parent.left; + } + // Case 4: Sibling has red left child. Recolor sibling, + // left child, and parent, and rotate parent right. + sibling.color = parent.color; + parent.color = BLACK; + sibling.left.color = BLACK; + rotateRight(parent); + node = root; // Finished. + } + } + } + node.color = BLACK; + } + + /** + * Construct a perfectly balanced tree consisting of n "blank" nodes. This + * permits a tree to be generated from pre-sorted input in linear time. + * + * @param count the number of blank nodes, non-negative + */ + private void fabricateTree(final int count) + { + if (count == 0) + { + root = nil; + size = 0; + return; + } + + // We color every row of nodes black, except for the overflow nodes. + // I believe that this is the optimal arrangement. We construct the tree + // in place by temporarily linking each node to the next node in the row, + // then updating those links to the children when working on the next row. + + // Make the root node. + root = new Node(null, null, BLACK); + size = count; + Node row = root; + int rowsize; + + // Fill each row that is completely full of nodes. + for (rowsize = 2; rowsize + rowsize <= count; rowsize <<= 1) + { + Node parent = row; + Node last = null; + for (int i = 0; i < rowsize; i += 2) + { + Node left = new Node(null, null, BLACK); + Node right = new Node(null, null, BLACK); + left.parent = parent; + left.right = right; + right.parent = parent; + parent.left = left; + Node next = parent.right; + parent.right = right; + parent = next; + if (last != null) + last.right = left; + last = right; + } + row = row.left; + } + + // Now do the partial final row in red. + int overflow = count - rowsize; + Node parent = row; + int i; + for (i = 0; i < overflow; i += 2) + { + Node left = new Node(null, null, RED); + Node right = new Node(null, null, RED); + left.parent = parent; + right.parent = parent; + parent.left = left; + Node next = parent.right; + parent.right = right; + parent = next; + } + // Add a lone left node if necessary. + if (i - overflow == 0) + { + Node left = new Node(null, null, RED); + left.parent = parent; + parent.left = left; + parent = parent.right; + left.parent.right = nil; + } + // Unlink the remaining nodes of the previous row. + while (parent != nil) + { + Node next = parent.right; + parent.right = nil; + parent = next; + } + } + + /** + * Returns the first sorted node in the map, or nil if empty. Package + * visible for use by nested classes. + * + * @return the first node + */ + final Node firstNode() + { + // Exploit fact that nil.left == nil. + Node node = root; + while (node.left != nil) + node = node.left; + return node; + } + + /** + * Return the TreeMap.Node associated with key, or the nil node if no such + * node exists in the tree. Package visible for use by nested classes. + * + * @param key the key to search for + * @return the node where the key is found, or nil + */ + final Node getNode(Object key) + { + Node current = root; + while (current != nil) + { + int comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else + return current; + } + return current; + } + + /** + * Find the "highest" node which is < key. If key is nil, return last + * node. Package visible for use by nested classes. + * + * @param key the upper bound, exclusive + * @return the previous node + */ + final Node highestLessThan(Object key) + { + if (key == nil) + return lastNode(); + + Node last = nil; + Node current = root; + int comparison = 0; + + while (current != nil) + { + last = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else // Exact match. + return predecessor(last); + } + return comparison <= 0 ? predecessor(last) : last; + } + + /** + * Maintain red-black balance after inserting a new node. + * + * @param n the newly inserted node + */ + private void insertFixup(Node n) + { + // Only need to rebalance when parent is a RED node, and while at least + // 2 levels deep into the tree (ie: node has a grandparent). Remember + // that nil.color == BLACK. + while (n.parent.color == RED && n.parent.parent != nil) + { + if (n.parent == n.parent.parent.left) + { + Node uncle = n.parent.parent.right; + // Uncle may be nil, in which case it is BLACK. + if (uncle.color == RED) + { + // Case 1. Uncle is RED: Change colors of parent, uncle, + // and grandparent, and move n to grandparent. + n.parent.color = BLACK; + uncle.color = BLACK; + uncle.parent.color = RED; + n = uncle.parent; + } + else + { + if (n == n.parent.right) + { + // Case 2. Uncle is BLACK and x is right child. + // Move n to parent, and rotate n left. + n = n.parent; + rotateLeft(n); + } + // Case 3. Uncle is BLACK and x is left child. + // Recolor parent, grandparent, and rotate grandparent right. + n.parent.color = BLACK; + n.parent.parent.color = RED; + rotateRight(n.parent.parent); + } + } + else + { + // Mirror image of above code. + Node uncle = n.parent.parent.left; + // Uncle may be nil, in which case it is BLACK. + if (uncle.color == RED) + { + // Case 1. Uncle is RED: Change colors of parent, uncle, + // and grandparent, and move n to grandparent. + n.parent.color = BLACK; + uncle.color = BLACK; + uncle.parent.color = RED; + n = uncle.parent; + } + else + { + if (n == n.parent.left) + { + // Case 2. Uncle is BLACK and x is left child. + // Move n to parent, and rotate n right. + n = n.parent; + rotateRight(n); + } + // Case 3. Uncle is BLACK and x is right child. + // Recolor parent, grandparent, and rotate grandparent left. + n.parent.color = BLACK; + n.parent.parent.color = RED; + rotateLeft(n.parent.parent); + } + } + } + root.color = BLACK; + } + + /** + * Returns the last sorted node in the map, or nil if empty. + * + * @return the last node + */ + private Node lastNode() + { + // Exploit fact that nil.right == nil. + Node node = root; + while (node.right != nil) + node = node.right; + return node; + } + + /** + * Find the "lowest" node which is >= key. If key is nil, return either + * nil or the first node, depending on the parameter first. + * Package visible for use by nested classes. + * + * @param key the lower bound, inclusive + * @param first true to return the first element instead of nil for nil key + * @return the next node + */ + final Node lowestGreaterThan(Object key, boolean first) + { + if (key == nil) + return first ? firstNode() : nil; + + Node last = nil; + Node current = root; + int comparison = 0; + + while (current != nil) + { + last = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else + return current; + } + return comparison > 0 ? successor(last) : last; + } + + /** + * Return the node preceding the given one, or nil if there isn't one. + * + * @param node the current node, not nil + * @return the prior node in sorted order + */ + private Node predecessor(Node node) + { + if (node.left != nil) + { + node = node.left; + while (node.right != nil) + node = node.right; + return node; + } + + Node parent = node.parent; + // Exploit fact that nil.left == nil and node is non-nil. + while (node == parent.left) + { + node = parent; + parent = node.parent; + } + return parent; + } + + /** + * Construct a tree from sorted keys in linear time. Package visible for + * use by TreeSet. + * + * @param s the stream to read from + * @param count the number of keys to read + * @param readValue true to read values, false to insert "" as the value + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @see #readObject(ObjectInputStream) + * @see TreeSet#readObject(ObjectInputStream) + */ + final void putFromObjStream(ObjectInputStream s, int count, + boolean readValues) + throws IOException, ClassNotFoundException + { + fabricateTree(count); + Node node = firstNode(); + + while (--count >= 0) + { + node.key = s.readObject(); + node.value = readValues ? s.readObject() : ""; + node = successor(node); + } + } + + /** + * Construct a tree from sorted keys in linear time, with values of "". + * Package visible for use by TreeSet. + * + * @param keys the iterator over the sorted keys + * @param count the number of nodes to insert + * @see TreeSet#TreeSet(SortedSet) + */ + final void putKeysLinear(Iterator keys, int count) + { + fabricateTree(count); + Node node = firstNode(); + + while (--count >= 0) + { + node.key = keys.next(); + node.value = ""; + node = successor(node); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the <i>size</i> (int), followed by key (Object) and value + * (Object) pairs in sorted order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int size = s.readInt(); + putFromObjStream(s, size, true); + } + + /** + * Remove node from tree. This will increment modCount and decrement size. + * Node must exist in the tree. Package visible for use by nested classes. + * + * @param node the node to remove + */ + final void removeNode(Node node) + { + Node splice; + Node child; + + modCount++; + size--; + + // Find splice, the node at the position to actually remove from the tree. + if (node.left == nil) + { + // Node to be deleted has 0 or 1 children. + splice = node; + child = node.right; + } + else if (node.right == nil) + { + // Node to be deleted has 1 child. + splice = node; + child = node.left; + } + else + { + // Node has 2 children. Splice is node's predecessor, and we swap + // its contents into node. + splice = node.left; + while (splice.right != nil) + splice = splice.right; + child = splice.left; + node.key = splice.key; + node.value = splice.value; + } + + // Unlink splice from the tree. + Node parent = splice.parent; + if (child != nil) + child.parent = parent; + if (parent == nil) + { + // Special case for 0 or 1 node remaining. + root = child; + return; + } + if (splice == parent.left) + parent.left = child; + else + parent.right = child; + + if (splice.color == BLACK) + deleteFixup(child, parent); + } + + /** + * Rotate node n to the left. + * + * @param node the node to rotate + */ + private void rotateLeft(Node node) + { + Node child = node.right; + // if (node == nil || child == nil) + // throw new InternalError(); + + // Establish node.right link. + node.right = child.left; + if (child.left != nil) + child.left.parent = node; + + // Establish child->parent link. + child.parent = node.parent; + if (node.parent != nil) + { + if (node == node.parent.left) + node.parent.left = child; + else + node.parent.right = child; + } + else + root = child; + + // Link n and child. + child.left = node; + node.parent = child; + } + + /** + * Rotate node n to the right. + * + * @param node the node to rotate + */ + private void rotateRight(Node node) + { + Node child = node.left; + // if (node == nil || child == nil) + // throw new InternalError(); + + // Establish node.left link. + node.left = child.right; + if (child.right != nil) + child.right.parent = node; + + // Establish child->parent link. + child.parent = node.parent; + if (node.parent != nil) + { + if (node == node.parent.right) + node.parent.right = child; + else + node.parent.left = child; + } + else + root = child; + + // Link n and child. + child.right = node; + node.parent = child; + } + + /** + * Return the node following the given one, or nil if there isn't one. + * Package visible for use by nested classes. + * + * @param node the current node, not nil + * @return the next node in sorted order + */ + final Node successor(Node node) + { + if (node.right != nil) + { + node = node.right; + while (node.left != nil) + node = node.left; + return node; + } + + Node parent = node.parent; + // Exploit fact that nil.right == nil and node is non-nil. + while (node == parent.right) + { + node = parent; + parent = parent.parent; + } + return parent; + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the <i>size</i> (int), followed by key (Object) and value + * (Object) pairs in sorted order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + + Node node = firstNode(); + s.writeInt(size); + while (node != nil) + { + s.writeObject(node.key); + s.writeObject(node.value); + node = successor(node); + } + } + + /** + * Iterate over TreeMap's entries. This implementation is parameterized + * to give a sequential view of keys, values, or entries. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class TreeIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + private final int type; + /** The number of modifications to the backing Map that we know about. */ + private int knownMod = modCount; + /** The last Entry returned by a next() call. */ + private Node last; + /** The next entry that should be returned by next(). */ + private Node next; + /** + * The last node visible to this iterator. This is used when iterating + * on a SubMap. + */ + private final Node max; + + /** + * Construct a new TreeIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + TreeIterator(int type) + { + // FIXME gcj cannot handle this. Bug java/4695 + // this(type, firstNode(), nil); + this.type = type; + this.next = firstNode(); + this.max = nil; + } + + /** + * Construct a new TreeIterator with the supplied type. Iteration will + * be from "first" (inclusive) to "max" (exclusive). + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @param first where to start iteration, nil for empty iterator + * @param max the cutoff for iteration, nil for all remaining nodes + */ + TreeIterator(int type, Node first, Node max) + { + this.type = type; + this.next = first; + this.max = max; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + * @throws ConcurrentModificationException if the TreeMap was modified + */ + public boolean hasNext() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return next != max; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the TreeMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (next == max) + throw new NoSuchElementException(); + last = next; + next = successor(last); + + if (type == VALUES) + return last.value; + else if (type == KEYS) + return last.key; + return last; + } + + /** + * Removes from the backing TreeMap the last element which was fetched + * with the <code>next()</code> method. + * @throws ConcurrentModificationException if the TreeMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (last == null) + throw new IllegalStateException(); + if (knownMod != modCount) + throw new ConcurrentModificationException(); + + removeNode(last); + last = null; + knownMod++; + } + } // class TreeIterator + + /** + * Implementation of {@link #subMap(Object, Object)} and other map + * ranges. This class provides a view of a portion of the original backing + * map, and throws {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class SubMap extends AbstractMap implements SortedMap + { + /** + * The lower range of this view, inclusive, or nil for unbounded. + * Package visible for use by nested classes. + */ + final Object minKey; + + /** + * The upper range of this view, exclusive, or nil for unbounded. + * Package visible for use by nested classes. + */ + final Object maxKey; + + /** + * The cache for {@link #entrySet()}. + */ + private Set entries; + + /** + * Create a SubMap representing the elements between minKey (inclusive) + * and maxKey (exclusive). If minKey is nil, SubMap has no lower bound + * (headMap). If maxKey is nil, the SubMap has no upper bound (tailMap). + * + * @param minKey the lower bound + * @param maxKey the upper bound + * @throws IllegalArgumentException if minKey > maxKey + */ + SubMap(Object minKey, Object maxKey) + { + if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0) + throw new IllegalArgumentException("fromKey > toKey"); + this.minKey = minKey; + this.maxKey = maxKey; + } + + /** + * Check if "key" is in within the range bounds for this SubMap. The + * lower ("from") SubMap range is inclusive, and the upper ("to") bound + * is exclusive. Package visible for use by nested classes. + * + * @param key the key to check + * @return true if the key is in range + */ + boolean keyInRange(Object key) + { + return ((minKey == nil || compare(key, minKey) >= 0) + && (maxKey == nil || compare(key, maxKey) < 0)); + } + + public void clear() + { + Node next = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + while (next != max) + { + Node current = next; + next = successor(current); + removeNode(current); + } + } + + public Comparator comparator() + { + return comparator; + } + + public boolean containsKey(Object key) + { + return keyInRange(key) && TreeMap.this.containsKey(key); + } + + public boolean containsValue(Object value) + { + Node node = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + while (node != max) + { + if (equals(value, node.getValue())) + return true; + node = successor(node); + } + return false; + } + + public Set entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + entries = new AbstractSet() + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(ENTRIES, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Object key = me.getKey(); + if (! keyInRange(key)) + return false; + Node n = getNode(key); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Object key = me.getKey(); + if (! keyInRange(key)) + return false; + Node n = getNode(key); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + }; + return entries; + } + + public Object firstKey() + { + Node node = lowestGreaterThan(minKey, true); + if (node == nil || ! keyInRange(node.key)) + throw new NoSuchElementException(); + return node.key; + } + + public Object get(Object key) + { + if (keyInRange(key)) + return TreeMap.this.get(key); + return null; + } + + public SortedMap headMap(Object toKey) + { + if (! keyInRange(toKey)) + throw new IllegalArgumentException("key outside range"); + return new SubMap(minKey, toKey); + } + + public Set keySet() + { + if (this.keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + this.keys = new AbstractSet() + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(KEYS, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! keyInRange(o)) + return false; + return getNode(o) != nil; + } + + public boolean remove(Object o) + { + if (! keyInRange(o)) + return false; + Node n = getNode(o); + if (n != nil) + { + removeNode(n); + return true; + } + return false; + } + }; + return this.keys; + } + + public Object lastKey() + { + Node node = highestLessThan(maxKey); + if (node == nil || ! keyInRange(node.key)) + throw new NoSuchElementException(); + return node.key; + } + + public Object put(Object key, Object value) + { + if (! keyInRange(key)) + throw new IllegalArgumentException("Key outside range"); + return TreeMap.this.put(key, value); + } + + public Object remove(Object key) + { + if (keyInRange(key)) + return TreeMap.this.remove(key); + return null; + } + + public int size() + { + Node node = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + int count = 0; + while (node != max) + { + count++; + node = successor(node); + } + return count; + } + + public SortedMap subMap(Object fromKey, Object toKey) + { + if (! keyInRange(fromKey) || ! keyInRange(toKey)) + throw new IllegalArgumentException("key outside range"); + return new SubMap(fromKey, toKey); + } + + public SortedMap tailMap(Object fromKey) + { + if (! keyInRange(fromKey)) + throw new IllegalArgumentException("key outside range"); + return new SubMap(fromKey, maxKey); + } + + public Collection values() + { + if (this.values == null) + // Create an AbstractCollection with custom implementations of those + // methods that can be overriden easily and efficiently. + this.values = new AbstractCollection() + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(VALUES, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + }; + return this.values; + } + } // class SubMap +} // class TreeMap diff --git a/libjava/classpath/java/util/TreeSet.java b/libjava/classpath/java/util/TreeSet.java new file mode 100644 index 00000000000..34cb39acc07 --- /dev/null +++ b/libjava/classpath/java/util/TreeSet.java @@ -0,0 +1,416 @@ +/* TreeSet.java -- a class providing a TreeMap-backed SortedSet + Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a TreeMap-backed implementation of the SortedSet + * interface. The elements will be sorted according to their <i>natural + * order</i>, or according to the provided <code>Comparator</code>.<p> + * + * Most operations are O(log n), but there is so much overhead that this + * makes small sets expensive. Note that the ordering must be <i>consistent + * with equals</i> to correctly implement the Set interface. If this + * condition is violated, the set is still well-behaved, but you may have + * suprising results when comparing it to other sets.<p> + * + * This implementation is not synchronized. If you need to share this between + * multiple threads, do something like:<br> + * <code>SortedSet s + * = Collections.synchronizedSortedSet(new TreeSet(...));</code><p> + * + * The iterators are <i>fail-fast</i>, meaning that any structural + * modification, except for <code>remove()</code> called on the iterator + * itself, cause the iterator to throw a + * <code>ConcurrentModificationException</code> rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see HashSet + * @see LinkedHashSet + * @see Comparable + * @see Comparator + * @see Collections#synchronizedSortedSet(SortedSet) + * @see TreeMap + * @since 1.2 + * @status updated to 1.4 + */ +public class TreeSet extends AbstractSet + implements SortedSet, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -2479143000061671589L; + + /** + * The SortedMap which backs this Set. + */ + // Not final because of readObject. This will always be one of TreeMap or + // TreeMap.SubMap, which both extend AbstractMap. + private transient SortedMap map; + + /** + * Construct a new TreeSet whose backing TreeMap using the "natural" + * ordering of keys. Elements that are not mutually comparable will cause + * ClassCastExceptions down the road. + * + * @see Comparable + */ + public TreeSet() + { + map = new TreeMap(); + } + + /** + * Construct a new TreeSet whose backing TreeMap uses the supplied + * Comparator. Elements that are not mutually comparable will cause + * ClassCastExceptions down the road. + * + * @param comparator the Comparator this Set will use + */ + public TreeSet(Comparator comparator) + { + map = new TreeMap(comparator); + } + + /** + * Construct a new TreeSet whose backing TreeMap uses the "natural" + * orering of the keys and which contains all of the elements in the + * supplied Collection. This runs in n*log(n) time. + * + * @param collection the new Set will be initialized with all + * of the elements in this Collection + * @throws ClassCastException if the elements of the collection are not + * comparable + * @throws NullPointerException if the collection is null + * @see Comparable + */ + public TreeSet(Collection collection) + { + map = new TreeMap(); + addAll(collection); + } + + /** + * Construct a new TreeSet, using the same key ordering as the supplied + * SortedSet and containing all of the elements in the supplied SortedSet. + * This constructor runs in linear time. + * + * @param sortedSet the new TreeSet will use this SortedSet's comparator + * and will initialize itself with all its elements + * @throws NullPointerException if sortedSet is null + */ + public TreeSet(SortedSet sortedSet) + { + map = new TreeMap(sortedSet.comparator()); + Iterator itr = sortedSet.iterator(); + ((TreeMap) map).putKeysLinear(itr, sortedSet.size()); + } + + /** + * This private constructor is used to implement the subSet() calls around + * a backing TreeMap.SubMap. + * + * @param backingMap the submap + */ + private TreeSet(SortedMap backingMap) + { + map = backingMap; + } + + /** + * Adds the spplied Object to the Set if it is not already in the Set; + * returns true if the element is added, false otherwise. + * + * @param obj the Object to be added to this Set + * @throws ClassCastException if the element cannot be compared with objects + * already in the set + */ + public boolean add(Object obj) + { + return map.put(obj, "") == null; + } + + /** + * Adds all of the elements in the supplied Collection to this TreeSet. + * + * @param c The collection to add + * @return true if the Set is altered, false otherwise + * @throws NullPointerException if c is null + * @throws ClassCastException if an element in c cannot be compared with + * objects already in the set + */ + public boolean addAll(Collection c) + { + boolean result = false; + int pos = c.size(); + Iterator itr = c.iterator(); + while (--pos >= 0) + result |= (map.put(itr.next(), "") == null); + return result; + } + + /** + * Removes all elements in this Set. + */ + public void clear() + { + map.clear(); + } + + /** + * Returns a shallow copy of this Set. The elements are not cloned. + * + * @return the cloned set + */ + public Object clone() + { + TreeSet copy = null; + try + { + copy = (TreeSet) super.clone(); + // Map may be either TreeMap or TreeMap.SubMap, hence the ugly casts. + copy.map = (SortedMap) ((AbstractMap) map).clone(); + } + catch (CloneNotSupportedException x) + { + // Impossible result. + } + return copy; + } + + /** + * Returns this Set's comparator. + * + * @return the comparator, or null if the set uses natural ordering + */ + public Comparator comparator() + { + return map.comparator(); + } + + /** + * Returns true if this Set contains the supplied Object, false otherwise. + * + * @param obj the Object to check for + * @return true if it is in the set + * @throws ClassCastException if obj cannot be compared with objects + * already in the set + */ + public boolean contains(Object obj) + { + return map.containsKey(obj); + } + + /** + * Returns the first (by order) element in this Set. + * + * @return the first element + * @throws NoSuchElementException if the set is empty + */ + public Object first() + { + return map.firstKey(); + } + + /** + * Returns a view of this Set including all elements less than + * <code>to</code>. The returned set is backed by the original, so changes + * in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned set does not include + * the endpoint; if you want inclusion, pass the successor element. + * + * @param to the (exclusive) cutoff point + * @return a view of the set less than the cutoff + * @throws ClassCastException if <code>to</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if to is null, but the comparator does not + * tolerate null elements + */ + public SortedSet headSet(Object to) + { + return new TreeSet(map.headMap(to)); + } + + /** + * Returns true if this Set has size 0, false otherwise. + * + * @return true if the set is empty + */ + public boolean isEmpty() + { + return map.isEmpty(); + } + + /** + * Returns in Iterator over the elements in this TreeSet, which traverses + * in ascending order. + * + * @return an iterator + */ + public Iterator iterator() + { + return map.keySet().iterator(); + } + + /** + * Returns the last (by order) element in this Set. + * + * @return the last element + * @throws NoSuchElementException if the set is empty + */ + public Object last() + { + return map.lastKey(); + } + + /** + * If the supplied Object is in this Set, it is removed, and true is + * returned; otherwise, false is returned. + * + * @param obj the Object to remove from this Set + * @return true if the set was modified + * @throws ClassCastException if obj cannot be compared to set elements + */ + public boolean remove(Object obj) + { + return map.remove(obj) != null; + } + + /** + * Returns the number of elements in this Set + * + * @return the set size + */ + public int size() + { + return map.size(); + } + + /** + * Returns a view of this Set including all elements greater or equal to + * <code>from</code> and less than <code>to</code> (a half-open interval). + * The returned set is backed by the original, so changes in one appear in + * the other. The subset will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoffs. + * The returned set includes the low endpoint but not the high; if you want + * to reverse this behavior on either end, pass in the successor element. + * + * @param from the (inclusive) low cutoff point + * @param to the (exclusive) high cutoff point + * @return a view of the set between the cutoffs + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from or to is null, but the comparator + * does not tolerate null elements + * @throws IllegalArgumentException if from is greater than to + */ + public SortedSet subSet(Object from, Object to) + { + return new TreeSet(map.subMap(from, to)); + } + + /** + * Returns a view of this Set including all elements greater or equal to + * <code>from</code>. The returned set is backed by the original, so + * changes in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned set includes the + * endpoint; if you want to exclude it, pass in the successor element. + * + * @param from the (inclusive) low cutoff point + * @return a view of the set above the cutoff + * @throws ClassCastException if <code>from</code> is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from is null, but the comparator + * does not tolerate null elements + */ + public SortedSet tailSet(Object from) + { + return new TreeSet(map.tailMap(from)); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the <i>comparator</i> (Object), followed by the set size + * (int), the the elements in sorted order (Object) + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + Iterator itr = map.keySet().iterator(); + int pos = map.size(); + s.writeObject(map.comparator()); + s.writeInt(pos); + while (--pos >= 0) + s.writeObject(itr.next()); + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the <i>comparator</i> (Object), followed by the set size + * (int), the the elements in sorted order (Object) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + Comparator comparator = (Comparator) s.readObject(); + int size = s.readInt(); + map = new TreeMap(comparator); + ((TreeMap) map).putFromObjStream(s, size, false); + } +} diff --git a/libjava/classpath/java/util/Vector.java b/libjava/classpath/java/util/Vector.java new file mode 100644 index 00000000000..e26d7aaa4dd --- /dev/null +++ b/libjava/classpath/java/util/Vector.java @@ -0,0 +1,931 @@ +/* Vector.java -- Class that provides growable arrays. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * The <code>Vector</code> classes implements growable arrays of Objects. + * You can access elements in a Vector with an index, just as you + * can in a built in array, but Vectors can grow and shrink to accommodate + * more or fewer objects.<p> + * + * Vectors try to mantain efficiency in growing by having a + * <code>capacityIncrement</code> that can be specified at instantiation. + * When a Vector can no longer hold a new Object, it grows by the amount + * in <code>capacityIncrement</code>. If this value is 0, the vector doubles in + * size.<p> + * + * Vector implements the JDK 1.2 List interface, and is therefore a fully + * compliant Collection object. The iterators are fail-fast - if external + * code structurally modifies the vector, any operation on the iterator will + * then throw a {@link ConcurrentModificationException}. The Vector class is + * fully synchronized, but the iterators are not. So, when iterating over a + * vector, be sure to synchronize on the vector itself. If you don't want the + * expense of synchronization, use ArrayList instead. On the other hand, the + * Enumeration of elements() is not thread-safe, nor is it fail-fast; so it + * can lead to undefined behavior even in a single thread if you modify the + * vector during iteration.<p> + * + * Note: Some methods, especially those specified by List, specify throwing + * {@link IndexOutOfBoundsException}, but it is easier to implement by + * throwing the subclass {@link ArrayIndexOutOfBoundsException}. Others + * directly specify this subclass. + * + * @author Scott G. Miller + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see ArrayList + * @see LinkedList + * @since 1.0 + * @status updated to 1.4 + */ +public class Vector extends AbstractList + implements List, RandomAccess, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2767605614048989439L; + + /** + * The internal array used to hold members of a Vector. The elements are + * in positions 0 through elementCount - 1, and all remaining slots are null. + * @serial the elements + */ + protected Object[] elementData; + + /** + * The number of elements currently in the vector, also returned by + * {@link #size}. + * @serial the size + */ + protected int elementCount; + + /** + * The amount the Vector's internal array should be increased in size when + * a new element is added that exceeds the current size of the array, + * or when {@link #ensureCapacity} is called. If <= 0, the vector just + * doubles in size. + * @serial the amount to grow the vector by + */ + protected int capacityIncrement; + + /** + * Constructs an empty vector with an initial size of 10, and + * a capacity increment of 0 + */ + public Vector() + { + this(10, 0); + } + + /** + * Constructs a vector containing the contents of Collection, in the + * order given by the collection. + * + * @param c collection of elements to add to the new vector + * @throws NullPointerException if c is null + * @since 1.2 + */ + public Vector(Collection c) + { + elementCount = c.size(); + elementData = c.toArray(new Object[elementCount]); + } + + /** + * Constructs a Vector with the initial capacity and capacity + * increment specified. + * + * @param initialCapacity the initial size of the Vector's internal array + * @param capacityIncrement the amount the internal array should be + * increased by when necessary, 0 to double the size + * @throws IllegalArgumentException if initialCapacity < 0 + */ + public Vector(int initialCapacity, int capacityIncrement) + { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + elementData = new Object[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs a Vector with the initial capacity specified, and a capacity + * increment of 0 (double in size). + * + * @param initialCapacity the initial size of the Vector's internal array + * @throws IllegalArgumentException if initialCapacity < 0 + */ + public Vector(int initialCapacity) + { + this(initialCapacity, 0); + } + + /** + * Copies the contents of a provided array into the Vector. If the + * array is too large to fit in the Vector, an IndexOutOfBoundsException + * is thrown without modifying the array. Old elements in the Vector are + * overwritten by the new elements. + * + * @param a target array for the copy + * @throws IndexOutOfBoundsException the array is not large enough + * @throws NullPointerException the array is null + * @see #toArray(Object[]) + */ + public synchronized void copyInto(Object[] a) + { + System.arraycopy(elementData, 0, a, 0, elementCount); + } + + /** + * Trims the Vector down to size. If the internal data array is larger + * than the number of Objects its holding, a new array is constructed + * that precisely holds the elements. Otherwise this does nothing. + */ + public synchronized void trimToSize() + { + // Don't bother checking for the case where size() == the capacity of the + // vector since that is a much less likely case; it's more efficient to + // not do the check and lose a bit of performance in that infrequent case + + Object[] newArray = new Object[elementCount]; + System.arraycopy(elementData, 0, newArray, 0, elementCount); + elementData = newArray; + } + + /** + * Ensures that <code>minCapacity</code> elements can fit within this Vector. + * If <code>elementData</code> is too small, it is expanded as follows: + * If the <code>elementCount + capacityIncrement</code> is adequate, that + * is the new size. If <code>capacityIncrement</code> is non-zero, the + * candidate size is double the current. If that is not enough, the new + * size is <code>minCapacity</code>. + * + * @param minCapacity the desired minimum capacity, negative values ignored + */ + public synchronized void ensureCapacity(int minCapacity) + { + if (elementData.length >= minCapacity) + return; + + int newCapacity; + if (capacityIncrement <= 0) + newCapacity = elementData.length * 2; + else + newCapacity = elementData.length + capacityIncrement; + + Object[] newArray = new Object[Math.max(newCapacity, minCapacity)]; + + System.arraycopy(elementData, 0, newArray, 0, elementCount); + elementData = newArray; + } + + /** + * Explicitly sets the size of the vector (but not necessarily the size of + * the internal data array). If the new size is smaller than the old one, + * old values that don't fit are lost. If the new size is larger than the + * old one, the vector is padded with null entries. + * + * @param newSize The new size of the internal array + * @throws ArrayIndexOutOfBoundsException if the new size is negative + */ + public synchronized void setSize(int newSize) + { + // Don't bother checking for the case where size() == the capacity of the + // vector since that is a much less likely case; it's more efficient to + // not do the check and lose a bit of performance in that infrequent case + modCount++; + ensureCapacity(newSize); + if (newSize < elementCount) + Arrays.fill(elementData, newSize, elementCount, null); + elementCount = newSize; + } + + /** + * Returns the size of the internal data array (not the amount of elements + * contained in the Vector). + * + * @return capacity of the internal data array + */ + public synchronized int capacity() + { + return elementData.length; + } + + /** + * Returns the number of elements stored in this Vector. + * + * @return the number of elements in this Vector + */ + public synchronized int size() + { + return elementCount; + } + + /** + * Returns true if this Vector is empty, false otherwise + * + * @return true if the Vector is empty, false otherwise + */ + public synchronized boolean isEmpty() + { + return elementCount == 0; + } + + /** + * Returns an Enumeration of the elements of this Vector. The enumeration + * visits the elements in increasing index order, but is NOT thread-safe. + * + * @return an Enumeration + * @see #iterator() + */ + // No need to synchronize as the Enumeration is not thread-safe! + public Enumeration elements() + { + return new Enumeration() + { + private int i = 0; + + public boolean hasMoreElements() + { + return i < elementCount; + } + + public Object nextElement() + { + if (i >= elementCount) + throw new NoSuchElementException(); + return elementData[i++]; + } + }; + } + + /** + * Returns true when <code>elem</code> is contained in this Vector. + * + * @param elem the element to check + * @return true if the object is contained in this Vector, false otherwise + */ + public boolean contains(Object elem) + { + return indexOf(elem, 0) >= 0; + } + + /** + * Returns the first occurrence of <code>elem</code> in the Vector, or -1 if + * <code>elem</code> is not found. + * + * @param elem the object to search for + * @return the index of the first occurrence, or -1 if not found + */ + public int indexOf(Object elem) + { + return indexOf(elem, 0); + } + + /** + * Searches the vector starting at <code>index</code> for object + * <code>elem</code> and returns the index of the first occurrence of this + * Object. If the object is not found, or index is larger than the size + * of the vector, -1 is returned. + * + * @param e the Object to search for + * @param index start searching at this index + * @return the index of the next occurrence, or -1 if it is not found + * @throws IndexOutOfBoundsException if index < 0 + */ + public synchronized int indexOf(Object e, int index) + { + for (int i = index; i < elementCount; i++) + if (equals(e, elementData[i])) + return i; + return -1; + } + + /** + * Returns the last index of <code>elem</code> within this Vector, or -1 + * if the object is not within the Vector. + * + * @param elem the object to search for + * @return the last index of the object, or -1 if not found + */ + public int lastIndexOf(Object elem) + { + return lastIndexOf(elem, elementCount - 1); + } + + /** + * Returns the index of the first occurrence of <code>elem</code>, when + * searching backwards from <code>index</code>. If the object does not + * occur in this Vector, or index is less than 0, -1 is returned. + * + * @param e the object to search for + * @param index the index to start searching in reverse from + * @return the index of the Object if found, -1 otherwise + * @throws IndexOutOfBoundsException if index >= size() + */ + public synchronized int lastIndexOf(Object e, int index) + { + checkBoundExclusive(index); + for (int i = index; i >= 0; i--) + if (equals(e, elementData[i])) + return i; + return -1; + } + + /** + * Returns the Object stored at <code>index</code>. + * + * @param index the index of the Object to retrieve + * @return the object at <code>index</code> + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @see #get(int) + */ + public synchronized Object elementAt(int index) + { + checkBoundExclusive(index); + return elementData[index]; + } + + /** + * Returns the first element (index 0) in the Vector. + * + * @return the first Object in the Vector + * @throws NoSuchElementException the Vector is empty + */ + public synchronized Object firstElement() + { + if (elementCount == 0) + throw new NoSuchElementException(); + + return elementData[0]; + } + + /** + * Returns the last element in the Vector. + * + * @return the last Object in the Vector + * @throws NoSuchElementException the Vector is empty + */ + public synchronized Object lastElement() + { + if (elementCount == 0) + throw new NoSuchElementException(); + + return elementData[elementCount - 1]; + } + + /** + * Changes the element at <code>index</code> to be <code>obj</code> + * + * @param obj the object to store + * @param index the position in the Vector to store the object + * @throws ArrayIndexOutOfBoundsException the index is out of range + * @see #set(int, Object) + */ + public void setElementAt(Object obj, int index) + { + set(index, obj); + } + + /** + * Removes the element at <code>index</code>, and shifts all elements at + * positions greater than index to their index - 1. + * + * @param index the index of the element to remove + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size(); + * @see #remove(int) + */ + public void removeElementAt(int index) + { + remove(index); + } + + /** + * Inserts a new element into the Vector at <code>index</code>. Any elements + * at or greater than index are shifted up one position. + * + * @param obj the object to insert + * @param index the index at which the object is inserted + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @see #add(int, Object) + */ + public synchronized void insertElementAt(Object obj, int index) + { + checkBoundInclusive(index); + if (elementCount == elementData.length) + ensureCapacity(elementCount + 1); + modCount++; + System.arraycopy(elementData, index, elementData, index + 1, + elementCount - index); + elementCount++; + elementData[index] = obj; + } + + /** + * Adds an element to the Vector at the end of the Vector. The vector + * is increased by ensureCapacity(size() + 1) if needed. + * + * @param obj the object to add to the Vector + */ + public synchronized void addElement(Object obj) + { + if (elementCount == elementData.length) + ensureCapacity(elementCount + 1); + modCount++; + elementData[elementCount++] = obj; + } + + /** + * Removes the first (the lowestindex) occurance of the given object from + * the Vector. If such a remove was performed (the object was found), true + * is returned. If there was no such object, false is returned. + * + * @param obj the object to remove from the Vector + * @return true if the Object was in the Vector, false otherwise + * @see #remove(Object) + */ + public synchronized boolean removeElement(Object obj) + { + int idx = indexOf(obj, 0); + if (idx >= 0) + { + remove(idx); + return true; + } + return false; + } + + /** + * Removes all elements from the Vector. Note that this does not + * resize the internal data array. + * + * @see #clear() + */ + public synchronized void removeAllElements() + { + if (elementCount == 0) + return; + + modCount++; + Arrays.fill(elementData, 0, elementCount, null); + elementCount = 0; + } + + /** + * Creates a new Vector with the same contents as this one. The clone is + * shallow; elements are not cloned. + * + * @return the clone of this vector + */ + public synchronized Object clone() + { + try + { + Vector clone = (Vector) super.clone(); + clone.elementData = (Object[]) elementData.clone(); + return clone; + } + catch (CloneNotSupportedException ex) + { + // Impossible to get here. + throw new InternalError(ex.toString()); + } + } + + /** + * Returns an Object array with the contents of this Vector, in the order + * they are stored within this Vector. Note that the Object array returned + * is not the internal data array, and that it holds only the elements + * within the Vector. This is similar to creating a new Object[] with the + * size of this Vector, then calling Vector.copyInto(yourArray). + * + * @return an Object[] containing the contents of this Vector in order + * @since 1.2 + */ + public synchronized Object[] toArray() + { + Object[] newArray = new Object[elementCount]; + copyInto(newArray); + return newArray; + } + + /** + * Returns an array containing the contents of this Vector. + * If the provided array is large enough, the contents are copied + * into that array, and a null is placed in the position size(). + * In this manner, you can obtain the size of a Vector by the position + * of the null element, if you know the vector does not itself contain + * null entries. If the array is not large enough, reflection is used + * to create a bigger one of the same runtime type. + * + * @param a an array to copy the Vector into if large enough + * @return an array with the contents of this Vector in order + * @throws ArrayStoreException the runtime type of the provided array + * cannot hold the elements of the Vector + * @throws NullPointerException if <code>a</code> is null + * @since 1.2 + */ + public synchronized Object[] toArray(Object[] a) + { + if (a.length < elementCount) + a = (Object[]) Array.newInstance(a.getClass().getComponentType(), + elementCount); + else if (a.length > elementCount) + a[elementCount] = null; + System.arraycopy(elementData, 0, a, 0, elementCount); + return a; + } + + /** + * Returns the element at position <code>index</code>. + * + * @param index the position from which an element will be retrieved + * @return the element at that position + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + public Object get(int index) + { + return elementAt(index); + } + + /** + * Puts <code>element</code> into the Vector at position <code>index</code> + * and returns the Object that previously occupied that position. + * + * @param index the index within the Vector to place the Object + * @param element the Object to store in the Vector + * @return the previous object at the specified index + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + public synchronized Object set(int index, Object element) + { + checkBoundExclusive(index); + Object temp = elementData[index]; + elementData[index] = element; + return temp; + } + + /** + * Adds an object to the Vector. + * + * @param o the element to add to the Vector + * @return true, as specified by List + * @since 1.2 + */ + public boolean add(Object o) + { + addElement(o); + return true; + } + + /** + * Removes the given Object from the Vector. If it exists, true + * is returned, if not, false is returned. + * + * @param o the object to remove from the Vector + * @return true if the Object existed in the Vector, false otherwise + * @since 1.2 + */ + public boolean remove(Object o) + { + return removeElement(o); + } + + /** + * Adds an object at the specified index. Elements at or above + * index are shifted up one position. + * + * @param index the index at which to add the element + * @param element the element to add to the Vector + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @since 1.2 + */ + public void add(int index, Object element) + { + insertElementAt(element, index); + } + + /** + * Removes the element at the specified index, and returns it. + * + * @param index the position from which to remove the element + * @return the object removed + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + public synchronized Object remove(int index) + { + checkBoundExclusive(index); + Object temp = elementData[index]; + modCount++; + elementCount--; + if (index < elementCount) + System.arraycopy(elementData, index + 1, elementData, index, + elementCount - index); + elementData[elementCount] = null; + return temp; + } + + /** + * Clears all elements in the Vector and sets its size to 0. + */ + public void clear() + { + removeAllElements(); + } + + /** + * Returns true if this Vector contains all the elements in c. + * + * @param c the collection to compare to + * @return true if this vector contains all elements of c + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean containsAll(Collection c) + { + // Here just for the sychronization. + return super.containsAll(c); + } + + /** + * Appends all elements of the given collection to the end of this Vector. + * Behavior is undefined if the collection is modified during this operation + * (for example, if this == c). + * + * @param c the collection to append + * @return true if this vector changed, in other words c was not empty + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean addAll(Collection c) + { + return addAll(elementCount, c); + } + + /** + * Remove from this vector all elements contained in the given collection. + * + * @param c the collection to filter out + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean removeAll(Collection c) + { + if (c == null) + throw new NullPointerException(); + + int i; + int j; + for (i = 0; i < elementCount; i++) + if (c.contains(elementData[i])) + break; + if (i == elementCount) + return false; + + modCount++; + for (j = i++; i < elementCount; i++) + if (! c.contains(elementData[i])) + elementData[j++] = elementData[i]; + elementCount -= i - j; + return true; + } + + /** + * Retain in this vector only the elements contained in the given collection. + * + * @param c the collection to filter by + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean retainAll(Collection c) + { + if (c == null) + throw new NullPointerException(); + + int i; + int j; + for (i = 0; i < elementCount; i++) + if (! c.contains(elementData[i])) + break; + if (i == elementCount) + return false; + + modCount++; + for (j = i++; i < elementCount; i++) + if (c.contains(elementData[i])) + elementData[j++] = elementData[i]; + elementCount -= i - j; + return true; + } + + /** + * Inserts all elements of the given collection at the given index of + * this Vector. Behavior is undefined if the collection is modified during + * this operation (for example, if this == c). + * + * @param c the collection to append + * @return true if this vector changed, in other words c was not empty + * @throws NullPointerException if c is null + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @since 1.2 + */ + public synchronized boolean addAll(int index, Collection c) + { + checkBoundInclusive(index); + Iterator itr = c.iterator(); + int csize = c.size(); + + modCount++; + ensureCapacity(elementCount + csize); + int end = index + csize; + if (elementCount > 0 && index != elementCount) + System.arraycopy(elementData, index, + elementData, end, elementCount - index); + elementCount += csize; + for ( ; index < end; index++) + elementData[index] = itr.next(); + return (csize > 0); + } + + /** + * Compares this to the given object. + * + * @param o the object to compare to + * @return true if the two are equal + * @since 1.2 + */ + public synchronized boolean equals(Object o) + { + // Here just for the sychronization. + return super.equals(o); + } + + /** + * Computes the hashcode of this object. + * + * @return the hashcode + * @since 1.2 + */ + public synchronized int hashCode() + { + // Here just for the sychronization. + return super.hashCode(); + } + + /** + * Returns a string representation of this Vector in the form + * "[element0, element1, ... elementN]". + * + * @return the String representation of this Vector + */ + public synchronized String toString() + { + // Here just for the sychronization. + return super.toString(); + } + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list is modifiable, and changes in one + * reflect in the other. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + * <p> + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this vector + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() + * @throws IllegalArgumentException if fromIndex > toIndex + * @see ConcurrentModificationException + * @since 1.2 + */ + public synchronized List subList(int fromIndex, int toIndex) + { + List sub = super.subList(fromIndex, toIndex); + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + return new Collections.SynchronizedList(this, sub); + } + + /** + * Removes a range of elements from this list. + * Does nothing when toIndex is equal to fromIndex. + * + * @param fromIndex the index to start deleting from (inclusive) + * @param toIndex the index to delete up to (exclusive) + * @throws IndexOutOfBoundsException if fromIndex > toIndex + */ + // This does not need to be synchronized, because it is only called through + // clear() of a sublist, and clear() had already synchronized. + protected void removeRange(int fromIndex, int toIndex) + { + int change = toIndex - fromIndex; + if (change > 0) + { + modCount++; + System.arraycopy(elementData, toIndex, elementData, fromIndex, + elementCount - toIndex); + int save = elementCount; + elementCount -= change; + Arrays.fill(elementData, elementCount, save, null); + } + else if (change < 0) + throw new IndexOutOfBoundsException(); + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws ArrayIndexOutOfBoundsException if index > size + */ + private void checkBoundInclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException + // with no effort on our part. + if (index > elementCount) + throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws ArrayIndexOutOfBoundsException if index >= size + */ + private void checkBoundExclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException + // with no effort on our part. + if (index >= elementCount) + throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData just calls default write function + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + } + +} diff --git a/libjava/classpath/java/util/WeakHashMap.java b/libjava/classpath/java/util/WeakHashMap.java new file mode 100644 index 00000000000..7593f7e330e --- /dev/null +++ b/libjava/classpath/java/util/WeakHashMap.java @@ -0,0 +1,881 @@ +/* WeakHashMap -- a hashtable that keeps only weak references + to its keys, allowing the virtual machine to reclaim them + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +/** + * A weak hash map has only weak references to the key. This means that it + * allows the key to be garbage collected if it is not used otherwise. If + * this happens, the entry will eventually disappear from the map, + * asynchronously. + * + * <p>A weak hash map makes most sense when the keys doesn't override the + * <code>equals</code> method: If there is no other reference to the + * key nobody can ever look up the key in this table and so the entry + * can be removed. This table also works when the <code>equals</code> + * method is overloaded, such as String keys, but you should be prepared + * to deal with some entries disappearing spontaneously. + * + * <p>Other strange behaviors to be aware of: The size of this map may + * spontaneously shrink (even if you use a synchronized map and synchronize + * it); it behaves as if another thread removes entries from this table + * without synchronization. The entry set returned by <code>entrySet</code> + * has similar phenomenons: The size may spontaneously shrink, or an + * entry, that was in the set before, suddenly disappears. + * + * <p>A weak hash map is not meant for caches; use a normal map, with + * soft references as values instead, or try {@link LinkedHashMap}. + * + * <p>The weak hash map supports null values and null keys. The null key + * is never deleted from the map (except explictly of course). The + * performance of the methods are similar to that of a hash map. + * + * <p>The value objects are strongly referenced by this table. So if a + * value object maintains a strong reference to the key (either direct + * or indirect) the key will never be removed from this map. According + * to Sun, this problem may be fixed in a future release. It is not + * possible to do it with the jdk 1.2 reference model, though. + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * + * @see HashMap + * @see WeakReference + * @see LinkedHashMap + * @since 1.2 + * @status updated to 1.4 + */ +public class WeakHashMap extends AbstractMap implements Map +{ + // WARNING: WeakHashMap is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The default capacity for an instance of HashMap. + * Sun's documentation mildly suggests that this (11) is the correct + * value. + */ + private static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor of a HashMap. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + + /** + * This is used instead of the key value <i>null</i>. It is needed + * to distinguish between an null key and a removed key. + */ + // Package visible for use by nested classes. + static final Object NULL_KEY = new Object() + { + /** + * Sets the hashCode to 0, since that's what null would map to. + * @return the hash code 0 + */ + public int hashCode() + { + return 0; + } + + /** + * Compares this key to the given object. Normally, an object should + * NEVER compare equal to null, but since we don't publicize NULL_VALUE, + * it saves bytecode to do so here. + * @return true iff o is this or null + */ + public boolean equals(Object o) + { + return null == o || this == o; + } + }; + + /** + * The reference queue where our buckets (which are WeakReferences) are + * registered to. + */ + private final ReferenceQueue queue; + + /** + * The number of entries in this hash map. + */ + // Package visible for use by nested classes. + int size; + + /** + * The load factor of this WeakHashMap. This is the maximum ratio of + * size versus number of buckets. If size grows the number of buckets + * must grow, too. + */ + private float loadFactor; + + /** + * The rounded product of the capacity (i.e. number of buckets) and + * the load factor. When the number of elements exceeds the + * threshold, the HashMap calls <code>rehash()</code>. + */ + private int threshold; + + /** + * The number of structural modifications. This is used by + * iterators, to see if they should fail. This doesn't count + * the silent key removals, when a weak reference is cleared + * by the garbage collection. Instead the iterators must make + * sure to have strong references to the entries they rely on. + */ + // Package visible for use by nested classes. + int modCount; + + /** + * The entry set. There is only one instance per hashmap, namely + * theEntrySet. Note that the entry set may silently shrink, just + * like the WeakHashMap. + */ + private final class WeakEntrySet extends AbstractSet + { + /** + * Non-private constructor to reduce bytecode emitted. + */ + WeakEntrySet() + { + } + + /** + * Returns the size of this set. + * + * @return the set size + */ + public int size() + { + return size; + } + + /** + * Returns an iterator for all entries. + * + * @return an Entry iterator + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The entry that was returned by the last + * <code>next()</code> call. This is also the entry whose + * bucket should be removed by the <code>remove</code> call. <br> + * + * It is null, if the <code>next</code> method wasn't + * called yet, or if the entry was already removed. <br> + * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry lastEntry; + + /** + * The entry that will be returned by the next + * <code>next()</code> call. It is <code>null</code> if there + * is no further entry. <br> + * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry nextEntry = findNext(null); + + /** + * The known number of modification to the list, if it differs + * from the real number, we throw an exception. + */ + int knownMod = modCount; + + /** + * Check the known number of modification to the number of + * modifications of the table. If it differs from the real + * number, we throw an exception. + * @throws ConcurrentModificationException if the number + * of modifications doesn't match. + */ + private void checkMod() + { + // This method will get inlined. + cleanQueue(); + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Get a strong reference to the next entry after + * lastBucket. + * @param lastEntry the previous bucket, or null if we should + * get the first entry. + * @return the next entry. + */ + private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry) + { + int slot; + WeakBucket nextBucket; + if (lastEntry != null) + { + nextBucket = lastEntry.getBucket().next; + slot = lastEntry.getBucket().slot; + } + else + { + nextBucket = buckets[0]; + slot = 0; + } + + while (true) + { + while (nextBucket != null) + { + WeakBucket.WeakEntry entry = nextBucket.getEntry(); + if (entry != null) + // This is the next entry. + return entry; + + // Entry was cleared, try next. + nextBucket = nextBucket.next; + } + + slot++; + if (slot == buckets.length) + // No more buckets, we are through. + return null; + + nextBucket = buckets[slot]; + } + } + + /** + * Checks if there are more entries. + * @return true, iff there are more elements. + * @throws ConcurrentModificationException if the hash map was + * modified. + */ + public boolean hasNext() + { + checkMod(); + return nextEntry != null; + } + + /** + * Returns the next entry. + * @return the next entry. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws NoSuchElementException if there is no entry. + */ + public Object next() + { + checkMod(); + if (nextEntry == null) + throw new NoSuchElementException(); + lastEntry = nextEntry; + nextEntry = findNext(lastEntry); + return lastEntry; + } + + /** + * Removes the last returned entry from this set. This will + * also remove the bucket of the underlying weak hash map. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws IllegalStateException if <code>next()</code> was + * never called or the element was already removed. + */ + public void remove() + { + checkMod(); + if (lastEntry == null) + throw new IllegalStateException(); + modCount++; + internalRemove(lastEntry.getBucket()); + lastEntry = null; + knownMod++; + } + }; + } + } + + /** + * A bucket is a weak reference to the key, that contains a strong + * reference to the value, a pointer to the next bucket and its slot + * number. <br> + * + * It would be cleaner to have a WeakReference as field, instead of + * extending it, but if a weak reference gets cleared, we only get + * the weak reference (by queue.poll) and wouldn't know where to + * look for this reference in the hashtable, to remove that entry. + * + * @author Jochen Hoenicke + */ + private static class WeakBucket extends WeakReference + { + /** + * The value of this entry. The key is stored in the weak + * reference that we extend. + */ + Object value; + + /** + * The next bucket describing another entry that uses the same + * slot. + */ + WeakBucket next; + + /** + * The slot of this entry. This should be + * <code>Math.abs(key.hashCode() % buckets.length)</code>. + * + * But since the key may be silently removed we have to remember + * the slot number. + * + * If this bucket was removed the slot is -1. This marker will + * prevent the bucket from being removed twice. + */ + int slot; + + /** + * Creates a new bucket for the given key/value pair and the specified + * slot. + * @param key the key + * @param queue the queue the weak reference belongs to + * @param value the value + * @param slot the slot. This must match the slot where this bucket + * will be enqueued. + */ + public WeakBucket(Object key, ReferenceQueue queue, Object value, + int slot) + { + super(key, queue); + this.value = value; + this.slot = slot; + } + + /** + * This class gives the <code>Entry</code> representation of the + * current bucket. It also keeps a strong reference to the + * key; bad things may happen otherwise. + */ + class WeakEntry implements Map.Entry + { + /** + * The strong ref to the key. + */ + Object key; + + /** + * Creates a new entry for the key. + * @param key the key + */ + public WeakEntry(Object key) + { + this.key = key; + } + + /** + * Returns the underlying bucket. + * @return the owning bucket + */ + public WeakBucket getBucket() + { + return WeakBucket.this; + } + + /** + * Returns the key. + * @return the key + */ + public Object getKey() + { + return key == NULL_KEY ? null : key; + } + + /** + * Returns the value. + * @return the value + */ + public Object getValue() + { + return value; + } + + /** + * This changes the value. This change takes place in + * the underlying hash map. + * @param newVal the new value + * @return the old value + */ + public Object setValue(Object newVal) + { + Object oldVal = value; + value = newVal; + return oldVal; + } + + /** + * The hashCode as specified in the Entry interface. + * @return the hash code + */ + public int hashCode() + { + return key.hashCode() ^ WeakHashMap.hashCode(value); + } + + /** + * The equals method as specified in the Entry interface. + * @param o the object to compare to + * @return true iff o represents the same key/value pair + */ + public boolean equals(Object o) + { + if (o instanceof Map.Entry) + { + Map.Entry e = (Map.Entry) o; + return key.equals(e.getKey()) + && WeakHashMap.equals(value, e.getValue()); + } + return false; + } + + public String toString() + { + return key + "=" + value; + } + } + + /** + * This returns the entry stored in this bucket, or null, if the + * bucket got cleared in the mean time. + * @return the Entry for this bucket, if it exists + */ + WeakEntry getEntry() + { + final Object key = this.get(); + if (key == null) + return null; + return new WeakEntry(key); + } + } + + /** + * The entry set returned by <code>entrySet()</code>. + */ + private final WeakEntrySet theEntrySet; + + /** + * The hash buckets. These are linked lists. Package visible for use in + * nested classes. + */ + WeakBucket[] buckets; + + /** + * Creates a new weak hash map with default load factor and default + * capacity. + */ + public WeakHashMap() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with default load factor and the given + * capacity. + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if initialCapacity is negative + */ + public WeakHashMap(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with the given initial capacity and + * load factor. + * @param initialCapacity the initial capacity. + * @param loadFactor the load factor (see class description of HashMap). + * @throws IllegalArgumentException if initialCapacity is negative, or + * loadFactor is non-positive + */ + public WeakHashMap(int initialCapacity, float loadFactor) + { + // Check loadFactor for NaN as well. + if (initialCapacity < 0 || ! (loadFactor > 0)) + throw new IllegalArgumentException(); + if (initialCapacity == 0) + initialCapacity = 1; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + theEntrySet = new WeakEntrySet(); + queue = new ReferenceQueue(); + buckets = new WeakBucket[initialCapacity]; + } + + /** + * Construct a new WeakHashMap with the same mappings as the given map. + * The WeakHashMap has a default load factor of 0.75. + * + * @param m the map to copy + * @throws NullPointerException if m is null + * @since 1.3 + */ + public WeakHashMap(Map m) + { + this(m.size(), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Simply hashes a non-null Object to its array index. + * @param key the key to hash + * @return its slot number + */ + private int hash(Object key) + { + return Math.abs(key.hashCode() % buckets.length); + } + + /** + * Cleans the reference queue. This will poll all references (which + * are WeakBuckets) from the queue and remove them from this map. + * This will not change modCount, even if it modifies the map. The + * iterators have to make sure that nothing bad happens. <br> + * + * Currently the iterator maintains a strong reference to the key, so + * that is no problem. + */ + // Package visible for use by nested classes. + void cleanQueue() + { + Object bucket = queue.poll(); + while (bucket != null) + { + internalRemove((WeakBucket) bucket); + bucket = queue.poll(); + } + } + + /** + * Rehashes this hashtable. This will be called by the + * <code>add()</code> method if the size grows beyond the threshold. + * It will grow the bucket size at least by factor two and allocates + * new buckets. + */ + private void rehash() + { + WeakBucket[] oldBuckets = buckets; + int newsize = buckets.length * 2 + 1; // XXX should be prime. + threshold = (int) (newsize * loadFactor); + buckets = new WeakBucket[newsize]; + + // Now we have to insert the buckets again. + for (int i = 0; i < oldBuckets.length; i++) + { + WeakBucket bucket = oldBuckets[i]; + WeakBucket nextBucket; + while (bucket != null) + { + nextBucket = bucket.next; + + Object key = bucket.get(); + if (key == null) + { + // This bucket should be removed; it is probably + // already on the reference queue. We don't insert it + // at all, and mark it as cleared. + bucket.slot = -1; + size--; + } + else + { + // Add this bucket to its new slot. + int slot = hash(key); + bucket.slot = slot; + bucket.next = buckets[slot]; + buckets[slot] = bucket; + } + bucket = nextBucket; + } + } + } + + /** + * Finds the entry corresponding to key. Since it returns an Entry + * it will also prevent the key from being removed under us. + * @param key the key, may be null + * @return The WeakBucket.WeakEntry or null, if the key wasn't found. + */ + private WeakBucket.WeakEntry internalGet(Object key) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = buckets[slot]; + while (bucket != null) + { + WeakBucket.WeakEntry entry = bucket.getEntry(); + if (entry != null && key.equals(entry.key)) + return entry; + + bucket = bucket.next; + } + return null; + } + + /** + * Adds a new key/value pair to the hash map. + * @param key the key. This mustn't exists in the map. It may be null. + * @param value the value. + */ + private void internalAdd(Object key, Object value) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = new WeakBucket(key, queue, value, slot); + bucket.next = buckets[slot]; + buckets[slot] = bucket; + size++; + } + + /** + * Removes a bucket from this hash map, if it wasn't removed before + * (e.g. one time through rehashing and one time through reference queue). + * Package visible for use in nested classes. + * + * @param bucket the bucket to remove. + */ + void internalRemove(WeakBucket bucket) + { + int slot = bucket.slot; + if (slot == -1) + // This bucket was already removed. + return; + + // Mark the bucket as removed. This is necessary, since the + // bucket may be enqueued later by the garbage collection, and + // internalRemove will be called a second time. + bucket.slot = -1; + if (buckets[slot] == bucket) + buckets[slot] = bucket.next; + else + { + WeakBucket prev = buckets[slot]; + /* This may throw a NullPointerException. It shouldn't but if + * a race condition occurred (two threads removing the same + * bucket at the same time) it may happen. <br> + * But with race condition many much worse things may happen + * anyway. + */ + while (prev.next != bucket) + prev = prev.next; + prev.next = bucket.next; + } + size--; + } + + /** + * Returns the size of this hash map. Note that the size() may shrink + * spontaneously, if the some of the keys were only weakly reachable. + * @return the number of entries in this hash map. + */ + public int size() + { + cleanQueue(); + return size; + } + + /** + * Tells if the map is empty. Note that the result may change + * spontanously, if all of the keys were only weakly reachable. + * @return true, iff the map is empty. + */ + public boolean isEmpty() + { + cleanQueue(); + return size == 0; + } + + /** + * Tells if the map contains the given key. Note that the result + * may change spontanously, if the key was only weakly + * reachable. + * @param key the key to look for + * @return true, iff the map contains an entry for the given key. + */ + public boolean containsKey(Object key) + { + cleanQueue(); + return internalGet(key) != null; + } + + /** + * Gets the value the key is mapped to. + * @return the value the key was mapped to. It returns null if + * the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public Object get(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + return entry == null ? null : entry.getValue(); + } + + /** + * Adds a new key/value mapping to this map. + * @param key the key, may be null + * @param value the value, may be null + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value + * was explicitly set to null. + */ + public Object put(Object key, Object value) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry != null) + return entry.setValue(value); + + modCount++; + if (size >= threshold) + rehash(); + + internalAdd(key, value); + return null; + } + + /** + * Removes the key and the corresponding value from this map. + * @param key the key. This may be null. + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public Object remove(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry == null) + return null; + + modCount++; + internalRemove(entry.getBucket()); + return entry.getValue(); + } + + /** + * Returns a set representation of the entries in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the entries. + */ + public Set entrySet() + { + cleanQueue(); + return theEntrySet; + } + + /** + * Clears all entries from this map. + */ + public void clear() + { + super.clear(); + } + + /** + * Returns true if the map contains at least one key which points to + * the specified object as a value. Note that the result + * may change spontanously, if its key was only weakly reachable. + * @param value the value to search for + * @return true if it is found in the set. + */ + public boolean containsValue(Object value) + { + cleanQueue(); + return super.containsValue(value); + } + + /** + * Returns a set representation of the keys in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the keys. + */ + public Set keySet() + { + cleanQueue(); + return super.keySet(); + } + + /** + * Puts all of the mappings from the given map into this one. If the + * key already exists in this map, its value is replaced. + * @param m the map to copy in + */ + public void putAll(Map m) + { + super.putAll(m); + } + + /** + * Returns a collection representation of the values in this map. This + * collection will not have strong references to the keys, so mappings + * can be silently removed. The returned collection has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a collection representation of the values. + */ + public Collection values() + { + cleanQueue(); + return super.values(); + } +} // class WeakHashMap diff --git a/libjava/classpath/java/util/class-dependencies.conf b/libjava/classpath/java/util/class-dependencies.conf new file mode 100644 index 00000000000..39f96062744 --- /dev/null +++ b/libjava/classpath/java/util/class-dependencies.conf @@ -0,0 +1,78 @@ +# This property file contains dependencies of classes, methods, and +# field on other methods or classes. +# +# Syntax: +# +# <used>: <needed 1> [... <needed N>] +# +# means that when <used> is included, <needed 1> (... <needed N>) must +# be included as well. +# +# <needed X> and <used> are of the form +# +# <class.methodOrField(signature)> +# +# or just +# +# <class> +# +# Within dependencies, variables can be used. A variable is defined as +# follows: +# +# {variable}: value1 value2 ... value<n> +# +# variables can be used on the right side of dependencies as follows: +# +# <used>: com.bla.blu.{variable}.Class.m()V +# +# The use of the variable will expand to <n> dependencies of the form +# +# <used>: com.bla.blu.value1.Class.m()V +# <used>: com.bla.blu.value2.Class.m()V +# ... +# <used>: com.bla.blu.value<n>.Class.m()V +# +# Variables can be redefined when building a system to select the +# required support for features like encodings, protocols, etc. +# +# Hints: +# +# - For methods and fields, the signature is mandatory. For +# specification, please see the Java Virtual Machine Specification by +# SUN. Unlike in the spec, field signatures (types) are in brackets. +# +# - Package names must be separated by '/' (and not '.'). E.g., +# java/lang/Class (this is necessary, because the '.' is used to +# separate method or field names from classes) +# +# - In case <needed> refers to a class, only the class itself will be +# included in the resulting binary, NOT necessarily all its methods +# and fields. If you want to refer to all methods and fields, you can +# write class.* as an abbreviation. +# +# - Abbreviations for packages are also possible: my/package/* means all +# methods and fields of all classes in my/package. +# +# - A line with a trailing '\' continues in the next line. + + +# All calendars supported are loaded via java/util/Calendar.getBundle or +# java/util/GregorianCalendar.getBundle from class +# gnu/java/locale/Calendar_{locale_id} +# +# This introduces a dependency for the localized calendars. To allow an easy +# selection and addition of locales, the library variable {calendar_locales} +# can be set to the set of supported calendar locales. +# + +{calendar_locales}: de en nl + +java/util/Calendar.getBundle(Ljava/util/Locale;)Ljava/util/ResourceBundle;: \ + gnu/java/locale/Calendar.* \ + gnu/java/locale/Calendar_{calendar_locales}.* + +java/util/GregorianCalendar.getBundle(Ljava/util/Locale;)Ljava/util/ResourceBundle;: \ + gnu/java/locale/Calendar.* \ + gnu/java/locale/Calendar_{calendar_locales}.* + +# end of file diff --git a/libjava/classpath/java/util/jar/Attributes.java b/libjava/classpath/java/util/jar/Attributes.java new file mode 100644 index 00000000000..4db2c72e75b --- /dev/null +++ b/libjava/classpath/java/util/jar/Attributes.java @@ -0,0 +1,630 @@ +/* Attributes.java -- Represents attribute name/value pairs from a Manifest + Copyright (C) 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.util.Collection; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * Represents attribute name/value pairs from a Manifest as a Map. + * The names of an attribute are represented by the + * <code>Attributes.Name</code> class and should confirm to the restrictions + * described in that class. Note that the Map interface that Attributes + * implements allows you to put names and values into the attribute that don't + * follow these restriction (and are not really Atrribute.Names, but if you do + * that it might cause undefined behaviour later). + * <p> + * If you use the constants defined in the inner class Name then you can be + * sure that you always access the right attribute names. This makes + * manipulating the Attributes more or less type safe. + * <p> + * Most of the methods are wrappers to implement the Map interface. The really + * useful and often used methods are <code>getValue(Name)</code> and + * <code>getValue(String)</code>. If you actually want to set attributes you + * may want to use the <code>putValue(String, String)</code> method + * (sorry there is no public type safe <code>putValue(Name, String)</code> + * method). + * + * @see java.util.jar.Attributes.Name + * @author Mark Wielaard (mark@klomp.org) + */ +public class Attributes implements Cloneable, Map +{ + + // Fields + + /** + * The map that holds all the attribute name/value pairs. In this + * implementation it is actually a Hashtable, but that can be different in + * other implementations. + */ + protected Map map; + + // Inner class + + /** + * Represents a name of a Manifest Attribute. Defines a couple of well + * know names for the general main attributes, stand alone application + * attributes, applet attributes, extension identification attributes, + * package versioning and sealing attributes, file contents attributes, + * bean objects attribute and signing attributes. See the + * + * <p>The characters of a Name must obey the following restrictions:</p> + * + * <ul> + * <li>Must contain at least one character</li> + * <li>The first character must be alphanumeric (a-z, A-Z, 0-9)</li> + * <li>All other characters must be alphanumeric, a '-' or a '_'</li> + * </ul> + * + * <p>When comparing Names (with <code>equals</code>) all characters are + * converted to lowercase. But you can get the original case sensitive + * string with the <code>toString()</code> method.</p> + * + * <p>Most important attributes have a constant defined in this + * class. Some other attributes used in Manifest files are: + * <ul> + * <li> "Created-By" - General main attribute, tool and version + * that created this Manifest file.</li> + * <li> "Java-Bean" - Bean objects attribute, whether the entry is a Bean. + * Value is either "true" or "false".</li> + * <li> "Magic" - Signing attribute, application specific signing attribute. + * Must be understood by the manifest parser when present to validate the + * jar (entry).</li> + * </ul> + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + public static class Name + { + // General Main Attributes + + /** + * General main attribute - + * the version of this Manifest file. + */ + public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); + + /** + * General main attribute - + * the version of the jar file signature. + */ + public static final Name SIGNATURE_VERSION + = new Name("Signature-Version"); + + /** + * General main attribute - + * (relative) file paths of the libraries/classpaths that the Classes in + * this jar file depend on. Paths are separated by spaces. + */ + public static final Name CLASS_PATH = new Name("Class-Path"); + + /** + * Stand alone application attribute - + * the entry (without the .class ending) that is the main + * class of this jar file. + */ + public static final Name MAIN_CLASS = new Name("Main-Class"); + + /** + * Applet attribute - + * a list of extension libraries that the applet in this + * jar file depends on. + * For every named extension there should be some Attributes in the + * Manifest manifest file with the following Names: + * <ul> + * <li> <extension>-Extension-Name: + * unique name of the extension</li> + * <li> <extension>-Specification-Version: + * minimum specification version</li> + * <li> <extension>-Implementation-Version: + * minimum implementation version</li> + * <li> <extension>-Implementation-Vendor-Id: + * unique id of implementation vendor</li> + * <li> <extension>-Implementation-URL: + * where the latest version of the extension library can be found</li> + * </ul> + */ + public static final Name EXTENSION_LIST = new Name("Extension-List"); + + /** + * Extension identification attribute - + * the name if the extension library contained in the jar. + */ + public static final Name EXTENSION_NAME = new Name("Extension-Name"); + + /** + * Extension identification attribute - + * synonym for <code>EXTENSTION_NAME</code>. + */ + public static final Name EXTENSION_INSTALLATION = EXTENSION_NAME; + + // Package versioning and sealing attributes + + /** + * Package versioning - + * name of extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_TITLE + = new Name("Implementation-Title"); + + /** + * Package versioning - + * version of the extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_VERSION + = new Name("Implementation-Version"); + + /** + * Package versioning - + * name of extension library creator contained in this jar. + */ + public static final Name IMPLEMENTATION_VENDOR + = new Name("Implementation-Vendor"); + + /** + * Package versioning - + * unique id of extension library creator. + */ + public static final Name IMPLEMENTATION_VENDOR_ID + = new Name("Implementation-Vendor-Id"); + + /** + * Package versioning - + * location where this implementation can be downloaded. + */ + public static final Name IMPLEMENTATION_URL + = new Name("Implementation-URL"); + + /** + * Package versioning - + * title of the specification contained in this jar. + */ + public static final Name SPECIFICATION_TITLE + = new Name("Specification-Title"); + + /** + * Package versioning - + * version of the specification contained in this jar. + */ + public static final Name SPECIFICATION_VERSION + = new Name("Specification-Version"); + + /** + * Package versioning - + * organisation that maintains the specification contains in this + * jar. + */ + public static final Name SPECIFICATION_VENDOR + = new Name("Specification-Vendor"); + + /** + * Package sealing - + * whether (all) package(s) is(/are) sealed. Value is either "true" + * or "false". + */ + public static final Name SEALED = new Name("Sealed"); + + /** + * File contents attribute - + * Mime type and subtype for the jar entry. + */ + public static final Name CONTENT_TYPE = new Name("Content-Type"); + + /** The (lowercase) String representation of this Name */ + private final String name; + + /** The original String given to the constructor */ + private final String origName; + + // Constructor + + /** + * Creates a new Name from the given String. + * Throws an IllegalArgumentException if the given String is empty or + * contains any illegal Name characters. + * + * @param name the name of the new Name + * @exception IllegalArgumentException if name isn't a valid String + * representation of a Name + * @exception NullPointerException if name is null + */ + public Name(String name) throws IllegalArgumentException, + NullPointerException + { + // name must not be null + // this will throw a NullPointerException if it is + char chars[] = name.toCharArray(); + + // there must be at least one character + if (chars.length == 0) + throw new + IllegalArgumentException + ("There must be at least one character in a name"); + + // first character must be alphanum + char c = chars[0]; + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) + throw new + IllegalArgumentException("First character must be alphanum"); + + // all other characters must be alphanums, '-' or '_' + for (int i = 1; i < chars.length; i++) + { + c = chars[i]; + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || (c == '-') || (c == '_'))) + throw new + IllegalArgumentException + ("Characters must be alphanums, '-' or '_'"); + } + + // Still here? Then convert to lower case and be done. + // Store the original name for toString(); + this.origName = name; + this.name = name.toLowerCase(); + } + + /** + * Returns the hash code of the (lowercase) String representation of + * this Name. + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * Checks if another object is equal to this Name object. + * Another object is equal to this Name object if it is an instance of + * Name and the (lowercase) string representation of the name is equal. + */ + public boolean equals(Object o) + { + // Quick and dirty check + if (name == o) + return true; + + try + { + // Note that the constructor already converts the strings to + // lowercase. + String otherName = ((Name) o).name; + return name.equals(otherName); + } + catch (ClassCastException cce) + { + return false; + } + catch (NullPointerException npe) + { + return false; + } + } + + /** + * Returns the string representation of this Name as given to the + * constructor (not neccesarily the lower case representation). + */ + public String toString() + { + return origName; + } + } + + // Constructors + + /** + * Creates an empty Attributes map. + */ + public Attributes() + { + map = new Hashtable(); + } + + /** + * Creates an empty Attributes map with the given initial size. + * @param size the initial size of the underlying map + */ + public Attributes(int size) + { + map = new Hashtable(size); + } + + /** + * Creates an Attributes map with the initial values taken from another + * Attributes map. + * @param attr Attributes map to take the initial values from + */ + public Attributes(Attributes attr) + { + map = new Hashtable(attr.map); + } + + // Methods + + /** + * Gets the value of an attribute name given as a String. + * + * @param name a String describing the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(String name) + { + return (String) get(new Name(name)); + } + + /** + * Gets the value of the given attribute name. + * + * @param name the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(Name name) + { + return (String) get(name); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the (case insensitive string) name already exists the value is + * replaced and the old value is returned. + * + * @param name a (case insensitive) String representation of the attribite + * name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + public String putValue(String name, String value) + { + return putValue(new Name(name), value); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the name already exists the value is replaced and the old value + * is returned. + * <p> + * I don't know why there is no public method with this signature. I think + * there should be one. + * + * @param name the attribite name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + String putValue(Name name, String value) + { + return (String) put(name, value); + } + + // Methods from Cloneable interface + + /** + * Return a clone of this attribute map. + */ + public Object clone() + { + return new Attributes(this); + } + + // Methods from Map interface + + /** + * Removes all attributes. + */ + public void clear() + { + map.clear(); + } + + /** + * Checks to see if there is an attribute with the specified name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute to check + * @return true if there is an attribute with the specified name, false + * otherwise + */ + public boolean containsKey(Object attrName) + { + return map.containsKey(attrName); + } + + /** + * Checks to see if there is an attribute name with the specified value. + * + * @param attrValue the value of a attribute to check + * @return true if there is an attribute name with the specified value, + * false otherwise + */ + public boolean containsValue(Object attrValue) + { + return map.containsValue(attrValue); + } + + /** + * Gives a Set of attribute name and values pairs as MapEntries. + * @see java.util.Map.Entry + * @see java.util.Map#entrySet() + * + * @return a set of attribute name value pairs + */ + public Set entrySet() + { + return map.entrySet(); + } + + /** + * Checks to see if two Attributes are equal. The supplied object must be + * a real instance of Attributes and contain the same attribute name/value + * pairs. + * + * @param o another Attribute object which should be checked for equality + * @return true if the object is an instance of Attributes and contains the + * same name/value pairs, false otherwise + */ + public boolean equals(Object o) + { + // quick and dirty check + if (this == o) + return true; + + try + { + return map.equals(((Attributes) o).map); + } + catch (ClassCastException cce) + { + return false; + } + catch (NullPointerException npe) + { + return false; + } + } + + /** + * Gets the value of a specified attribute name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute we want the value of + * @return the value of the specified attribute name or null when there is + * no such attribute name + */ + public Object get(Object attrName) + { + return map.get(attrName); + } + + /** + * Returns the hashcode of the attribute name/value map. + */ + public int hashCode() + { + return map.hashCode(); + } + + /** + * Returns true if there are no attributes set, false otherwise. + */ + public boolean isEmpty() + { + return map.isEmpty(); + } + + /** + * Gives a Set of all the values of defined attribute names. + */ + public Set keySet() + { + return map.keySet(); + } + + /** + * Adds or replaces a attribute name/value pair. + * XXX - What if the name is a string? What if the name is neither a Name + * nor a String? What if the value is not a string? + * + * @param name the name of the attribute + * @param value the (new) value of the attribute + * @return the old value of the attribute or null when there was no old + * attribute with this name + */ + public Object put(Object name, Object value) + { + return map.put(name, value); + } + + /** + * Adds or replaces all attribute name/value pairs from another + * Attributes object to this one. The supplied Map must be an instance of + * Attributes. + * + * @param attr the Attributes object to merge with this one + * @exception ClassCastException if the supplied map is not an instance of + * Attributes + */ + public void putAll(Map attr) + { + if (!(attr instanceof Attributes)) + { + throw new + ClassCastException("Supplied Map is not an instance of Attributes"); + } + map.putAll(attr); + } + + /** + * Remove a attribute name/value pair. + * XXX - What if the name is a String? + * + * @param name the name of the attribute name/value pair to remove + * @return the old value of the attribute or null if the attribute didn't + * exist + */ + public Object remove(Object name) + { + return map.remove(name); + } + + /** + * Returns the number of defined attribute name/value pairs. + */ + public int size() + { + return map.size(); + } + + /** + * Returns all the values of the defined attribute name/value pairs as a + * Collection. + */ + public Collection values() + { + return map.values(); + } +} diff --git a/libjava/classpath/java/util/jar/JarEntry.java b/libjava/classpath/java/util/jar/JarEntry.java new file mode 100644 index 00000000000..722a283bba3 --- /dev/null +++ b/libjava/classpath/java/util/jar/JarEntry.java @@ -0,0 +1,165 @@ +/* JarEntry.java - Represents an entry in a jar file + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.zip.ZipEntry; + +/** + * Extension to a ZipEntry that contains manifest attributes and certificates. + * Both the Atrributes and the Certificates can be null when not set. + * Note that the <code>getCertificates()</code> method only returns a + * valid value after all of the data of the entry has been read. + * <p> + * There are no public methods to set the attributes or certificate of an + * Entru. Only JarEntries created by the classes in <code>java.util.jar</code> + * will have these properties set. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarEntry extends ZipEntry +{ + // (Package local) fields + + Attributes attr; + Certificate certs[]; + + // Constructors + + /** + * Creates a new JarEntry with the specified name and no attributes or + * or certificates. Calls <code>super(name)</code> so all other (zip)entry + * fields are null or -1. + * + * @param name the name of the new jar entry + * @exception NullPointerException when the supplied name is null + * @exception IllegalArgumentException when the supplied name is longer + * than 65535 bytes + */ + public JarEntry(String name) throws NullPointerException, + IllegalArgumentException + { + super(name); + attr = null; + certs = null; + } + + /** + * Creates a new JarEntry with the specified ZipEntry as template for + * all properties of the entry. Both attributes and certificates will be + * null. + * + * @param entry the ZipEntry whose fields should be copied + */ + public JarEntry(ZipEntry entry) + { + super(entry); + attr = null; + certs = null; + } + + /** + * Creates a new JarEntry with the specified JarEntry as template for + * all properties of the entry. + * + * @param entry the jarEntry whose fields should be copied + */ + public JarEntry(JarEntry entry) + { + super(entry); + try + { + attr = entry.getAttributes(); + } + catch (IOException _) + { + } + certs = entry.getCertificates(); + } + + // Methods + + /** + * Returns a copy of the Attributes set for this entry. + * When no Attributes are set in the manifest null is returned. + * + * @return a copy of the Attributes set for this entry + * @exception IOException This will never be thrown. It is here for + * binary compatibility. + */ + public Attributes getAttributes() throws IOException + { + if (attr != null) + { + return (Attributes) attr.clone(); + } + else + { + return null; + } + } + + /** + * Returns a copy of the certificates set for this entry. + * When no certificates are set or when not all data of this entry has + * been read null is returned. + * <p> + * To make sure that this call returns a valid value you must read all + * data from the JarInputStream for this entry. + * When you don't need the data for an entry but want to know the + * certificates that are set for the entry then you can skip all data by + * calling <code>skip(entry.getSize())</code> on the JarInputStream for + * the entry. + * + * @return a copy of the certificates set for this entry + */ + public Certificate[] getCertificates() + { + if (certs != null) + { + return (Certificate[])certs.clone(); + } + else + { + return null; + } + } +} diff --git a/libjava/classpath/java/util/jar/JarException.java b/libjava/classpath/java/util/jar/JarException.java new file mode 100644 index 00000000000..d6f0634fe71 --- /dev/null +++ b/libjava/classpath/java/util/jar/JarException.java @@ -0,0 +1,77 @@ +/* JarException.java -- thrown to indicate an problem with a jar file + Copyright (C) 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.util.zip.ZipException; + +/** + * This exception is thrown to indicate an problem with a jar file. + * Note that none of the methods in the java.util.jar package actually declare + * to throw this exception, most just declare that they throw an IOException + * which is super class of JarException. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.2 + */ +public class JarException extends ZipException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7159778400963954473L; + + /** + * Create a new JarException without a descriptive error message. + */ + public JarException() + { + } + + /** + * Create a new JarException with a descriptive error message indicating + * what went wrong. This message can later be retrieved by calling the + * <code>getMessage()</code> method. + * + * @param message The descriptive error message + * @see #getMessage() + */ + public JarException(String message) + { + super(message); + } +} diff --git a/libjava/classpath/java/util/jar/JarFile.java b/libjava/classpath/java/util/jar/JarFile.java new file mode 100644 index 00000000000..a9ad21ecc1d --- /dev/null +++ b/libjava/classpath/java/util/jar/JarFile.java @@ -0,0 +1,1059 @@ +/* JarFile.java - Representation of a jar file + Copyright (C) 2000, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.jar; + +import gnu.java.io.Base64InputStream; +import gnu.java.security.OID; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Representation of a jar file. + * <p> + * Note that this class is not a subclass of java.io.File but a subclass of + * java.util.zip.ZipFile and you can only read JarFiles with it (although + * there are constructors that take a File object). + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry + * verification code. + */ +public class JarFile extends ZipFile +{ + // Fields + + /** The name of the manifest entry: META-INF/MANIFEST.MF */ + public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + /** The META-INF directory entry. */ + private static final String META_INF = "META-INF/"; + + /** The suffix for PKCS7 DSA signature entries. */ + private static final String PKCS7_DSA_SUFFIX = ".DSA"; + + /** The suffix for PKCS7 RSA signature entries. */ + private static final String PKCS7_RSA_SUFFIX = ".RSA"; + + /** The suffix for digest attributes. */ + private static final String DIGEST_KEY_SUFFIX = "-Digest"; + + /** The suffix for signature files. */ + private static final String SF_SUFFIX = ".SF"; + + // Signature OIDs. + private static final OID MD2_OID = new OID("1.2.840.113549.2.2"); + private static final OID MD4_OID = new OID("1.2.840.113549.2.4"); + private static final OID MD5_OID = new OID("1.2.840.113549.2.5"); + private static final OID SHA1_OID = new OID("1.3.14.3.2.26"); + private static final OID DSA_ENCRYPTION_OID = new OID("1.2.840.10040.4.1"); + private static final OID RSA_ENCRYPTION_OID = new OID("1.2.840.113549.1.1.1"); + + /** + * The manifest of this file, if any, otherwise null. + * Read when first needed. + */ + private Manifest manifest; + + /** Whether to verify the manifest and all entries. */ + boolean verify; + + /** Whether the has already been loaded. */ + private boolean manifestRead = false; + + /** Whether the signature files have been loaded. */ + boolean signaturesRead = false; + + /** + * A map between entry names and booleans, signaling whether or + * not that entry has been verified. + * Only be accessed with lock on this JarFile*/ + HashMap verified = new HashMap(); + + /** + * A mapping from entry name to certificates, if any. + * Only accessed with lock on this JarFile. + */ + HashMap entryCerts; + + static boolean DEBUG = false; + static void debug(Object msg) + { + System.err.print(JarFile.class.getName()); + System.err.print(" >>> "); + System.err.println(msg); + } + + // Constructors + + /** + * Creates a new JarFile. All jar entries are verified (when a Manifest file + * for this JarFile exists). You need to actually open and read the complete + * jar entry (with <code>getInputStream()</code>) to check its signature. + * + * @param fileName the name of the file to open + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName) throws FileNotFoundException, IOException + { + this(fileName, true); + } + + /** + * Creates a new JarFile. If verify is true then all jar entries are + * verified (when a Manifest file for this JarFile exists). You need to + * actually open and read the complete jar entry + * (with <code>getInputStream()</code>) to check its signature. + * + * @param fileName the name of the file to open + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName, boolean verify) throws + FileNotFoundException, IOException + { + super(fileName); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + /** + * Creates a new JarFile. All jar entries are verified (when a Manifest file + * for this JarFile exists). You need to actually open and read the complete + * jar entry (with <code>getInputStream()</code>) to check its signature. + * + * @param file the file to open as a jar file + * @exception FileNotFoundException if the file does not exits + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file) throws FileNotFoundException, IOException + { + this(file, true); + } + + /** + * Creates a new JarFile. If verify is true then all jar entries are + * verified (when a Manifest file for this JarFile exists). You need to + * actually open and read the complete jar entry + * (with <code>getInputStream()</code>) to check its signature. + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if file does not exist + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file, boolean verify) throws FileNotFoundException, + IOException + { + super(file); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + /** + * Creates a new JarFile with the indicated mode. If verify is true then + * all jar entries are verified (when a Manifest file for this JarFile + * exists). You need to actually open and read the complete jar entry + * (with <code>getInputStream()</code>) to check its signature. + * manifest and if the manifest exists and verify is true verfies it. + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @param mode either ZipFile.OPEN_READ or + * (ZipFile.OPEN_READ | ZipFile.OPEN_DELETE) + * @exception FileNotFoundException if the file does not exist + * @exception IOException if another IO exception occurs while reading + * @exception IllegalArgumentException when given an illegal mode + * + * @since 1.3 + */ + public JarFile(File file, boolean verify, int mode) throws + FileNotFoundException, IOException, IllegalArgumentException + { + super(file, mode); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + // Methods + + /** + * XXX - should verify the manifest file + */ + private void verify() + { + // only check if manifest is not null + if (manifest == null) + { + verify = false; + return; + } + + verify = true; + // XXX - verify manifest + } + + /** + * Parses and returns the manifest if it exists, otherwise returns null. + */ + private Manifest readManifest() + { + try + { + ZipEntry manEntry = super.getEntry(MANIFEST_NAME); + if (manEntry != null) + { + InputStream in = super.getInputStream(manEntry); + manifestRead = true; + return new Manifest(in); + } + else + { + manifestRead = true; + return null; + } + } + catch (IOException ioe) + { + manifestRead = true; + return null; + } + } + + /** + * Returns a enumeration of all the entries in the JarFile. + * Note that also the Jar META-INF entries are returned. + * + * @exception IllegalStateException when the JarFile is already closed + */ + public Enumeration entries() throws IllegalStateException + { + return new JarEnumeration(super.entries(), this); + } + + /** + * Wraps a given Zip Entries Enumeration. For every zip entry a + * JarEntry is created and the corresponding Attributes are looked up. + */ + private static class JarEnumeration implements Enumeration + { + + private final Enumeration entries; + private final JarFile jarfile; + + JarEnumeration(Enumeration e, JarFile f) + { + entries = e; + jarfile = f; + } + + public boolean hasMoreElements() + { + return entries.hasMoreElements(); + } + + public Object nextElement() + { + ZipEntry zip = (ZipEntry) entries.nextElement(); + JarEntry jar = new JarEntry(zip); + Manifest manifest; + try + { + manifest = jarfile.getManifest(); + } + catch (IOException ioe) + { + manifest = null; + } + + if (manifest != null) + { + jar.attr = manifest.getAttributes(jar.getName()); + } + + synchronized(jarfile) + { + if (!jarfile.signaturesRead) + try + { + jarfile.readSignatures(); + } + catch (IOException ioe) + { + if (JarFile.DEBUG) + { + JarFile.debug(ioe); + ioe.printStackTrace(); + } + jarfile.signaturesRead = true; // fudge it. + } + + // Include the certificates only if we have asserted that the + // signatures are valid. This means the certificates will not be + // available if the entry hasn't been read yet. + if (jarfile.entryCerts != null + && jarfile.verified.get(zip.getName()) == Boolean.TRUE) + { + Set certs = (Set) jarfile.entryCerts.get(jar.getName()); + if (certs != null) + jar.certs = (Certificate[]) + certs.toArray(new Certificate[certs.size()]); + } + } + return jar; + } + } + + /** + * XXX + * It actually returns a JarEntry not a zipEntry + * @param name XXX + */ + public synchronized ZipEntry getEntry(String name) + { + ZipEntry entry = super.getEntry(name); + if (entry != null) + { + JarEntry jarEntry = new JarEntry(entry); + Manifest manifest; + try + { + manifest = getManifest(); + } + catch (IOException ioe) + { + manifest = null; + } + + if (manifest != null) + { + jarEntry.attr = manifest.getAttributes(name); + } + + if (!signaturesRead) + try + { + readSignatures(); + } + catch (IOException ioe) + { + if (DEBUG) + { + debug(ioe); + ioe.printStackTrace(); + } + signaturesRead = true; + } + // See the comments in the JarEnumeration for why we do this + // check. + if (DEBUG) + debug("entryCerts=" + entryCerts + " verified " + name + + " ? " + verified.get(name)); + if (entryCerts != null && verified.get(name) == Boolean.TRUE) + { + Set certs = (Set) entryCerts.get(name); + if (certs != null) + jarEntry.certs = (Certificate[]) + certs.toArray(new Certificate[certs.size()]); + } + return jarEntry; + } + return null; + } + + /** + * Returns an input stream for the given entry. If configured to + * verify entries, the input stream returned will verify them while + * the stream is read, but only on the first time. + * + * @param entry The entry to get the input stream for. + * @exception ZipException XXX + * @exception IOException XXX + */ + public synchronized InputStream getInputStream(ZipEntry entry) throws + ZipException, IOException + { + // If we haven't verified the hash, do it now. + if (!verified.containsKey(entry.getName()) && verify) + { + if (DEBUG) + debug("reading and verifying " + entry); + return new EntryInputStream(entry, super.getInputStream(entry), this); + } + else + { + if (DEBUG) + debug("reading already verified entry " + entry); + if (verify && verified.get(entry.getName()) == Boolean.FALSE) + throw new ZipException("digest for " + entry + " is invalid"); + return super.getInputStream(entry); + } + } + + /** + * Returns the JarEntry that belongs to the name if such an entry + * exists in the JarFile. Returns null otherwise + * Convenience method that just casts the result from <code>getEntry</code> + * to a JarEntry. + * + * @param name the jar entry name to look up + * @return the JarEntry if it exists, null otherwise + */ + public JarEntry getJarEntry(String name) + { + return (JarEntry) getEntry(name); + } + + /** + * Returns the manifest for this JarFile or null when the JarFile does not + * contain a manifest file. + */ + public synchronized Manifest getManifest() throws IOException + { + if (!manifestRead) + manifest = readManifest(); + + return manifest; + } + + // Only called with lock on this JarFile. + // Package private for use in inner classes. + void readSignatures() throws IOException + { + Map pkcs7Dsa = new HashMap(); + Map pkcs7Rsa = new HashMap(); + Map sigFiles = new HashMap(); + + // Phase 1: Read all signature files. These contain the user + // certificates as well as the signatures themselves. + for (Enumeration e = super.entries(); e.hasMoreElements(); ) + { + ZipEntry ze = (ZipEntry) e.nextElement(); + String name = ze.getName(); + if (name.startsWith(META_INF)) + { + String alias = name.substring(META_INF.length()); + if (alias.lastIndexOf('.') >= 0) + alias = alias.substring(0, alias.lastIndexOf('.')); + + if (name.endsWith(PKCS7_DSA_SUFFIX) || name.endsWith(PKCS7_RSA_SUFFIX)) + { + if (DEBUG) + debug("reading PKCS7 info from " + name + ", alias=" + alias); + PKCS7SignedData sig = null; + try + { + sig = new PKCS7SignedData(super.getInputStream(ze)); + } + catch (CertificateException ce) + { + IOException ioe = new IOException("certificate parsing error"); + ioe.initCause(ce); + throw ioe; + } + catch (CRLException crle) + { + IOException ioe = new IOException("CRL parsing error"); + ioe.initCause(crle); + throw ioe; + } + if (name.endsWith(PKCS7_DSA_SUFFIX)) + pkcs7Dsa.put(alias, sig); + else if (name.endsWith(PKCS7_RSA_SUFFIX)) + pkcs7Rsa.put(alias, sig); + } + else if (name.endsWith(SF_SUFFIX)) + { + if (DEBUG) + debug("reading signature file for " + alias + ": " + name); + Manifest sf = new Manifest(super.getInputStream(ze)); + sigFiles.put(alias, sf); + if (DEBUG) + debug("result: " + sf); + } + } + } + + // Phase 2: verify the signatures on any signature files. + Set validCerts = new HashSet(); + Map entryCerts = new HashMap(); + for (Iterator it = sigFiles.entrySet().iterator(); it.hasNext(); ) + { + int valid = 0; + Map.Entry e = (Map.Entry) it.next(); + String alias = (String) e.getKey(); + + PKCS7SignedData sig = (PKCS7SignedData) pkcs7Dsa.get(alias); + if (sig != null) + { + Certificate[] certs = sig.getCertificates(); + Set signerInfos = sig.getSignerInfos(); + for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); ) + verify(certs, (SignerInfo) it2.next(), alias, validCerts); + } + + sig = (PKCS7SignedData) pkcs7Rsa.get(alias); + if (sig != null) + { + Certificate[] certs = sig.getCertificates(); + Set signerInfos = sig.getSignerInfos(); + for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); ) + verify(certs, (SignerInfo) it2.next(), alias, validCerts); + } + + // It isn't a signature for anything. Punt it. + if (validCerts.isEmpty()) + { + it.remove(); + continue; + } + + entryCerts.put(e.getValue(), new HashSet(validCerts)); + validCerts.clear(); + } + + // Phase 3: verify the signature file signatures against the manifest, + // mapping the entry name to the target certificates. + this.entryCerts = new HashMap(); + for (Iterator it = entryCerts.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + Manifest sigfile = (Manifest) e.getKey(); + Map entries = sigfile.getEntries(); + Set certificates = (Set) e.getValue(); + + for (Iterator it2 = entries.entrySet().iterator(); it2.hasNext(); ) + { + Map.Entry e2 = (Map.Entry) it2.next(); + String entryname = String.valueOf(e2.getKey()); + Attributes attr = (Attributes) e2.getValue(); + if (verifyHashes(entryname, attr)) + { + if (DEBUG) + debug("entry " + entryname + " has certificates " + certificates); + Set s = (Set) this.entryCerts.get(entryname); + if (s != null) + s.addAll(certificates); + else + this.entryCerts.put(entryname, new HashSet(certificates)); + } + } + } + + signaturesRead = true; + } + + /** + * Tell if the given signer info is over the given alias's signature file, + * given one of the certificates specified. + */ + private void verify(Certificate[] certs, SignerInfo signerInfo, + String alias, Set validCerts) + { + Signature sig = null; + try + { + OID alg = signerInfo.getDigestEncryptionAlgorithmId(); + if (alg.equals(DSA_ENCRYPTION_OID)) + { + if (!signerInfo.getDigestAlgorithmId().equals(SHA1_OID)) + return; + sig = Signature.getInstance("SHA1withDSA"); + } + else if (alg.equals(RSA_ENCRYPTION_OID)) + { + OID hash = signerInfo.getDigestAlgorithmId(); + if (hash.equals(MD2_OID)) + sig = Signature.getInstance("md2WithRsaEncryption"); + else if (hash.equals(MD4_OID)) + sig = Signature.getInstance("md4WithRsaEncryption"); + else if (hash.equals(MD5_OID)) + sig = Signature.getInstance("md5WithRsaEncryption"); + else if (hash.equals(SHA1_OID)) + sig = Signature.getInstance("sha1WithRsaEncryption"); + else + return; + } + else + { + if (DEBUG) + debug("unsupported signature algorithm: " + alg); + return; + } + } + catch (NoSuchAlgorithmException nsae) + { + if (DEBUG) + { + debug(nsae); + nsae.printStackTrace(); + } + return; + } + ZipEntry sigFileEntry = super.getEntry(META_INF + alias + SF_SUFFIX); + if (sigFileEntry == null) + return; + for (int i = 0; i < certs.length; i++) + { + if (!(certs[i] instanceof X509Certificate)) + continue; + X509Certificate cert = (X509Certificate) certs[i]; + if (!cert.getIssuerX500Principal().equals(signerInfo.getIssuer()) || + !cert.getSerialNumber().equals(signerInfo.getSerialNumber())) + continue; + try + { + sig.initVerify(cert.getPublicKey()); + InputStream in = super.getInputStream(sigFileEntry); + if (in == null) + continue; + byte[] buf = new byte[1024]; + int len = 0; + while ((len = in.read(buf)) != -1) + sig.update(buf, 0, len); + if (sig.verify(signerInfo.getEncryptedDigest())) + { + if (DEBUG) + debug("signature for " + cert.getSubjectDN() + " is good"); + validCerts.add(cert); + } + } + catch (IOException ioe) + { + continue; + } + catch (InvalidKeyException ike) + { + continue; + } + catch (SignatureException se) + { + continue; + } + } + } + + /** + * Verifies that the digest(s) in a signature file were, in fact, made + * over the manifest entry for ENTRY. + * + * @param entry The entry name. + * @param attr The attributes from the signature file to verify. + */ + private boolean verifyHashes(String entry, Attributes attr) + { + int verified = 0; + + // The bytes for ENTRY's manifest entry, which are signed in the + // signature file. + byte[] entryBytes = null; + try + { + ZipEntry e = super.getEntry(entry); + if (e == null) + { + if (DEBUG) + debug("verifyHashes: no entry '" + entry + "'"); + return false; + } + entryBytes = readManifestEntry(e); + } + catch (IOException ioe) + { + if (DEBUG) + { + debug(ioe); + ioe.printStackTrace(); + } + return false; + } + + for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + String key = String.valueOf(e.getKey()); + if (!key.endsWith(DIGEST_KEY_SUFFIX)) + continue; + String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length()); + try + { + byte[] hash = Base64InputStream.decode((String) e.getValue()); + MessageDigest md = MessageDigest.getInstance(alg); + md.update(entryBytes); + byte[] hash2 = md.digest(); + if (DEBUG) + debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm() + + " expect=" + new java.math.BigInteger(hash).toString(16) + + " comp=" + new java.math.BigInteger(hash2).toString(16)); + if (!Arrays.equals(hash, hash2)) + return false; + verified++; + } + catch (IOException ioe) + { + if (DEBUG) + { + debug(ioe); + ioe.printStackTrace(); + } + return false; + } + catch (NoSuchAlgorithmException nsae) + { + if (DEBUG) + { + debug(nsae); + nsae.printStackTrace(); + } + return false; + } + } + + // We have to find at least one valid digest. + return verified > 0; + } + + /** + * Read the raw bytes that comprise a manifest entry. We can't use the + * Manifest object itself, because that loses information (such as line + * endings, and order of entries). + */ + private byte[] readManifestEntry(ZipEntry entry) throws IOException + { + InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] target = ("Name: " + entry.getName()).getBytes(); + int t = 0, c, prev = -1, state = 0, l = -1; + + while ((c = in.read()) != -1) + { +// if (DEBUG) +// debug("read " +// + (c == '\n' ? "\\n" : (c == '\r' ? "\\r" : String.valueOf((char) c))) +// + " state=" + state + " prev=" +// + (prev == '\n' ? "\\n" : (prev == '\r' ? "\\r" : String.valueOf((char) prev))) +// + " t=" + t + (t < target.length ? (" target[t]=" + (char) target[t]) : "") +// + " l=" + l); + switch (state) + { + + // Step 1: read until we find the "target" bytes: the start + // of the entry we need to read. + case 0: + if (((byte) c) != target[t]) + t = 0; + else + { + t++; + if (t == target.length) + { + out.write(target); + state = 1; + } + } + break; + + // Step 2: assert that there is a newline character after + // the "target" bytes. + case 1: + if (c != '\n' && c != '\r') + { + out.reset(); + t = 0; + state = 0; + } + else + { + out.write(c); + state = 2; + } + break; + + // Step 3: read this whole entry, until we reach an empty + // line. + case 2: + if (c == '\n') + { + out.write(c); + // NL always terminates a line. + if (l == 0 || (l == 1 && prev == '\r')) + return out.toByteArray(); + l = 0; + } + else + { + // Here we see a blank line terminated by a CR, + // followed by the next entry. Technically, `c' should + // always be 'N' at this point. + if (l == 1 && prev == '\r') + return out.toByteArray(); + out.write(c); + l++; + } + prev = c; + break; + + default: + throw new RuntimeException("this statement should be unreachable"); + } + } + + // The last entry, with a single CR terminating the line. + if (state == 2 && prev == '\r' && l == 0) + return out.toByteArray(); + + // We should not reach this point, we didn't find the entry (or, possibly, + // it is the last entry and is malformed). + throw new IOException("could not find " + entry + " in manifest"); + } + + /** + * A utility class that verifies jar entries as they are read. + */ + private static class EntryInputStream extends FilterInputStream + { + private final JarFile jarfile; + private final long length; + private long pos; + private final ZipEntry entry; + private final byte[][] hashes; + private final MessageDigest[] md; + private boolean checked; + + EntryInputStream(final ZipEntry entry, + final InputStream in, + final JarFile jar) + throws IOException + { + super(in); + this.entry = entry; + this.jarfile = jar; + + length = entry.getSize(); + pos = 0; + checked = false; + + Attributes attr; + Manifest manifest = jarfile.getManifest(); + if (manifest != null) + attr = manifest.getAttributes(entry.getName()); + else + attr = null; + if (DEBUG) + debug("verifying entry " + entry + " attr=" + attr); + if (attr == null) + { + hashes = new byte[0][]; + md = new MessageDigest[0]; + } + else + { + List hashes = new LinkedList(); + List md = new LinkedList(); + for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + String key = String.valueOf(e.getKey()); + if (key == null) + continue; + if (!key.endsWith(DIGEST_KEY_SUFFIX)) + continue; + hashes.add(Base64InputStream.decode((String) e.getValue())); + try + { + md.add(MessageDigest.getInstance + (key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length()))); + } + catch (NoSuchAlgorithmException nsae) + { + IOException ioe = new IOException("no such message digest: " + key); + ioe.initCause(nsae); + throw ioe; + } + } + if (DEBUG) + debug("digests=" + md); + this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]); + this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]); + } + } + + public boolean markSupported() + { + return false; + } + + public void mark(int readLimit) + { + } + + public void reset() + { + } + + public int read() throws IOException + { + int b = super.read(); + if (b == -1) + { + eof(); + return -1; + } + for (int i = 0; i < md.length; i++) + md[i].update((byte) b); + pos++; + if (length > 0 && pos >= length) + eof(); + return b; + } + + public int read(byte[] buf, int off, int len) throws IOException + { + int count = super.read(buf, off, (int) Math.min(len, (length != 0 + ? length - pos + : Integer.MAX_VALUE))); + if (count == -1 || (length > 0 && pos >= length)) + { + eof(); + return -1; + } + for (int i = 0; i < md.length; i++) + md[i].update(buf, off, count); + pos += count; + if (length != 0 && pos >= length) + eof(); + return count; + } + + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + public long skip(long bytes) throws IOException + { + byte[] b = new byte[1024]; + long amount = 0; + while (amount < bytes) + { + int l = read(b, 0, (int) Math.min(b.length, bytes - amount)); + if (l == -1) + break; + amount += l; + } + return amount; + } + + private void eof() throws IOException + { + if (checked) + return; + checked = true; + for (int i = 0; i < md.length; i++) + { + byte[] hash = md[i].digest(); + if (DEBUG) + debug("verifying " + md[i].getAlgorithm() + " expect=" + + new java.math.BigInteger(hashes[i]).toString(16) + + " comp=" + new java.math.BigInteger(hash).toString(16)); + if (!Arrays.equals(hash, hashes[i])) + { + synchronized(jarfile) + { + if (DEBUG) + debug(entry + " could NOT be verified"); + jarfile.verified.put(entry.getName(), Boolean.FALSE); + } + return; + // XXX ??? what do we do here? + // throw new ZipException("message digest mismatch"); + } + } + + synchronized(jarfile) + { + if (DEBUG) + debug(entry + " has been VERIFIED"); + jarfile.verified.put(entry.getName(), Boolean.TRUE); + } + } + } +} diff --git a/libjava/classpath/java/util/jar/JarInputStream.java b/libjava/classpath/java/util/jar/JarInputStream.java new file mode 100644 index 00000000000..1788af6add5 --- /dev/null +++ b/libjava/classpath/java/util/jar/JarInputStream.java @@ -0,0 +1,200 @@ +/* JarInputStream.java - InputStream for reading jar files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * InputStream for reading jar files. + * XXX - verification of the signatures in the Manifest file is not yet + * implemented. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarInputStream extends ZipInputStream +{ + // Fields + + /** The manifest for this file or null when there was no manifest. */ + private Manifest manifest; + + /** The first real JarEntry for this file. Used by readManifest() to store + an entry that isn't the manifest but that should be returned by + getNextEntry next time it is called. Null when no firstEntry was read + while searching for the manifest entry, or when it has already been + returned by getNextEntry(). */ + private JarEntry firstEntry; + + // Constructors + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present the JarInputStream tries to verify all + * the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in) throws IOException + { + this(in, true); + } + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present and verify is true, the JarInputStream + * tries to verify all the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @param verify whether or not to verify the manifest entries + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in, boolean verify) throws IOException + { + super(in); + readManifest(verify); + } + + // Methods + + /** + * Set the manifest if found. Skips all entries that start with "META-INF/" + * + * @param verify when true (and a Manifest is found) checks the Manifest, + * when false no check is performed + * @exception IOException if an error occurs while reading + */ + private void readManifest(boolean verify) throws IOException + { + firstEntry = (JarEntry) super.getNextEntry(); + while ((firstEntry != null) && + firstEntry.getName().startsWith("META-INF/")) + { + if (firstEntry.getName().equals(JarFile.MANIFEST_NAME)) + { + manifest = new Manifest(this); + } + firstEntry = (JarEntry) super.getNextEntry(); + } + + if (verify) + { + // XXX + } + } + + /** + * Creates a JarEntry for a particular name and consults the manifest + * for the Attributes of the entry. + * Used by <code>ZipEntry.getNextEntry()</code> + * + * @param name the name of the new entry + */ + protected ZipEntry createZipEntry(String name) + { + ZipEntry zipEntry = super.createZipEntry(name); + JarEntry jarEntry = new JarEntry(zipEntry); + if (manifest != null) + { + jarEntry.attr = manifest.getAttributes(name); + } + return jarEntry; + } + + /** + * Returns the Manifest for the jar file or null if there was no Manifest. + */ + public Manifest getManifest() + { + return manifest; + } + + /** + * Returns the next entry or null when there are no more entries. + * Does actually return a JarEntry, if you don't want to cast it yourself + * use <code>getNextJarEntry()</code>. Does not return any entries found + * at the beginning of the ZipFile that are special + * (those that start with "META-INF/"). + * + * @exception IOException if an IO error occurs when reading the entry + */ + public ZipEntry getNextEntry() throws IOException + { + ZipEntry entry; + if (firstEntry != null) + { + entry = firstEntry; + firstEntry = null; + } + else + { + entry = super.getNextEntry(); + } + return entry; + } + + /** + * Returns the next jar entry or null when there are no more entries. + * + * @exception IOException if an IO error occurs when reading the entry + */ + public JarEntry getNextJarEntry() throws IOException + { + return (JarEntry) getNextEntry(); + } + + /** + * XXX + * + * @param buf XXX + * @param off XXX + * @param len XXX + * @return XXX + * @exception IOException XXX + */ + public int read(byte[]buf, int off, int len) throws IOException + { + // XXX if (verify) {} + return super.read(buf, off, len); + } +} diff --git a/libjava/classpath/java/util/jar/JarOutputStream.java b/libjava/classpath/java/util/jar/JarOutputStream.java new file mode 100644 index 00000000000..2c8c2f08d8f --- /dev/null +++ b/libjava/classpath/java/util/jar/JarOutputStream.java @@ -0,0 +1,113 @@ +/* JarOutputStream.java - OutputStream for writing jar files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * OutputStream for writing jar files. + * A special ZipOutputStream that can take JarEntries and can have a optional + * Manifest as first entry. + * + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarOutputStream extends ZipOutputStream +{ + // Constructors + + /** + * Creates a new JarOutputStream without a manifest entry. + * + * @param out the stream to create the new jar on + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out) throws IOException + { + this(out, null); + } + + /** + * Creates a new JarOutputStream with a manifest entry. + * The manifest will be the first entry in the jar. + * + * @param out the stream to create the new jar on + * @param man the manifest that should be put in the jar file or null + * for no manifest entry + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out, Manifest man) throws IOException + { + super(out); + if (man != null) + writeManifest(man); + } + + // Methods + + /** + * Writes the manifest to a new JarEntry in this JarOutputStream with as + * name JarFile.MANIFEST_NAME. + * + * @param manifest the non null manifest to be written + * @exception IOException if something unexpected happend + */ + private void writeManifest(Manifest manifest) throws IOException + { + // Create a new Jar Entry for the Manifest + JarEntry entry = new JarEntry(JarFile.MANIFEST_NAME); + putNextEntry(entry); + manifest.write(this); + closeEntry(); + } + + /** + * Prepares the JarOutputStream for writing the next entry. + * This implementation just calls <code>super.putNextEntre()</code>. + * + * @param entry The information for the next entry + * @exception IOException when some unexpected I/O exception occurred + */ + public void putNextEntry(ZipEntry entry) throws IOException + { + super.putNextEntry(entry); // XXX + } +} diff --git a/libjava/classpath/java/util/jar/Manifest.java b/libjava/classpath/java/util/jar/Manifest.java new file mode 100644 index 00000000000..fdc76ff97ee --- /dev/null +++ b/libjava/classpath/java/util/jar/Manifest.java @@ -0,0 +1,472 @@ +/* Manifest.java -- Reads, writes and manipulaties jar manifest files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +/** + * Reads, writes and manipulaties jar manifest files. + * XXX + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ +public class Manifest implements Cloneable +{ + // Fields + + /** The main attributes of the manifest (jar file). */ + private final Attributes mainAttr; + + /** A map of atrributes for all entries described in this Manifest. */ + private final Map entries; + + // Constructors + + /** + * Creates a new empty Manifest. + */ + public Manifest() + { + mainAttr = new Attributes(); + entries = new Hashtable(); + } + + /** + * Creates a Manifest from the supplied input stream. + * + * @see read(Inputstream) + * @see write(OutputStream) + * + * @param InputStream the input stream to read the manifest from + * @exception IOException when an i/o exception occurs or the input stream + * does not describe a valid manifest + */ + public Manifest(InputStream in) throws IOException + { + this(); + read(in); + } + + /** + * Creates a Manifest from another Manifest. + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. + * + * @see clone() + * @param man the Manifest to copy from + */ + public Manifest(Manifest man) + { + mainAttr = new Attributes(man.getMainAttributes()); + entries = new Hashtable(man.getEntries()); + } + + // Methods + + /** + * Gets the main attributes of this Manifest. + */ + public Attributes getMainAttributes() + { + return mainAttr; + } + + /** + * Gets a map of entry Strings to Attributes for all the entries described + * in this manifest. Adding, changing or removing from this entries map + * changes the entries of this manifest. + */ + public Map getEntries() + { + return entries; + } + + /** + * Returns the Attributes associated with the Entry. + * <p> + * Implemented as: + * <code>return (Attributes)getEntries().get(entryName)</code> + * + * @param entryName the name of the entry to look up + * @return the attributes associated with the entry or null when none + */ + public Attributes getAttributes(String entryName) + { + return (Attributes) getEntries().get(entryName); + } + + /** + * Clears the main attributes and removes all the entries from the + * manifest. + */ + public void clear() + { + mainAttr.clear(); + entries.clear(); + } + + /** + * XXX + */ + public void read(InputStream in) throws IOException + { + BufferedReader br = + new BufferedReader(new InputStreamReader(in, "8859_1")); + read_main_section(getMainAttributes(), br); + read_individual_sections(getEntries(), br); + } + + // Private Static methods for reading the Manifest file from BufferedReader + + private static void read_main_section(Attributes attr, + BufferedReader br) throws IOException + { + // According to the spec we should actually call read_version_info() here. + read_attributes(attr, br); + // Explicitly set Manifest-Version attribute if not set in Main + // attributes of Manifest. + if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null) + attr.putValue(Attributes.Name.MANIFEST_VERSION, "0.0"); + } + + /** + * Pedantic method that requires the next attribute in the Manifest to be + * the "Manifest-Version". This follows the Manifest spec closely but + * reject some jar Manifest files out in the wild. + */ + private static void read_version_info(Attributes attr, + BufferedReader br) throws IOException + { + String version_header = Attributes.Name.MANIFEST_VERSION.toString(); + try + { + String value = expect_header(version_header, br); + attr.putValue(Attributes.Name.MANIFEST_VERSION, value); + } + catch (IOException ioe) + { + throw new JarException("Manifest should start with a " + + version_header + ": " + ioe.getMessage()); + } + } + + private static String expect_header(String header, BufferedReader br) + throws IOException + { + String s = br.readLine(); + if (s == null) + { + throw new JarException("unexpected end of file"); + } + return expect_header(header, br, s); + } + + private static String expect_header(String header, BufferedReader br, + String s) throws IOException + { + try + { + String name = s.substring(0, header.length() + 1); + if (name.equalsIgnoreCase(header + ":")) + { + String value_start = s.substring(header.length() + 2); + return read_header_value(value_start, br); + } + } + catch (IndexOutOfBoundsException iobe) + { + } + // If we arrive here, something went wrong + throw new JarException("unexpected '" + s + "'"); + } + + private static String read_header_value(String s, BufferedReader br) + throws IOException + { + boolean try_next = true; + while (try_next) + { + // Lets see if there is something on the next line + br.mark(1); + if (br.read() == ' ') + { + s += br.readLine(); + } + else + { + br.reset(); + try_next = false; + } + } + return s; + } + + private static void read_attributes(Attributes attr, + BufferedReader br) throws IOException + { + String s = br.readLine(); + while (s != null && (!s.equals(""))) + { + read_attribute(attr, s, br); + s = br.readLine(); + } + } + + private static void read_attribute(Attributes attr, String s, + BufferedReader br) throws IOException + { + try + { + int colon = s.indexOf(": "); + String name = s.substring(0, colon); + String value_start = s.substring(colon + 2); + String value = read_header_value(value_start, br); + attr.putValue(name, value); + } + catch (IndexOutOfBoundsException iobe) + { + throw new JarException("Manifest contains a bad header: " + s); + } + } + + private static void read_individual_sections(Map entries, + BufferedReader br) throws + IOException + { + String s = br.readLine(); + while (s != null && (!s.equals(""))) + { + Attributes attr = read_section_name(s, br, entries); + read_attributes(attr, br); + s = br.readLine(); + } + } + + private static Attributes read_section_name(String s, BufferedReader br, + Map entries) throws JarException + { + try + { + String name = expect_header("Name", br, s); + Attributes attr = new Attributes(); + entries.put(name, attr); + return attr; + } + catch (IOException ioe) + { + throw new JarException + ("Section should start with a Name header: " + ioe.getMessage()); + } + } + + /** + * XXX + */ + public void write(OutputStream out) throws IOException + { + PrintWriter pw = + new PrintWriter(new + BufferedWriter(new OutputStreamWriter(out, "8859_1"))); + write_main_section(getMainAttributes(), pw); + pw.println(); + write_individual_sections(getEntries(), pw); + if (pw.checkError()) + { + throw new JarException("Error while writing manifest"); + } + } + + // Private Static functions for writing the Manifest file to a PrintWriter + + private static void write_main_section(Attributes attr, + PrintWriter pw) throws JarException + { + write_version_info(attr, pw); + write_main_attributes(attr, pw); + } + + private static void write_version_info(Attributes attr, PrintWriter pw) + { + // First check if there is already a version attribute set + String version = attr.getValue(Attributes.Name.MANIFEST_VERSION); + if (version == null) + { + version = "1.0"; + } + write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw); + } + + private static void write_header(String name, String value, PrintWriter pw) + { + pw.print(name + ": "); + + int last = 68 - name.length(); + if (last > value.length()) + { + pw.println(value); + } + else + { + pw.println(value.substring(0, last)); + } + while (last < value.length()) + { + pw.print(" "); + int end = (last + 69); + if (end > value.length()) + { + pw.println(value.substring(last)); + } + else + { + pw.println(value.substring(last, end)); + } + last = end; + } + } + + private static void write_main_attributes(Attributes attr, PrintWriter pw) + throws JarException + { + Iterator it = attr.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + // Don't print the manifest version again + if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey())) + { + write_attribute_entry(entry, pw); + } + } + } + + private static void write_attribute_entry(Map.Entry entry, PrintWriter pw) + throws JarException + { + String name = entry.getKey().toString(); + String value = entry.getValue().toString(); + + if (name.equalsIgnoreCase("Name")) + { + throw new JarException("Attributes cannot be called 'Name'"); + } + if (name.startsWith("From")) + { + throw new + JarException("Header cannot start with the four letters 'From'" + + name); + } + write_header(name, value, pw); + } + + private static void write_individual_sections(Map entries, PrintWriter pw) + throws JarException + { + + Iterator it = entries.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + write_header("Name", entry.getKey().toString(), pw); + write_entry_attributes((Attributes) entry.getValue(), pw); + pw.println(); + } + } + + private static void write_entry_attributes(Attributes attr, PrintWriter pw) + throws JarException + { + Iterator it = attr.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry entry = (Map.Entry) it.next(); + write_attribute_entry(entry, pw); + } + } + + /** + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. Calls <CODE>new Manifest(this)</CODE>. + */ + public Object clone() + { + return new Manifest(this); + } + + /** + * Checks if another object is equal to this Manifest object. + * Another Object is equal to this Manifest object if it is an instance of + * Manifest and the main attributes and the entries of the other manifest + * are equal to this one. + */ + public boolean equals(Object o) + { + return (o instanceof Manifest) && + (mainAttr.equals(((Manifest) o).mainAttr)) && + (entries.equals(((Manifest) o).entries)); + } + + /** + * Calculates the hash code of the manifest. Implemented by a xor of the + * hash code of the main attributes with the hash code of the entries map. + */ + public int hashCode() + { + return mainAttr.hashCode() ^ entries.hashCode(); + } + +} diff --git a/libjava/classpath/java/util/jar/package.html b/libjava/classpath/java/util/jar/package.html new file mode 100644 index 00000000000..7fd87878dd8 --- /dev/null +++ b/libjava/classpath/java/util/jar/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.jar package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.jar</title></head> + +<body> +<p>Utility classes for manipulating java archives +(zip files with a manifest file with attributes).</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/logging/ConsoleHandler.java b/libjava/classpath/java/util/logging/ConsoleHandler.java new file mode 100644 index 00000000000..3cf4bca3354 --- /dev/null +++ b/libjava/classpath/java/util/logging/ConsoleHandler.java @@ -0,0 +1,125 @@ +/* ConsoleHandler.java -- a class for publishing log messages to System.err + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * A <code>ConsoleHandler</code> publishes log records to + * <code>System.err</code>. + * + * <p><strong>Configuration:</strong> Values of the subsequent + * <code>LogManager</code> properties are taken into consideration + * when a <code>ConsoleHandler</code> is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + * <ul> + * + * <li><code>java.util.logging.ConsoleHandler.level</code> - specifies + * the initial severity level threshold. Default value: + * <code>Level.INFO</code>.</li> + * + * <li><code>java.util.logging.ConsoleHandler.filter</code> - specifies + * the name of a Filter class. Default value: No Filter.</li> + * + * <li><code>java.util.logging.ConsoleHandler.formatter</code> - specifies + * the name of a Formatter class. Default value: + * <code>java.util.logging.SimpleFormatter</code>.</li> + * + * <li><code>java.util.logging.ConsoleHandler.encoding</code> - specifies + * the name of the character encoding. Default value: + * the default platform encoding.</li> + * + * </ul> + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class ConsoleHandler + extends StreamHandler +{ + /** + * Constructs a <code>StreamHandler</code> that publishes + * log records to <code>System.err</code>. The initial + * configuration is determined by the <code>LogManager</code> + * properties described above. + */ + public ConsoleHandler() + { + super(System.err, "java.util.logging.ConsoleHandler", Level.INFO, + /* formatter */ null, SimpleFormatter.class); + } + + + /** + * Forces any data that may have been buffered to the underlying + * output device, but does <i>not</i> close <code>System.err</code>. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>ConsoleHandler</code> will be informed, but the caller + * of this method will not receive an exception. + */ + public void close() + { + flush(); + } + + + /** + * Publishes a <code>LogRecord</code> to the console, provided the + * record passes all tests for being loggable. + * + * <p>Most applications do not need to call this method directly. + * Instead, they will use use a <code>Logger</code>, which will + * create LogRecords and distribute them to registered handlers. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>SocketHandler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * <p>The GNU implementation of <code>ConsoleHandler.publish</code> + * calls flush() for every request to publish a record, so + * they appear immediately on the console. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + super.publish(record); + flush(); + } +} diff --git a/libjava/classpath/java/util/logging/ErrorManager.java b/libjava/classpath/java/util/logging/ErrorManager.java new file mode 100644 index 00000000000..57c079fe25b --- /dev/null +++ b/libjava/classpath/java/util/logging/ErrorManager.java @@ -0,0 +1,194 @@ +/* ErrorManager.java -- + A class for dealing with errors that a Handler encounters + during logging + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * An <code>ErrorManager</code> deals with errors that a <code>Handler</code> + * encounters while logging. + * + * @see Handler#setErrorManager(ErrorManager) + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class ErrorManager +{ + /* The values have been taken from Sun's public J2SE 1.4 API + * documentation. + * See http://java.sun.com/j2se/1.4/docs/api/constant-values.html + */ + + /** + * Indicates that there was a failure that does not readily + * fall into any of the other categories. + */ + public static final int GENERIC_FAILURE = 0; + + + /** + * Indicates that there was a problem upon writing to + * an output stream. + */ + public static final int WRITE_FAILURE = 1; + + + /** + * Indicates that there was a problem upon flushing + * an output stream. + */ + public static final int FLUSH_FAILURE = 2; + + + /** + * Indicates that there was a problem upon closing + * an output stream. + */ + public static final int CLOSE_FAILURE = 3; + + + /** + * Indicates that there was a problem upon opening + * an output stream. + */ + public static final int OPEN_FAILURE = 4; + + + /** + * Indicates that there was a problem upon formatting + * the message of a log record. + */ + public static final int FORMAT_FAILURE = 5; + + + /** + * Indicates whether the {@link #error} method of this ErrorManager + * has ever been used. + * + * Declared volatile in order to correctly support the + * double-checked locking idiom (once the revised Java Memory Model + * gets adopted); see Classpath bug #2944. + */ + private volatile boolean everUsed = false; + + + public ErrorManager() + { + } + + + /** + * Reports an error that occured upon logging. The default implementation + * emits the very first error to System.err, ignoring subsequent errors. + * + * @param message a message describing the error, or <code>null</code> if + * there is no suitable description. + * + * @param ex an exception, or <code>null</code> if the error is not + * related to an exception. + * + * @param errorCode one of the defined error codes, for example + * <code>ErrorManager.CLOSE_FAILURE</code>. + */ + public void error(String message, Exception ex, int errorCode) + { + if (everUsed) + return; + + synchronized (this) + { + /* The double check is intentional. If the first check was + * omitted, the monitor would have to be entered every time + * error() method was called. If the second check was + * omitted, the code below could be executed by multiple + * threads simultaneously. + * + * This is the 'double-checked locking' idiom, which is broken + * with the current version of the Java memory model. However, + * we assume that JVMs will have adopted a revised version of + * the Java Memory Model by the time GNU Classpath gains + * widespread acceptance. See Classpath bug #2944. + */ + if (everUsed) + return; + + everUsed = true; + } + + String codeMsg; + switch (errorCode) + { + case GENERIC_FAILURE: + codeMsg = "GENERIC_FAILURE"; + break; + + case WRITE_FAILURE: + codeMsg = "WRITE_FAILURE"; + break; + + case FLUSH_FAILURE: + codeMsg = "FLUSH_FAILURE"; + break; + + case CLOSE_FAILURE: + codeMsg = "CLOSE_FAILURE"; + break; + + case OPEN_FAILURE: + codeMsg = "OPEN_FAILURE"; + break; + + case FORMAT_FAILURE: + codeMsg = "FORMAT_FAILURE"; + break; + + default: + codeMsg = String.valueOf(errorCode); + break; + } + + System.err.println("Error upon logging: " + codeMsg); + if ((message != null) && (message.length() > 0)) + System.err.println(message); + + if (ex != null) + ex.printStackTrace(); + } +} + diff --git a/libjava/classpath/java/util/logging/FileHandler.java b/libjava/classpath/java/util/logging/FileHandler.java new file mode 100644 index 00000000000..3d958b7d760 --- /dev/null +++ b/libjava/classpath/java/util/logging/FileHandler.java @@ -0,0 +1,646 @@ +/* FileHandler.java -- a class for publishing log messages to log files + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; + +import java.util.LinkedList; +import java.util.ListIterator; + +/** + * A <code>FileHandler</code> publishes log records to a set of log + * files. A maximum file size can be specified; as soon as a log file + * reaches the size limit, it is closed and the next file in the set + * is taken. + * + * <p><strong>Configuration:</strong> Values of the subsequent + * <code>LogManager</code> properties are taken into consideration + * when a <code>FileHandler</code> is initialized. If a property is + * not defined, or if it has an invalid value, a default is taken + * without an exception being thrown. + * + * <ul> + * + * <li><code>java.util.FileHandler.level</code> - specifies + * the initial severity level threshold. Default value: + * <code>Level.ALL</code>.</li> + * + * <li><code>java.util.FileHandler.filter</code> - specifies + * the name of a Filter class. Default value: No Filter.</li> + * + * <li><code>java.util.FileHandler.formatter</code> - specifies + * the name of a Formatter class. Default value: + * <code>java.util.logging.XMLFormatter</code>.</li> + * + * <li><code>java.util.FileHandler.encoding</code> - specifies + * the name of the character encoding. Default value: + * the default platform encoding.</li> + * + * <li><code>java.util.FileHandler.limit</code> - specifies the number + * of bytes a log file is approximately allowed to reach before it + * is closed and the handler switches to the next file in the + * rotating set. A value of zero means that files can grow + * without limit. Default value: 0 (unlimited growth).</li> + * + * <li><code>java.util.FileHandler.count</code> - specifies the number + * of log files through which this handler cycles. Default value: + * 1.</li> + * + * <li><code>java.util.FileHandler.pattern</code> - specifies a + * pattern for the location and name of the produced log files. + * See the section on <a href="#filePatterns">file name + * patterns</a> for details. Default value: + * <code>"%h/java%u.log"</code>.</li> + * + * <li><code>java.util.FileHandler.append</code> - specifies + * whether the handler will append log records to existing + * files, or whether the handler will clear log files + * upon switching to them. Default value: <code>false</code>, + * indicating that files will be cleared.</li> + * + * </ul> + * + * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a> + * The name and location and log files are specified with pattern + * strings. The handler will replace the following character sequences + * when opening log files: + * + * <p><ul> + * <li><code>/</code> - replaced by the platform-specific path name + * separator. This value is taken from the system property + * <code>file.separator</code>.</li> + * + * <li><code>%t</code> - replaced by the platform-specific location of + * the directory intended for temporary files. This value is + * taken from the system property <code>java.io.tmpdir</code>.</li> + * + * <li><code>%h</code> - replaced by the location of the home + * directory of the current user. This value is taken from the + * system property <code>file.separator</code>.</li> + * + * <li><code>%g</code> - replaced by a generation number for + * distinguisthing the individual items in the rotating set + * of log files. The generation number cycles through the + * sequence 0, 1, ..., <code>count</code> - 1.</li> + * + * <li><code>%u</code> - replaced by a unique number for + * distinguisthing the output files of several concurrently + * running processes. The <code>FileHandler</code> starts + * with 0 when it tries to open a log file. If the file + * cannot be opened because it is currently in use, + * the unique number is incremented by one and opening + * is tried again. These steps are repeated until the + * opening operation succeeds. + * + * <p>FIXME: Is the following correct? Please review. The unique + * number is determined for each log file individually when it is + * opened upon switching to the next file. Therefore, it is not + * correct to assume that all log files in a rotating set bear the + * same unique number. + * + * <p>FIXME: The Javadoc for the Sun reference implementation + * says: "Note that the use of unique ids to avoid conflicts is + * only guaranteed to work reliably when using a local disk file + * system." Why? This needs to be mentioned as well, in case + * the reviewers decide the statement is true. Otherwise, + * file a bug report with Sun.</li> + * + * <li><code>%%</code> - replaced by a single percent sign.</li> + * </ul> + * + * <p>If the pattern string does not contain <code>%g</code> and + * <code>count</code> is greater than one, the handler will append + * the string <code>.%g</code> to the specified pattern. + * + * <p>If the handler attempts to open a log file, this log file + * is being used at the time of the attempt, and the pattern string + * does not contain <code>%u</code>, the handler will append + * the string <code>.%u</code> to the specified pattern. This + * step is performed after any generation number has been + * appended. + * + * <p><em>Examples for the GNU platform:</em> + * + * <p><ul> + * + * <li><code>%h/java%u.log</code> will lead to a single log file + * <code>/home/janet/java0.log</code>, assuming <code>count</code> + * equals 1, the user's home directory is + * <code>/home/janet</code>, and the attempt to open the file + * succeeds.</li> + * + * <li><code>%h/java%u.log</code> will lead to three log files + * <code>/home/janet/java0.log.0</code>, + * <code>/home/janet/java0.log.1</code>, and + * <code>/home/janet/java0.log.2</code>, + * assuming <code>count</code> equals 3, the user's home + * directory is <code>/home/janet</code>, and all attempts + * to open files succeed.</li> + * + * <li><code>%h/java%u.log</code> will lead to three log files + * <code>/home/janet/java0.log.0</code>, + * <code>/home/janet/java1.log.1</code>, and + * <code>/home/janet/java0.log.2</code>, + * assuming <code>count</code> equals 3, the user's home + * directory is <code>/home/janet</code>, and the attempt + * to open <code>/home/janet/java0.log.1</code> fails.</li> + * + * </ul> + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class FileHandler + extends StreamHandler +{ + /** + * The number of bytes a log file is approximately allowed to reach + * before it is closed and the handler switches to the next file in + * the rotating set. A value of zero means that files can grow + * without limit. + */ + private final int limit; + + + /** + * The number of log files through which this handler cycles. + */ + private final int count; + + + /** + * The pattern for the location and name of the produced log files. + * See the section on <a href="#filePatterns">file name patterns</a> + * for details. + */ + private final String pattern; + + + /** + * Indicates whether the handler will append log records to existing + * files (<code>true</code>), or whether the handler will clear log files + * upon switching to them (<code>false</code>). + */ + private final boolean append; + + + /** + * The number of bytes that have currently been written to the stream. + * Package private for use in inner classes. + */ + long written; + + + /** + * A linked list of files we are, or have written to. The entries + * are file path strings, kept in the order + */ + private LinkedList logFiles; + + + /** + * Constructs a <code>FileHandler</code>, taking all property values + * from the current {@link LogManager LogManager} configuration. + * + * @throws java.io.IOException FIXME: The Sun Javadoc says: "if + * there are IO problems opening the files." This conflicts + * with the general principle that configuration errors do + * not prohibit construction. Needs review. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public FileHandler() + throws IOException, SecurityException + { + this(/* pattern: use configiguration */ null, + + LogManager.getIntProperty("java.util.logging.FileHandler.limit", + /* default */ 0), + + LogManager.getIntProperty("java.util.logging.FileHandler.count", + /* default */ 1), + + LogManager.getBooleanProperty("java.util.logging.FileHandler.append", + /* default */ false)); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern) + throws IOException, SecurityException + { + this(pattern, + /* limit */ 0, + /* count */ 1, + /* append */ false); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern, boolean append) + throws IOException, SecurityException + { + this(pattern, + /* limit */ 0, + /* count */ 1, + append); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern, int limit, int count) + throws IOException, SecurityException + { + this(pattern, limit, count, + LogManager.getBooleanProperty( + "java.util.logging.FileHandler.append", + /* default */ false)); + } + + + /** + * Constructs a <code>FileHandler</code> given the pattern for the + * location and name of the produced log files, the size limit, the + * number of log files thorough which the handler will rotate, and + * the <code>append</code> property. All other property values are + * taken from the current {@link LogManager LogManager} + * configuration. + * + * @param pattern The pattern for the location and name of the + * produced log files. See the section on <a + * href="#filePatterns">file name patterns</a> for details. + * If <code>pattern</code> is <code>null</code>, the value is + * taken from the {@link LogManager LogManager} configuration + * property + * <code>java.util.logging.FileHandler.pattern</code>. + * However, this is a pecularity of the GNU implementation, + * and Sun's API specification does not mention what behavior + * is to be expected for <code>null</code>. Therefore, + * applications should not rely on this feature. + * + * @param limit specifies the number of bytes a log file is + * approximately allowed to reach before it is closed and the + * handler switches to the next file in the rotating set. A + * value of zero means that files can grow without limit. + * + * @param count specifies the number of log files through which this + * handler cycles. + * + * @param append specifies whether the handler will append log + * records to existing files (<code>true</code>), or whether the + * handler will clear log files upon switching to them + * (<code>false</code>). + * + * @throws java.io.IOException FIXME: The Sun Javadoc says: "if + * there are IO problems opening the files." This conflicts + * with the general principle that configuration errors do + * not prohibit construction. Needs review. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * <p>FIXME: This seems in contrast to all other handler + * constructors -- verify this by running tests against + * the Sun reference implementation. + */ + public FileHandler(String pattern, + int limit, + int count, + boolean append) + throws IOException, SecurityException + { + super(/* output stream, created below */ null, + "java.util.logging.FileHandler", + /* default level */ Level.ALL, + /* formatter */ null, + /* default formatter */ XMLFormatter.class); + + if ((limit <0) || (count < 1)) + throw new IllegalArgumentException(); + + this.pattern = pattern; + this.limit = limit; + this.count = count; + this.append = append; + this.written = 0; + this.logFiles = new LinkedList (); + + setOutputStream (createFileStream (pattern, limit, count, append, + /* generation */ 0)); + } + + + /* FIXME: Javadoc missing. */ + private OutputStream createFileStream(String pattern, + int limit, + int count, + boolean append, + int generation) + { + String path; + int unique = 0; + + /* Throws a SecurityException if the caller does not have + * LoggingPermission("control"). + */ + LogManager.getLogManager().checkAccess(); + + /* Default value from the java.util.logging.FileHandler.pattern + * LogManager configuration property. + */ + if (pattern == null) + pattern = LogManager.getLogManager().getProperty( + "java.util.logging.FileHandler.pattern"); + if (pattern == null) + pattern = "%h/java%u.log"; + + if (count > 1 && !has (pattern, 'g')) + pattern = pattern + ".%g"; + + do + { + path = replaceFileNameEscapes(pattern, generation, unique, count); + + try + { + File file = new File(path); + if (!file.exists () || append) + { + FileOutputStream fout = new FileOutputStream (file, append); + // FIXME we need file locks for this to work properly, but they + // are not implemented yet in Classpath! Madness! +// FileChannel channel = fout.getChannel (); +// FileLock lock = channel.tryLock (); +// if (lock != null) // We've locked the file. +// { + if (logFiles.isEmpty ()) + logFiles.addFirst (path); + return new ostr (fout); +// } + } + } + catch (Exception ex) + { + reportError (null, ex, ErrorManager.OPEN_FAILURE); + } + + unique = unique + 1; + if (!has (pattern, 'u')) + pattern = pattern + ".%u"; + } + while (true); + } + + + /** + * Replaces the substrings <code>"/"</code> by the value of the + * system property <code>"file.separator"</code>, <code>"%t"</code> + * by the value of the system property + * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of + * the system property <code>"user.home"</code>, <code>"%g"</code> + * by the value of <code>generation</code>, <code>"%u"</code> by the + * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a + * single percent character. If <code>pattern</code> does + * <em>not</em> contain the sequence <code>"%g"</code>, + * the value of <code>generation</code> will be appended to + * the result. + * + * @throws NullPointerException if one of the system properties + * <code>"file.separator"</code>, + * <code>"java.io.tmpdir"</code>, or + * <code>"user.home"</code> has no value and the + * corresponding escape sequence appears in + * <code>pattern</code>. + */ + private static String replaceFileNameEscapes(String pattern, + int generation, + int uniqueNumber, + int count) + { + StringBuffer buf = new StringBuffer(pattern); + String replaceWith; + boolean foundGeneration = false; + + int pos = 0; + do + { + // Uncomment the next line for finding bugs. + // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos)); + + if (buf.charAt(pos) == '/') + { + /* The same value is also provided by java.io.File.separator. */ + replaceWith = System.getProperty("file.separator"); + buf.replace(pos, pos + 1, replaceWith); + pos = pos + replaceWith.length() - 1; + continue; + } + + if (buf.charAt(pos) == '%') + { + switch (buf.charAt(pos + 1)) + { + case 't': + replaceWith = System.getProperty("java.io.tmpdir"); + break; + + case 'h': + replaceWith = System.getProperty("user.home"); + break; + + case 'g': + replaceWith = Integer.toString(generation); + foundGeneration = true; + break; + + case 'u': + replaceWith = Integer.toString(uniqueNumber); + break; + + case '%': + replaceWith = "%"; + break; + + default: + replaceWith = "??"; + break; // FIXME: Throw exception? + } + + buf.replace(pos, pos + 2, replaceWith); + pos = pos + replaceWith.length() - 1; + continue; + } + } + while (++pos < buf.length() - 1); + + if (!foundGeneration && (count > 1)) + { + buf.append('.'); + buf.append(generation); + } + + return buf.toString(); + } + + + /* FIXME: Javadoc missing. */ + public void publish(LogRecord record) + { + if (limit > 0 && written >= limit) + rotate (); + super.publish(record); + flush (); + } + + /** + * Rotates the current log files, possibly removing one if we + * exceed the file count. + */ + private synchronized void rotate () + { + if (logFiles.size () > 0) + { + File f1 = null; + ListIterator lit = null; + + // If we reach the file count, ditch the oldest file. + if (logFiles.size () == count) + { + f1 = new File ((String) logFiles.getLast ()); + f1.delete (); + lit = logFiles.listIterator (logFiles.size () - 1); + } + // Otherwise, move the oldest to a new location. + else + { + String path = replaceFileNameEscapes (pattern, logFiles.size (), + /* unique */ 0, count); + f1 = new File (path); + logFiles.addLast (path); + lit = logFiles.listIterator (logFiles.size () - 1); + } + + // Now rotate the files. + while (lit.hasPrevious ()) + { + String s = (String) lit.previous (); + File f2 = new File (s); + f2.renameTo (f1); + f1 = f2; + } + } + + setOutputStream (createFileStream (pattern, limit, count, append, + /* generation */ 0)); + + // Reset written count. + written = 0; + } + + /** + * Tell if <code>pattern</code> contains the pattern sequence + * with character <code>escape</code>. That is, if <code>escape</code> + * is 'g', this method returns true if the given pattern contains + * "%g", and not just the substring "%g" (for example, in the case of + * "%%g"). + * + * @param pattern The pattern to test. + * @param escape The escape character to search for. + * @return True iff the pattern contains the escape sequence with the + * given character. + */ + private static boolean has (final String pattern, final char escape) + { + final int len = pattern.length (); + boolean sawPercent = false; + for (int i = 0; i < len; i++) + { + char c = pattern.charAt (i); + if (sawPercent) + { + if (c == escape) + return true; + if (c == '%') // Double percent + { + sawPercent = false; + continue; + } + } + sawPercent = (c == '%'); + } + return false; + } + + /** + * An output stream that tracks the number of bytes written to it. + */ + private final class ostr extends FilterOutputStream + { + private ostr (OutputStream out) + { + super (out); + } + + public void write (final int b) throws IOException + { + out.write (b); + FileHandler.this.written++; // FIXME: synchronize? + } + + public void write (final byte[] b) throws IOException + { + write (b, 0, b.length); + } + + public void write (final byte[] b, final int offset, final int length) + throws IOException + { + out.write (b, offset, length); + FileHandler.this.written += length; // FIXME: synchronize? + } + } +} diff --git a/libjava/classpath/java/util/logging/Filter.java b/libjava/classpath/java/util/logging/Filter.java new file mode 100644 index 00000000000..ec4597670d5 --- /dev/null +++ b/libjava/classpath/java/util/logging/Filter.java @@ -0,0 +1,64 @@ +/* Filter.java -- an interface for filters that decide whether a + LogRecord should be published or discarded + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * By implementing the <code>Filter</code> interface, applications + * can control what is being logged based on arbitrary properties, + * not just the severity level. Both <code>Handler</code> and + * <code>Logger</code> allow to register Filters whose + * <code>isLoggable</code> method will be called when a + * <code>LogRecord</code> has passed the test based on the + * severity level. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public interface Filter +{ + /** + * Determines whether a LogRecord should be published or discarded. + * + * @param record the <code>LogRecord</code> to be inspected. + * + * @return <code>true</code> if the record should be published, + * <code>false</code> if it should be discarded. + */ + boolean isLoggable(LogRecord record); +} diff --git a/libjava/classpath/java/util/logging/Formatter.java b/libjava/classpath/java/util/logging/Formatter.java new file mode 100644 index 00000000000..ee747b0b978 --- /dev/null +++ b/libjava/classpath/java/util/logging/Formatter.java @@ -0,0 +1,171 @@ +/* Formatter.java -- + A class for formatting log messages by localizing message texts + and performing substitution of parameters + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +/** + * A <code>Formatter</code> supports handlers by localizing + * message texts and by subsituting parameter values for their + * placeholders. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public abstract class Formatter +{ + /** + * Constructs a new Formatter. + */ + protected Formatter() + { + } + + + /** + * Formats a LogRecord into a string. Usually called by handlers + * which need a string for a log record, for example to append + * a record to a log file or to transmit a record over the network. + * + * @param record the log record for which a string form is requested. + */ + public abstract String format(LogRecord record); + + + /** + * Returns a string that handlers are supposed to emit before + * the first log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable header. + * + * @return a string for the header. + * + * @param handler the handler which will prepend the returned + * string in front of the first log record. This method + * may inspect certain properties of the handler, for + * example its encoding, in order to construct the header. + */ + public String getHead(Handler handler) + { + return ""; + } + + + /** + * Returns a string that handlers are supposed to emit after + * the last log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable tail. + * + * @return a string for the header. + * + * @param handler the handler which will append the returned + * string after the last log record. This method + * may inspect certain properties of the handler + * in order to construct the tail. + */ + public String getTail(Handler handler) + { + return ""; + } + + + /** + * Formats the message part of a log record. + * + * <p>First, the Formatter localizes the record message to the + * default locale by looking up the message in the record's + * localization resource bundle. If this step fails because there + * is no resource bundle associated with the record, or because the + * record message is not a key in the bundle, the raw message is + * used instead. + * + * <p>Second, the Formatter substitutes appropriate strings for + * the message parameters. If the record returns a non-empty + * array for <code>getParameters()</code> and the localized + * message string contains the character sequence "{0", the + * formatter uses <code>java.text.MessageFormat</code> to format + * the message. Otherwise, no parameter substitution is performed. + * + * @param record the log record to be localized and formatted. + * + * @return the localized message text where parameters have been + * substituted by suitable strings. + * + * @throws NullPointerException if <code>record</code> + * is <code>null</code>. + */ + public String formatMessage(LogRecord record) + { + String msg; + ResourceBundle bundle; + Object[] params; + + /* This will throw a NullPointerExceptionif record is null. */ + msg = record.getMessage(); + if (msg == null) + msg = ""; + + /* Try to localize the message. */ + bundle = record.getResourceBundle(); + if (bundle != null) + { + try + { + msg = bundle.getString(msg); + } + catch (java.util.MissingResourceException _) + { + } + } + + /* Format the message if there are parameters. */ + params = record.getParameters(); + if ((params != null) + && (params.length > 0) + && (msg.indexOf("{0") >= 0)) + { + msg = MessageFormat.format(msg, params); + } + + return msg; + } +} diff --git a/libjava/classpath/java/util/logging/Handler.java b/libjava/classpath/java/util/logging/Handler.java new file mode 100644 index 00000000000..c3227d6f531 --- /dev/null +++ b/libjava/classpath/java/util/logging/Handler.java @@ -0,0 +1,386 @@ +/* Handler.java -- a class for publishing log messages + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.UnsupportedEncodingException; + +/** + * A <code>Handler</code> publishes <code>LogRecords</code> to + * a sink, for example a file, the console or a network socket. + * There are different subclasses of <code>Handler</code> + * to deal with different kinds of sinks. + * + * <p>FIXME: Are handlers thread-safe, or is the assumption that only + * loggers are, and a handler can belong only to one single logger? If + * the latter, should we enforce it? (Spec not clear). In any + * case, it needs documentation. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public abstract class Handler +{ + Formatter formatter; + Filter filter; + Level level; + ErrorManager errorManager; + String encoding; + + /** + * Constructs a Handler with a logging severity level of + * <code>Level.ALL</code>, no formatter, no filter, and + * an instance of <code>ErrorManager</code> managing errors. + * + * <p><strong>Specification Note:</strong> The specification of the + * Java<sup>TM</sup> Logging API does not mention which character + * encoding is to be used by freshly constructed Handlers. The GNU + * implementation uses the default platform encoding, but other + * Java implementations might behave differently. + * + * <p><strong>Specification Note:</strong> While a freshly constructed + * Handler is required to have <em>no filter</em> according to the + * specification, <code>null</code> is not a valid parameter for + * <code>Handler.setFormatter</code>. Therefore, the following + * code will throw a <code>java.lang.NullPointerException</code>: + * + * <p><pre>Handler h = new MyConcreteSubclassOfHandler(); +h.setFormatter(h.getFormatter());</pre> + * + * It seems strange that a freshly constructed Handler is not + * supposed to provide a Formatter, but this is what the specification + * says. + */ + protected Handler() + { + level = Level.ALL; + } + + + /** + * Publishes a <code>LogRecord</code> to an appropriate sink, + * provided the record passes all tests for being loggable. The + * <code>Handler</code> will localize the message of the log + * record and substitute any message parameters. + * + * <p>Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will + * create LogRecords and distribute them to registered handlers. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * @param record the log event to be published. + */ + public abstract void publish(LogRecord record); + + + /** + * Forces any data that may have been buffered to the underlying + * output device. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception. + */ + public abstract void flush(); + + + /** + * Closes this <code>Handler</code> after having flushed + * the buffers. As soon as <code>close</code> has been called, + * a <code>Handler</code> should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * <code>Handler</code> in any other way may throw runtime + * exceptions after calling <code>close</code>. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public abstract void close() + throws SecurityException; + + + /** + * Returns the <code>Formatter</code> which will be used to + * localize the text of log messages and to substitute + * message parameters. A <code>Handler</code> is encouraged, + * but not required to actually use an assigned + * <code>Formatter</code>. + * + * @return the <code>Formatter</code> being used, or + * <code>null</code> if this <code>Handler</code> + * does not use formatters and no formatter has + * ever been set by calling <code>setFormatter</code>. + */ + public Formatter getFormatter() + { + return formatter; + } + + + /** + * Sets the <code>Formatter</code> which will be used to + * localize the text of log messages and to substitute + * message parameters. A <code>Handler</code> is encouraged, + * but not required to actually use an assigned + * <code>Formatter</code>. + * + * @param formatter the new <code>Formatter</code> to use. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @throws NullPointerException if <code>formatter</code> is + * <code>null</code>. + */ + public void setFormatter(Formatter formatter) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + /* Throws a NullPointerException if formatter is null. */ + formatter.getClass(); + + this.formatter = formatter; + } + + + /** + * Returns the character encoding which this handler uses for publishing + * log records. + * + * @param encoding the name of a character encoding, or <code>null</code> + * for the default platform encoding. + */ + public String getEncoding() + { + return encoding; + } + + + /** + * Sets the character encoding which this handler uses for publishing + * log records. The encoding of a <code>Handler</code> must be + * set before any log records have been published. + * + * @param encoding the name of a character encoding, or <code>null</code> + * for the default encoding. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + */ + public void setEncoding(String encoding) + throws SecurityException, UnsupportedEncodingException + { + /* Should any developer ever change this implementation, they are + * advised to have a look at StreamHandler.setEncoding(String), + * which overrides this method without calling super.setEncoding. + */ + LogManager.getLogManager().checkAccess(); + + /* Simple check for supported encodings. This is more expensive + * than it could be, but this method is overwritten by StreamHandler + * anyway. + */ + if (encoding != null) + new String(new byte[0], encoding); + + this.encoding = encoding; + } + + + /** + * Returns the <code>Filter</code> that currently controls which + * log records are being published by this <code>Handler</code>. + * + * @return the currently active <code>Filter</code>, or + * <code>null</code> if no filter has been associated. + * In the latter case, log records are filtered purely + * based on their severity level. + */ + public Filter getFilter() + { + return filter; + } + + + /** + * Sets the <code>Filter</code> for controlling which + * log records will be published by this <code>Handler</code>. + * + * @return the <code>Filter</code> to use, or + * <code>null</code> to filter log records purely based + * on their severity level. + */ + public void setFilter(Filter filter) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + this.filter = filter; + } + + + /** + * Returns the <code>ErrorManager</code> that currently deals + * with errors originating from this Handler. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public ErrorManager getErrorManager() + { + LogManager.getLogManager().checkAccess(); + + /* Developers wanting to change the subsequent code should + * have a look at Handler.reportError -- it also can create + * an ErrorManager, but does so without checking permissions + * to control the logging infrastructure. + */ + if (errorManager == null) + errorManager = new ErrorManager(); + + return errorManager; + } + + + public void setErrorManager(ErrorManager manager) + { + LogManager.getLogManager().checkAccess(); + + /* Make sure manager is not null. */ + manager.getClass(); + + this.errorManager = manager; + } + + + protected void reportError(String message, Exception ex, int code) + { + if (errorManager == null) + errorManager = new ErrorManager(); + + errorManager.error(message, ex, code); + } + + + /** + * Returns the severity level threshold for this <code>Handler</code> + * All log records with a lower severity level will be discarded; + * a log record of the same or a higher level will be published + * unless an installed <code>Filter</code> decides to discard it. + * + * @return the severity level below which all log messages + * will be discarded. + */ + public Level getLevel() + { + return level; + } + + + /** + * Sets the severity level threshold for this <code>Handler</code>. + * All log records with a lower severity level will be discarded; + * a log record of the same or a higher level will be published + * unless an installed <code>Filter</code> decides to discard it. + * + * @param level the severity level below which all log messages + * will be discarded. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @exception NullPointerException if <code>level</code> is + * <code>null</code>. + */ + public void setLevel(Level level) + { + LogManager.getLogManager().checkAccess(); + + /* Throw NullPointerException if level is null. */ + level.getClass(); + this.level = level; + } + + + /** + * Checks whether a <code>LogRecord</code> would be logged + * if it was passed to this <code>Handler</code> for publication. + * + * <p>The <code>Handler</code> implementation considers a record as + * loggable if its level is greater than or equal to the severity + * level threshold. In a second step, if a {@link Filter} has + * been installed, its {@link Filter#isLoggable(LogRecord) isLoggable} + * method is invoked. Subclasses of <code>Handler</code> can override + * this method to impose their own constraints. + * + * @param record the <code>LogRecord</code> to be checked. + * + * @return <code>true</code> if <code>record</code> would + * be published by {@link #publish(LogRecord) publish}, + * <code>false</code> if it would be discarded. + * + * @see #setLevel(Level) + * @see #setFilter(Filter) + * @see Filter#isLoggable(LogRecord) + * + * @throws NullPointerException if <code>record</code> + * is <code>null</code>. + */ + public boolean isLoggable(LogRecord record) + { + if (record.getLevel().intValue() < level.intValue()) + return false; + + if (filter != null) + return filter.isLoggable(record); + else + return true; + } +} diff --git a/libjava/classpath/java/util/logging/Level.java b/libjava/classpath/java/util/logging/Level.java new file mode 100644 index 00000000000..2c400dc3a90 --- /dev/null +++ b/libjava/classpath/java/util/logging/Level.java @@ -0,0 +1,414 @@ +/* Level.java -- a class for indicating logging levels + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.Serializable; +import java.util.ResourceBundle; + +/** + * A class for indicating logging levels. A number of commonly used + * levels is pre-defined (such as <code>java.util.logging.Level.INFO</code>), + * and applications should utilize those whenever possible. For specialized + * purposes, however, applications can sub-class Level in order to define + * custom logging levels. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class Level implements Serializable +{ + /* The integer values are the same as in the Sun J2SE 1.4. + * They have been obtained with a test program. In J2SE 1.4.1, + * Sun has amended the API documentation; these values are now + * publicly documented. + */ + + /** + * The <code>OFF</code> level is used as a threshold for filtering + * log records, meaning that no message should be logged. + * + * @see Logger#setLevel(java.util.logging.Level) + */ + public static final Level OFF = new Level ("OFF", Integer.MAX_VALUE); + + /** + * Log records whose level is <code>SEVERE</code> indicate a serious + * failure that prevents normal program execution. Messages at this + * level should be understandable to an inexperienced, non-technical + * end user. Ideally, they explain in simple words what actions the + * user can take in order to resolve the problem. + */ + public static final Level SEVERE = new Level ("SEVERE", 1000); + + + /** + * Log records whose level is <code>WARNING</code> indicate a + * potential problem that does not prevent normal program execution. + * Messages at this level should be understandable to an + * inexperienced, non-technical end user. Ideally, they explain in + * simple words what actions the user can take in order to resolve + * the problem. + */ + public static final Level WARNING = new Level ("WARNING", 900); + + + /** + * Log records whose level is <code>INFO</code> are used in purely + * informational situations that do not constitute serious errors or + * potential problems. In the default logging configuration, INFO + * messages will be written to the system console. For this reason, + * the INFO level should be used only for messages that are + * important to end users and system administrators. Messages at + * this level should be understandable to an inexperienced, + * non-technical user. + */ + public static final Level INFO = new Level ("INFO", 800); + + + /** + * Log records whose level is <code>CONFIG</code> are used for + * describing the static configuration, for example the windowing + * environment, the operating system version, etc. + */ + public static final Level CONFIG = new Level ("CONFIG", 700); + + + /** + * Log records whose level is <code>FINE</code> are typically used + * for messages that are relevant for developers using + * the component generating log messages. Examples include minor, + * recoverable failures, or possible inefficiencies. + */ + public static final Level FINE = new Level ("FINE", 500); + + + /** + * Log records whose level is <code>FINER</code> are intended for + * rather detailed tracing, for example entering a method, returning + * from a method, or throwing an exception. + */ + public static final Level FINER = new Level ("FINER", 400); + + + /** + * Log records whose level is <code>FINEST</code> are used for + * highly detailed tracing, for example to indicate that a certain + * point inside the body of a method has been reached. + */ + public static final Level FINEST = new Level ("FINEST", 300); + + + /** + * The <code>ALL</code> level is used as a threshold for filtering + * log records, meaning that every message should be logged. + * + * @see Logger#setLevel(java.util.logging.Level) + */ + public static final Level ALL = new Level ("ALL", Integer.MIN_VALUE); + + + private static final Level[] knownLevels = { + ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF + }; + + + /** + * The name of the Level without localizing it, for example + * "WARNING". + */ + private String name; + + + /** + * The integer value of this <code>Level</code>. + */ + private int value; + + + /** + * The name of the resource bundle used for localizing the level + * name, or <code>null</code> if the name does not undergo + * localization. + */ + private String resourceBundleName; + + + /** + * Creates a logging level given a name and an integer value. + * It rarely is necessary to create custom levels, + * as most applications should be well served with one of the + * standard levels such as <code>Level.CONFIG</code>, + * <code>Level.INFO</code>, or <code>Level.FINE</code>. + * + * @param name the name of the level. + * + * @param value the integer value of the level. Please note + * that the Java<small><sup>TM</sup></small> + * Logging API does not specify integer + * values for standard levels (such as + * Level.FINE). Therefore, a custom + * level should pass an integer value that + * is calculated at run-time, e.g. + * <code>(Level.FINE.intValue() + Level.CONFIG.intValue()) + * / 2</code> for a level between FINE and CONFIG. + */ + protected Level(String name, int value) + { + this(name, value, null); + } + + + /** + * Create a logging level given a name, an integer value and a name + * of a resource bundle for localizing the level name. It rarely + * is necessary to create custom levels, as most applications + * should be well served with one of the standard levels such as + * <code>Level.CONFIG</code>, <code>Level.INFO</code>, or + * <code>Level.FINE</code>. + * + * @param name the name of the level. + * + * @param value the integer value of the level. Please note + * that the Java<small><sup>TM</sup></small> + * Logging API does not specify integer + * values for standard levels (such as + * Level.FINE). Therefore, a custom + * level should pass an integer value that + * is calculated at run-time, e.g. + * <code>(Level.FINE.intValue() + Level.CONFIG.intValue()) + * / 2</code> for a level between FINE and CONFIG. + * + * @param resourceBundleName the name of a resource bundle + * for localizing the level name, or <code>null</code> + * if the name does not need to be localized. + */ + protected Level(String name, int value, String resourceBundleName) + { + this.name = name; + this.value = value; + this.resourceBundleName = resourceBundleName; + } + + + static final long serialVersionUID = -8176160795706313070L; + + + /** + * Checks whether the Level has the same intValue as one of the + * pre-defined levels. If so, the pre-defined level object is + * returned. + * + * <br/>Since the resource bundle name is not taken into + * consideration, it is possible to resolve Level objects that have + * been de-serialized by another implementation, even if the other + * implementation uses a different resource bundle for localizing + * the names of pre-defined levels. + */ + private Object readResolve() + { + for (int i = 0; i < knownLevels.length; i++) + if (value == knownLevels[i].intValue()) + return knownLevels[i]; + + return this; + } + + + /** + * Returns the name of the resource bundle used for localizing the + * level name. + * + * @return the name of the resource bundle used for localizing the + * level name, or <code>null</code> if the name does not undergo + * localization. + */ + public String getResourceBundleName() + { + return resourceBundleName; + } + + + /** + * Returns the name of the Level without localizing it, for example + * "WARNING". + */ + public String getName() + { + return name; + } + + + /** + * Returns the name of the Level after localizing it, for example + * "WARNUNG". + */ + public String getLocalizedName() + { + String localizedName = null; + + if (resourceBundleName != null) + { + try + { + ResourceBundle b = ResourceBundle.getBundle(resourceBundleName); + localizedName = b.getString(name); + } + catch (Exception _) + { + } + } + + if (localizedName != null) + return localizedName; + else + return name; + } + + + /** + * Returns the name of the Level without localizing it, for example + * "WARNING". + */ + public final String toString() + { + return getName(); + } + + + /** + * Returns the integer value of the Level. + */ + public final int intValue() + { + return value; + } + + + /** + * Returns one of the standard Levels given either its name or its + * integer value. Custom subclasses of Level will not be returned + * by this method. + * + * @throws IllegalArgumentException if <code>name</code> is neither + * the name nor the integer value of one of the pre-defined standard + * logging levels. + * + * @throws NullPointerException if <code>name</code> is null. + * + */ + public static Level parse(String name) + throws IllegalArgumentException + { + /* This will throw a NullPointerException if name is null, + * as required by the API specification. + */ + name = name.intern(); + + for (int i = 0; i < knownLevels.length; i++) + { + if (name == knownLevels[i].name) + return knownLevels[i]; + } + + try + { + int num = Integer.parseInt(name); + for (int i = 0; i < knownLevels.length; i++) + if (num == knownLevels[i].value) + return knownLevels[i]; + } + catch (NumberFormatException _) + { + } + + String msg = "Not the name of a standard logging level: \"" + name + "\""; + throw new IllegalArgumentException(msg); + } + + + /** + * Checks whether this Level's integer value is equal to that of + * another object. + * + * @return <code>true</code> if <code>other</code> is an instance of + * <code>java.util.logging.Level</code> and has the same integer + * value, <code>false</code> otherwise. + */ + public boolean equals(Object other) + { + if (!(other instanceof Level)) + return false; + + return value == ((Level) other).value; + } + + + /** + * Returns a hash code for this Level which is based on its numeric + * value. + */ + public int hashCode() + { + return value; + } + + + /** + * Determines whether or not this Level is one of the standard + * levels specified in the Logging API. + * + * <p>This method is package-private because it is not part + * of the logging API specification. However, an XMLFormatter + * is supposed to emit the numeric value for a custom log + * level, but the name for a pre-defined level. It seems + * cleaner to put this method to Level than to write some + * procedural code for XMLFormatter. + * + * @return <code>true</code> if this Level is a standard level, + * <code>false</code> otherwise. + */ + final boolean isStandardLevel() + { + for (int i = 0; i < knownLevels.length; i++) + if (knownLevels[i] == this) + return true; + + return false; + } +} + diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java new file mode 100644 index 00000000000..7e3fd97d01f --- /dev/null +++ b/libjava/classpath/java/util/logging/LogManager.java @@ -0,0 +1,829 @@ +/* LogManager.java -- a class for maintaining Loggers and managing + configuration properties + Copyright (C) 2002,2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * The <code>LogManager</code> maintains a hierarchical namespace + * of Logger objects and manages properties for configuring the logging + * framework. There exists only one single <code>LogManager</code> + * per virtual machine. This instance can be retrieved using the + * static method {@link #getLogManager()}. + * + * <p><strong>Configuration Process:</strong> The global LogManager + * object is created and configured when the class + * <code>java.util.logging.LogManager</code> is initialized. + * The configuration process includes the subsequent steps: + * + * <ul> + * <li>If the system property <code>java.util.logging.manager</code> + * is set to the name of a subclass of + * <code>java.util.logging.LogManager</code>, an instance of + * that subclass is created and becomes the global LogManager. + * Otherwise, a new instance of LogManager is created.</li> + * <li>The <code>LogManager</code> constructor tries to create + * a new instance of the class specified by the system + * property <code>java.util.logging.config.class</code>. + * Typically, the constructor of this class will call + * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code> + * for configuring the logging framework. + * The configuration process stops at this point if + * the system property <code>java.util.logging.config.class</code> + * is set (irrespective of whether the class constructor + * could be called or an exception was thrown).</li> + * + * <li>If the system property <code>java.util.logging.config.class</code> + * is <em>not</em> set, the configuration parameters are read in from + * a file and passed to + * {@link #readConfiguration(java.io.InputStream)}. + * The name and location of this file are specified by the system + * property <code>java.util.logging.config.file</code>.</li> + * <li>If the system property <code>java.util.logging.config.file</code> + * is not set, however, the contents of the URL + * "{gnu.classpath.home.url}/logging.properties" are passed to + * {@link #readConfiguration(java.io.InputStream)}. + * Here, "{gnu.classpath.home.url}" stands for the value of + * the system property <code>gnu.classpath.home.url</code>.</li> + * </ul> + * + * <p>The <code>LogManager</code> has a level of <code>INFO</code> by + * default, and this will be inherited by <code>Logger</code>s unless they + * override it either by properties or programmatically. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class LogManager +{ + /** + * The singleton LogManager instance. + */ + private static LogManager logManager; + + /** + * The registered named loggers; maps the name of a Logger to + * a WeakReference to it. + */ + private Map loggers; + final Logger rootLogger; + + /** + * The properties for the logging framework which have been + * read in last. + */ + private Properties properties; + + /** + * A delegate object that provides support for handling + * PropertyChangeEvents. The API specification does not + * mention which bean should be the source in the distributed + * PropertyChangeEvents, but Mauve test code has determined that + * the Sun J2SE 1.4 reference implementation uses the LogManager + * class object. This is somewhat strange, as the class object + * is not the bean with which listeners have to register, but + * there is no reason for the GNU Classpath implementation to + * behave differently from the reference implementation in + * this case. + */ + private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */ + LogManager.class); + + protected LogManager() + { + if (logManager != null) + throw new IllegalStateException("there can be only one LogManager; use LogManager.getLogManager()"); + + logManager = this; + loggers = new java.util.HashMap(); + rootLogger = new Logger("", null); + rootLogger.setLevel(Level.INFO); + addLogger(rootLogger); + + /* Make sure that Logger.global has the rootLogger as its parent. + * + * Logger.global is set during class initialization of Logger, + * which may or may not be before this code is being executed. + * For example, on the Sun 1.3.1 and 1.4.0 JVMs, Logger.global + * has been set before this code is being executed. In contrast, + * Logger.global still is null on GCJ 3.2. Since the LogManager + * and Logger classes are mutually dependent, both behaviors are + * correct. + * + * This means that we cannot depend on Logger.global to have its + * value when this code executes, although that variable is final. + * Since Logger.getLogger will always return the same logger for + * the same name, the subsequent line works fine irrespective of + * the order in which classes are initialized. + */ + Logger.getLogger("global").setParent(rootLogger); + Logger.getLogger("global").setUseParentHandlers(true); + } + + /** + * Returns the globally shared LogManager instance. + */ + public static LogManager getLogManager() + { + return logManager; + } + + static + { + makeLogManager(); + + /* The Javadoc description of the class explains + * what is going on here. + */ + Object configurator = createInstance(System.getProperty("java.util.logging.config.class"), + /* must be instance of */ Object.class); + + try + { + if (configurator == null) + getLogManager().readConfiguration(); + } + catch (IOException ex) + { + /* FIXME: Is it ok to ignore exceptions here? */ + } + } + + private static LogManager makeLogManager() + { + String managerClassName; + LogManager manager; + + managerClassName = System.getProperty("java.util.logging.manager"); + manager = (LogManager) createInstance(managerClassName, LogManager.class); + if (manager != null) + return manager; + + if (managerClassName != null) + System.err.println("WARNING: System property \"java.util.logging.manager\"" + + " should be the name of a subclass of java.util.logging.LogManager"); + + return new LogManager(); + } + + /** + * Registers a listener which will be notified when the + * logging properties are re-read. + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener listener) + { + /* do not register null. */ + listener.getClass(); + + pcs.addPropertyChangeListener(listener); + } + + /** + * Unregisters a listener. + * + * If <code>listener</code> has not been registered previously, + * nothing happens. Also, no exception is thrown if + * <code>listener</code> is <code>null</code>. + */ + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) + { + if (listener != null) + pcs.removePropertyChangeListener(listener); + } + + /** + * Adds a named logger. If a logger with the same name has + * already been registered, the method returns <code>false</code> + * without adding the logger. + * + * <p>The <code>LogManager</code> only keeps weak references + * to registered loggers. Therefore, names can become available + * after automatic garbage collection. + * + * @param logger the logger to be added. + * + * @return <code>true</code>if <code>logger</code> was added, + * <code>false</code> otherwise. + * + * @throws NullPointerException if <code>name</code> is + * <code>null</code>. + */ + public synchronized boolean addLogger(Logger logger) + { + /* To developers thinking about to remove the 'synchronized' + * declaration from this method: Please read the comment + * in java.util.logging.Logger.getLogger(String, String) + * and make sure that whatever you change wrt. synchronization + * does not endanger thread-safety of Logger.getLogger. + * The current implementation of Logger.getLogger assumes + * that LogManager does its synchronization on the globally + * shared instance of LogManager. + */ + String name; + WeakReference ref; + + /* This will throw a NullPointerException if logger is null, + * as required by the API specification. + */ + name = logger.getName(); + + ref = (WeakReference) loggers.get(name); + if (ref != null) + { + if (ref.get() != null) + return false; + + /* There has been a logger under this name in the past, + * but it has been garbage collected. + */ + loggers.remove(ref); + } + + /* Adding a named logger requires a security permission. */ + if ((name != null) && ! name.equals("")) + checkAccess(); + + Logger parent = findAncestor(logger); + loggers.put(name, new WeakReference(logger)); + if (parent != logger.getParent()) + logger.setParent(parent); + + /* It can happen that existing loggers should be children of + * the newly added logger. For example, assume that there + * already exist loggers under the names "", "foo", and "foo.bar.baz". + * When adding "foo.bar", the logger "foo.bar.baz" should change + * its parent to "foo.bar". + */ + if (parent != rootLogger) + { + for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();) + { + Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())) + .get(); + if ((possChild == null) || (possChild == logger) + || (possChild.getParent() != parent)) + continue; + + if (! possChild.getName().startsWith(name)) + continue; + + if (possChild.getName().charAt(name.length()) != '.') + continue; + + possChild.setParent(logger); + } + } + + return true; + } + + /** + * Finds the closest ancestor for a logger among the currently + * registered ones. For example, if the currently registered + * loggers have the names "", "foo", and "foo.bar", the result for + * "foo.bar.baz" will be the logger whose name is "foo.bar". + * + * @param child a logger for whose name no logger has been + * registered. + * + * @return the closest ancestor for <code>child</code>, + * or <code>null</code> if <code>child</code> + * is the root logger. + * + * @throws NullPointerException if <code>child</code> + * is <code>null</code>. + */ + private synchronized Logger findAncestor(Logger child) + { + String childName = child.getName(); + int childNameLength = childName.length(); + Logger best = rootLogger; + int bestNameLength = 0; + + Logger cand; + String candName; + int candNameLength; + + if (child == rootLogger) + return null; + + for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();) + { + candName = (String) iter.next(); + candNameLength = candName.length(); + + if (candNameLength > bestNameLength + && childNameLength > candNameLength + && childName.startsWith(candName) + && childName.charAt(candNameLength) == '.') + { + cand = (Logger) ((WeakReference) loggers.get(candName)).get(); + if ((cand == null) || (cand == child)) + continue; + + bestNameLength = candName.length(); + best = cand; + } + } + + return best; + } + + /** + * Returns a Logger given its name. + * + * @param name the name of the logger. + * + * @return a named Logger, or <code>null</code> if there is no + * logger with that name. + * + * @throw java.lang.NullPointerException if <code>name</code> + * is <code>null</code>. + */ + public synchronized Logger getLogger(String name) + { + WeakReference ref; + + /* Throw a NullPointerException if name is null. */ + name.getClass(); + + ref = (WeakReference) loggers.get(name); + if (ref != null) + return (Logger) ref.get(); + else + return null; + } + + /** + * Returns an Enumeration of currently registered Logger names. + * Since other threads can register loggers at any time, the + * result could be different any time this method is called. + * + * @return an Enumeration with the names of the currently + * registered Loggers. + */ + public synchronized Enumeration getLoggerNames() + { + return Collections.enumeration(loggers.keySet()); + } + + /** + * Resets the logging configuration by removing all handlers for + * registered named loggers and setting their level to <code>null</code>. + * The level of the root logger will be set to <code>Level.INFO</code>. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public synchronized void reset() throws SecurityException + { + /* Throw a SecurityException if the caller does not have the + * permission to control the logging infrastructure. + */ + checkAccess(); + + properties = new Properties(); + + Iterator iter = loggers.values().iterator(); + while (iter.hasNext()) + { + WeakReference ref; + Logger logger; + + ref = (WeakReference) iter.next(); + if (ref != null) + { + logger = (Logger) ref.get(); + + if (logger == null) + iter.remove(); + else if (logger != rootLogger) + { + logger.resetLogger(); + logger.setLevel(null); + } + } + } + + rootLogger.setLevel(Level.INFO); + rootLogger.resetLogger(); + } + + /** + * Configures the logging framework by reading a configuration file. + * The name and location of this file are specified by the system + * property <code>java.util.logging.config.file</code>. If this + * property is not set, the URL + * "{gnu.classpath.home.url}/logging.properties" is taken, where + * "{gnu.classpath.home.url}" stands for the value of the system + * property <code>gnu.classpath.home.url</code>. + * + * <p>The task of configuring the framework is then delegated to + * {@link #readConfiguration(java.io.InputStream)}, which will + * notify registered listeners after having read the properties. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure, or if the caller is + * not granted the permission to read the configuration + * file. + * + * @throws IOException if there is a problem reading in the + * configuration file. + */ + public synchronized void readConfiguration() + throws IOException, SecurityException + { + String path; + InputStream inputStream; + + path = System.getProperty("java.util.logging.config.file"); + if ((path == null) || (path.length() == 0)) + { + String url = (System.getProperty("gnu.classpath.home.url") + + "/logging.properties"); + inputStream = new URL(url).openStream(); + } + else + inputStream = new java.io.FileInputStream(path); + + try + { + readConfiguration(inputStream); + } + finally + { + /* Close the stream in order to save + * resources such as file descriptors. + */ + inputStream.close(); + } + } + + public synchronized void readConfiguration(InputStream inputStream) + throws IOException, SecurityException + { + Properties newProperties; + Enumeration keys; + + checkAccess(); + newProperties = new Properties(); + newProperties.load(inputStream); + reset(); + this.properties = newProperties; + keys = newProperties.propertyNames(); + + while (keys.hasMoreElements()) + { + String key = ((String) keys.nextElement()).trim(); + String value = newProperties.getProperty(key); + + if (value == null) + continue; + + value = value.trim(); + + if ("handlers".equals(key)) + { + StringTokenizer tokenizer = new StringTokenizer(value); + while (tokenizer.hasMoreTokens()) + { + String handlerName = tokenizer.nextToken(); + try + { + Class handlerClass = ClassLoader.getSystemClassLoader().loadClass(handlerName); + getLogger("").addHandler((Handler) handlerClass + .newInstance()); + } + catch (ClassCastException ex) + { + System.err.println("[LogManager] class " + handlerName + + " is not subclass of java.util.logging.Handler"); + } + catch (Exception ex) + { + //System.out.println("[LogManager.readConfiguration]"+ex); + } + } + } + + if (key.endsWith(".level")) + { + String loggerName = key.substring(0, key.length() - 6); + Logger logger = getLogger(loggerName); + + if (logger == null) + { + logger = Logger.getLogger(loggerName); + addLogger(logger); + } + try + { + logger.setLevel(Level.parse(value)); + } + catch (Exception _) + { + //System.out.println("[LogManager.readConfiguration] "+_); + } + continue; + } + } + + /* The API specification does not talk about the + * property name that is distributed with the + * PropertyChangeEvent. With test code, it could + * be determined that the Sun J2SE 1.4 reference + * implementation uses null for the property name. + */ + pcs.firePropertyChange(null, null, null); + } + + /** + * Returns the value of a configuration property as a String. + */ + public synchronized String getProperty(String name) + { + if (properties != null) + return properties.getProperty(name); + else + return null; + } + + /** + * Returns the value of a configuration property as an integer. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is <em>not</em> specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is not an integer + * number. + */ + static int getIntProperty(String name, int defaultValue) + { + try + { + return Integer.parseInt(getLogManager().getProperty(name)); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as an integer, + * provided it is inside the acceptable range. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is <em>not</em> specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param minValue the lowest acceptable value. + * + * @param maxValue the highest acceptable value. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is not an integer + * number, or if it is less than the minimum value, + * or if it is greater than the maximum value. + */ + static int getIntPropertyClamped(String name, int defaultValue, + int minValue, int maxValue) + { + int val = getIntProperty(name, defaultValue); + if ((val < minValue) || (val > maxValue)) + val = defaultValue; + return val; + } + + /** + * Returns the value of a configuration property as a boolean. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is <em>not</em> specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is neither + * <code>"true"</code> nor <code>"false"</code>. + */ + static boolean getBooleanProperty(String name, boolean defaultValue) + { + try + { + return (new Boolean(getLogManager().getProperty(name))).booleanValue(); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as a Level. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is <em>not</em> specified in the + * logging API. + * + * @param propertyName the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if + * {@link Level.parse(java.lang.String)} does not like + * the property value. + */ + static Level getLevelProperty(String propertyName, Level defaultValue) + { + try + { + return Level.parse(getLogManager().getProperty(propertyName)); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as a Class. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is <em>not</em> specified in the + * logging API. + * + * @param propertyName the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if it does not specify + * the name of a loadable class. + */ + static final Class getClassProperty(String propertyName, Class defaultValue) + { + Class usingClass = null; + + try + { + String propertyValue = logManager.getProperty(propertyName); + if (propertyValue != null) + usingClass = Class.forName(propertyValue); + if (usingClass != null) + return usingClass; + } + catch (Exception _) + { + } + + return defaultValue; + } + + static final Object getInstanceProperty(String propertyName, Class ofClass, + Class defaultClass) + { + Class klass = getClassProperty(propertyName, defaultClass); + if (klass == null) + return null; + + try + { + Object obj = klass.newInstance(); + if (ofClass.isInstance(obj)) + return obj; + } + catch (Exception _) + { + } + + if (defaultClass == null) + return null; + + try + { + return defaultClass.newInstance(); + } + catch (java.lang.InstantiationException ex) + { + throw new RuntimeException(ex.getMessage()); + } + catch (java.lang.IllegalAccessException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + /** + * An instance of <code>LoggingPermission("control")</code> + * that is shared between calls to <code>checkAccess()</code>. + */ + private static final LoggingPermission controlPermission = new LoggingPermission("control", + null); + + /** + * Checks whether the current security context allows changing + * the configuration of the logging framework. For the security + * context to be trusted, it has to be granted + * a LoggingPermission("control"). + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public void checkAccess() throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(controlPermission); + } + + /** + * Creates a new instance of a class specified by name. + * + * @param className the name of the class of which a new instance + * should be created. + * + * @param ofClass the class to which the new instance should + * be either an instance or an instance of a subclass. + * FIXME: This description is just terrible. + * + * @return the new instance, or <code>null</code> if + * <code>className</code> is <code>null</code>, if no class + * with that name could be found, if there was an error + * loading that class, or if the constructor of the class + * has thrown an exception. + */ + static final Object createInstance(String className, Class ofClass) + { + Class klass; + + if ((className == null) || (className.length() == 0)) + return null; + + try + { + klass = Class.forName(className); + if (! ofClass.isAssignableFrom(klass)) + return null; + + return klass.newInstance(); + } + catch (Exception _) + { + return null; + } + catch (java.lang.LinkageError _) + { + return null; + } + } +} diff --git a/libjava/classpath/java/util/logging/LogRecord.java b/libjava/classpath/java/util/logging/LogRecord.java new file mode 100644 index 00000000000..af7f2058dbe --- /dev/null +++ b/libjava/classpath/java/util/logging/LogRecord.java @@ -0,0 +1,672 @@ +/* LogRecord.java -- + A class for the state associated with individual logging events + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.util.ResourceBundle; + + +/** + * A <code>LogRecord</code> contains the state for an individual + * event to be logged. + * + * <p>As soon as a LogRecord instance has been handed over to the + * logging framework, applications should not manipulate it anymore. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class LogRecord + implements java.io.Serializable +{ + /** + * The severity level of this <code>LogRecord</code>. + */ + private Level level; + + + /** + * The sequence number of this <code>LogRecord</code>. + */ + private long sequenceNumber; + + + /** + * The name of the class that issued the logging request, or + * <code>null</code> if this information could not be obtained. + */ + private String sourceClassName; + + + /** + * The name of the method that issued the logging request, or + * <code>null</code> if this information could not be obtained. + */ + private String sourceMethodName; + + + /** + * The message for this <code>LogRecord</code> before + * any localization or formatting. + */ + private String message; + + + /** + * An identifier for the thread in which this <code>LogRecord</code> + * was created. The identifier is not necessarily related to any + * thread identifiers used by the operating system. + */ + private int threadID; + + + /** + * The time when this <code>LogRecord</code> was created, + * in milliseconds since the beginning of January 1, 1970. + */ + private long millis; + + + /** + * The Throwable associated with this <code>LogRecord</code>, or + * <code>null</code> if the logged event is not related to an + * exception or error. + */ + private Throwable thrown; + + + /** + * The name of the logger where this <code>LogRecord</code> has + * originated, or <code>null</code> if this <code>LogRecord</code> + * does not originate from a <code>Logger</code>. + */ + private String loggerName; + + + /** + * The name of the resource bundle used for localizing log messages, + * or <code>null</code> if no bundle has been specified. + */ + private String resourceBundleName; + + private transient Object[] parameters; + + private transient ResourceBundle bundle; + + + /** + * Constructs a <code>LogRecord</code> given a severity level and + * an unlocalized message text. In addition, the sequence number, + * creation time (as returned by <code>getMillis()</code>) and + * thread ID are assigned. All other properties are set to + * <code>null</code>. + * + * @param level the severity level, for example <code>Level.WARNING</code>. + * + * @param message the message text (which will be used as key + * for looking up the localized message text + * if a resource bundle has been associated). + */ + public LogRecord(Level level, String message) + { + this.level = level; + this.message = message; + this.millis = System.currentTimeMillis(); + + /* A subclass of java.lang.Thread could override hashCode(), + * in which case the result would not be guaranteed anymore + * to be unique among all threads. While System.identityHashCode + * is not necessarily unique either, it at least cannot be + * overridden by user code. However, is might be a good idea + * to use something better for generating thread IDs. + */ + this.threadID = System.identityHashCode(Thread.currentThread()); + + sequenceNumber = allocateSeqNum(); + } + + + /** + * Determined with the serialver tool of the Sun J2SE 1.4. + */ + static final long serialVersionUID = 5372048053134512534L; + + private void readObject(java.io.ObjectInputStream in) + throws java.io.IOException, java.lang.ClassNotFoundException + { + in.defaultReadObject(); + + /* We assume that future versions will be downwards compatible, + * so we can ignore the versions. + */ + byte majorVersion = in.readByte(); + byte minorVersion = in.readByte(); + + int numParams = in.readInt(); + if (numParams >= 0) + { + parameters = new Object[numParams]; + for (int i = 0; i < numParams; i++) + parameters[i] = in.readObject(); + } + } + + + /** + * @serialData The default fields, followed by a major byte version + * number, followed by a minor byte version number, followed by + * information about the log record parameters. If + * <code>parameters</code> is <code>null</code>, the integer -1 is + * written, otherwise the length of the <code>parameters</code> + * array (which can be zero), followed by the result of calling + * {@link Object#toString() toString()} on the parameter (or + * <code>null</code> if the parameter is <code>null</code>). + * + * <p><strong>Specification Note:</strong> The Javadoc for the + * Sun reference implementation does not specify the version + * number. FIXME: Reverse-engineer the JDK and file a bug + * report with Sun, asking for amendment of the specification. + */ + private void writeObject(java.io.ObjectOutputStream out) + throws java.io.IOException + { + out.defaultWriteObject(); + + /* Major, minor version number: The Javadoc for J2SE1.4 does not + * specify the values. + */ + out.writeByte(0); + out.writeByte(0); + + if (parameters == null) + out.writeInt(-1); + else + { + out.writeInt(parameters.length); + for (int i = 0; i < parameters.length; i++) + { + if (parameters[i] == null) + out.writeObject(null); + else + out.writeObject(parameters[i].toString()); + } + } + } + + + /** + * Returns the name of the logger where this <code>LogRecord</code> + * has originated. + * + * @return the name of the source {@link Logger}, or + * <code>null</code> if this <code>LogRecord</code> + * does not originate from a <code>Logger</code>. + */ + public String getLoggerName() + { + return loggerName; + } + + + /** + * Sets the name of the logger where this <code>LogRecord</code> + * has originated. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param name the name of the source logger, or <code>null</code> to + * indicate that this <code>LogRecord</code> does not + * originate from a <code>Logger</code>. + */ + public void setLoggerName(String name) + { + loggerName = name; + } + + + /** + * Returns the resource bundle that is used when the message + * of this <code>LogRecord</code> needs to be localized. + * + * @return the resource bundle used for localization, + * or <code>null</code> if this message does not need + * to be localized. + */ + public ResourceBundle getResourceBundle() + { + return bundle; + } + + + /** + * Sets the resource bundle that is used when the message + * of this <code>LogRecord</code> needs to be localized. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param bundle the resource bundle to be used, or + * <code>null</code> to indicate that this + * message does not need to be localized. + */ + public void setResourceBundle(ResourceBundle bundle) + { + this.bundle = bundle; + + /* FIXME: Is there a way to infer the name + * of a resource bundle from a ResourceBundle object? + */ + this.resourceBundleName = null; + } + + + /** + * Returns the name of the resource bundle that is used when the + * message of this <code>LogRecord</code> needs to be localized. + * + * @return the name of the resource bundle used for localization, + * or <code>null</code> if this message does not need + * to be localized. + */ + public String getResourceBundleName() + { + return resourceBundleName; + } + + + /** + * Sets the name of the resource bundle that is used when the + * message of this <code>LogRecord</code> needs to be localized. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param name the name of the resource bundle to be used, or + * <code>null</code> to indicate that this message + * does not need to be localized. + */ + public void setResourceBundleName(String name) + { + resourceBundleName = name; + bundle = null; + + try + { + if (resourceBundleName != null) + bundle = ResourceBundle.getBundle(resourceBundleName); + } + catch (java.util.MissingResourceException _) + { + } + } + + + /** + * Returns the level of the LogRecord. + * + * <p>Applications should be aware of the possibility that the + * result is not necessarily one of the standard logging levels, + * since the logging framework allows to create custom subclasses + * of <code>java.util.logging.Level</code>. Therefore, filters + * should perform checks like <code>theRecord.getLevel().intValue() + * == Level.INFO.intValue()</code> instead of <code>theRecord.getLevel() + * == Level.INFO</code>. + */ + public Level getLevel() + { + return level; + } + + + /** + * Sets the severity level of this <code>LogRecord</code> to a new + * value. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param level the new severity level, for example + * <code>Level.WARNING</code>. + */ + public void setLevel(Level level) + { + this.level = level; + } + + + /** + * The last used sequence number for any LogRecord. + */ + private static long lastSeqNum; + + + /** + * Allocates a sequence number for a new LogRecord. This class + * method is only called by the LogRecord constructor. + */ + private static synchronized long allocateSeqNum() + { + lastSeqNum += 1; + return lastSeqNum; + } + + + /** + * Returns the sequence number of this <code>LogRecord</code>. + */ + public long getSequenceNumber() + { + return sequenceNumber; + } + + + /** + * Sets the sequence number of this <code>LogRecord</code> to a new + * value. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param seqNum the new sequence number. + */ + public void setSequenceNumber(long seqNum) + { + this.sequenceNumber = seqNum; + } + + + /** + * Returns the name of the class where the event being logged + * has had its origin. This information can be passed as + * parameter to some logging calls, and in certain cases, the + * logging framework tries to determine an approximation + * (which may or may not be accurate). + * + * @return the name of the class that issued the logging request, + * or <code>null</code> if this information could not + * be obtained. + */ + public String getSourceClassName() + { + if (sourceClassName != null) + return sourceClassName; + + /* FIXME: Should infer this information from the call stack. */ + return null; + } + + + /** + * Sets the name of the class where the event being logged + * has had its origin. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param sourceClassName the name of the class that issued the + * logging request, or <code>null</code> to indicate that + * this information could not be obtained. + */ + public void setSourceClassName(String sourceClassName) + { + this.sourceClassName = sourceClassName; + } + + + /** + * Returns the name of the method where the event being logged + * has had its origin. This information can be passed as + * parameter to some logging calls, and in certain cases, the + * logging framework tries to determine an approximation + * (which may or may not be accurate). + * + * @return the name of the method that issued the logging request, + * or <code>null</code> if this information could not + * be obtained. + */ + public String getSourceMethodName() + { + if (sourceMethodName != null) + return sourceMethodName; + + /* FIXME: Should infer this information from the call stack. */ + return null; + } + + + /** + * Sets the name of the method where the event being logged + * has had its origin. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param sourceMethodName the name of the method that issued the + * logging request, or <code>null</code> to indicate that + * this information could not be obtained. + */ + public void setSourceMethodName(String sourceMethodName) + { + this.sourceMethodName = sourceMethodName; + } + + + /** + * Returns the message for this <code>LogRecord</code> before + * any localization or parameter substitution. + * + * <p>A {@link Logger} will try to localize the message + * if a resource bundle has been associated with this + * <code>LogRecord</code>. In this case, the logger will call + * <code>getMessage()</code> and use the result as the key + * for looking up the localized message in the bundle. + * If no bundle has been associated, or if the result of + * <code>getMessage()</code> is not a valid key in the + * bundle, the logger will use the raw message text as + * returned by this method. + * + * @return the message text, or <code>null</code> if there + * is no message text. + */ + public String getMessage() + { + return message; + } + + + /** + * Sets the message for this <code>LogRecord</code>. + * + * <p>A <code>Logger</code> will try to localize the message + * if a resource bundle has been associated with this + * <code>LogRecord</code>. In this case, the logger will call + * <code>getMessage()</code> and use the result as the key + * for looking up the localized message in the bundle. + * If no bundle has been associated, or if the result of + * <code>getMessage()</code> is not a valid key in the + * bundle, the logger will use the raw message text as + * returned by this method. + * + * <p>It is possible to set the message to either an empty String or + * <code>null</code>, although this does not make the the message + * very helpful to human users. + * + * @param message the message text (which will be used as key + * for looking up the localized message text + * if a resource bundle has been associated). + */ + public void setMessage(String message) + { + this.message = message; + } + + + /** + * Returns the parameters to the log message. + * + * @return the parameters to the message, or <code>null</code> if + * the message has no parameters. + */ + public Object[] getParameters() + { + return parameters; + } + + + /** + * Sets the parameters to the log message. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param parameters the parameters to the message, or <code>null</code> + * to indicate that the message has no parameters. + */ + public void setParameters(Object[] parameters) + { + this.parameters = parameters; + } + + + /** + * Returns an identifier for the thread in which this + * <code>LogRecord</code> was created. The identifier is not + * necessarily related to any thread identifiers used by the + * operating system. + * + * @return an identifier for the source thread. + */ + public int getThreadID() + { + return threadID; + } + + + /** + * Sets the identifier indicating in which thread this + * <code>LogRecord</code> was created. The identifier is not + * necessarily related to any thread identifiers used by the + * operating system. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param threadID the identifier for the source thread. + */ + public void setThreadID(int threadID) + { + this.threadID = threadID; + } + + + /** + * Returns the time when this <code>LogRecord</code> was created. + * + * @return the time of creation in milliseconds since the beginning + * of January 1, 1970. + */ + public long getMillis() + { + return millis; + } + + + /** + * Sets the time when this <code>LogRecord</code> was created. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param millis the time of creation in milliseconds since the + * beginning of January 1, 1970. + */ + public void setMillis(long millis) + { + this.millis = millis; + } + + + /** + * Returns the Throwable associated with this <code>LogRecord</code>, + * or <code>null</code> if the logged event is not related to an exception + * or error. + */ + public Throwable getThrown() + { + return thrown; + } + + + /** + * Associates this <code>LogRecord</code> with an exception or error. + * + * <p>As soon as a <code>LogRecord</code> has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param thrown the exception or error to associate with, or + * <code>null</code> if this <code>LogRecord</code> + * should be made unrelated to an exception or error. + */ + public void setThrown(Throwable thrown) + { + this.thrown = thrown; + } +} diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java new file mode 100644 index 00000000000..ae985a93cbc --- /dev/null +++ b/libjava/classpath/java/util/logging/Logger.java @@ -0,0 +1,1199 @@ +/* Logger.java -- a class for logging messages + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * A Logger is used for logging information about events. Usually, there + * is a seprate logger for each subsystem or component, although there + * is a shared instance for components that make only occasional use of + * the logging framework. + * + * <p>It is common to name a logger after the name of a corresponding + * Java package. Loggers are organized into a hierarchical namespace; + * for example, the logger <code>"org.gnu.foo"</code> is the + * <em>parent</em> of logger <code>"org.gnu.foo.bar"</code>. + * + * <p>A logger for a named subsystem can be obtained through {@link + * java.util.logging.Logger#getLogger(java.lang.String)}. However, + * only code which has been granted the permission to control the + * logging infrastructure will be allowed to customize that logger. + * Untrusted code can obtain a private, anonymous logger through + * {@link #getAnonymousLogger()} if it wants to perform any + * modifications to the logger. + * + * <p>FIXME: Write more documentation. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class Logger +{ + /** + * A logger provided to applications that make only occasional use + * of the logging framework, typically early prototypes. Serious + * products are supposed to create and use their own Loggers, so + * they can be controlled individually. + */ + public static final Logger global = getLogger("global"); + + + /** + * The name of the Logger, or <code>null</code> if the logger is + * anonymous. + * + * <p>A previous version of the GNU Classpath implementation granted + * untrusted code the permission to control any logger whose name + * was null. However, test code revealed that the Sun J2SE 1.4 + * reference implementation enforces the security control for any + * logger that was not created through getAnonymousLogger, even if + * it has a null name. Therefore, a separate flag {@link + * Logger#anonymous} was introduced. + */ + private final String name; + + + /** + * The name of the resource bundle used for localization. + * + * <p>This variable cannot be declared as <code>final</code> + * because its value can change as a result of calling + * getLogger(String,String). + */ + private String resourceBundleName; + + + /** + * The resource bundle used for localization. + * + * <p>This variable cannot be declared as <code>final</code> + * because its value can change as a result of calling + * getLogger(String,String). + */ + private ResourceBundle resourceBundle; + + private Filter filter; + + private final List handlerList = new java.util.ArrayList(4); + private Handler[] handlers = new Handler[0]; + + /** + * Indicates whether or not this logger is anonymous. While + * a LoggingPermission is required for any modifications to + * a normal logger, untrusted code can obtain an anonymous logger + * and modify it according to its needs. + * + * <p>A previous version of the GNU Classpath implementation + * granted access to every logger whose name was null. + * However, test code revealed that the Sun J2SE 1.4 reference + * implementation enforces the security control for any logger + * that was not created through getAnonymousLogger, even + * if it has a null name. + */ + private boolean anonymous; + + + private boolean useParentHandlers; + + private Level level; + + private Logger parent; + + /** + * Constructs a Logger for a subsystem. Most applications do not + * need to create new Loggers explicitly; instead, they should call + * the static factory methods + * {@link #getLogger(java.lang.String,java.lang.String) getLogger} + * (with ResourceBundle for localization) or + * {@link #getLogger(java.lang.String) getLogger} (without + * ResourceBundle), respectively. + * + * @param name the name for the logger, for example "java.awt" + * or "com.foo.bar". The name should be based on + * the name of the package issuing log records + * and consist of dot-separated Java identifiers. + * + * @param resourceBundleName the name of a resource bundle + * for localizing messages, or <code>null</code> + * to indicate that messages do not need to be localized. + * + * @throws java.util.MissingResourceException if + * <code>resourceBundleName</code> is not <code>null</code> + * and no such bundle could be located. + */ + protected Logger(String name, String resourceBundleName) + throws MissingResourceException + { + this.name = name; + this.resourceBundleName = resourceBundleName; + + if (resourceBundleName == null) + resourceBundle = null; + else + resourceBundle = ResourceBundle.getBundle(resourceBundleName); + + level = null; + + /* This is null when the root logger is being constructed, + * and the root logger afterwards. + */ + parent = LogManager.getLogManager().rootLogger; + + useParentHandlers = (parent != null); + } + + + + /** + * Finds a registered logger for a subsystem, or creates one in + * case no logger has been registered yet. + * + * @param name the name for the logger, for example "java.awt" + * or "com.foo.bar". The name should be based on + * the name of the package issuing log records + * and consist of dot-separated Java identifiers. + * + * @throws IllegalArgumentException if a logger for the subsystem + * identified by <code>name</code> has already been created, + * but uses a a resource bundle for localizing messages. + * + * @throws NullPointerException if <code>name</code> is + * <code>null</code>. + * + * @return a logger for the subsystem specified by <code>name</code> + * that does not localize messages. + */ + public static Logger getLogger(String name) + { + return getLogger(name, null); + } + + + /** + * Finds a registered logger for a subsystem, or creates one in case + * no logger has been registered yet. + * + * <p>If a logger with the specified name has already been + * registered, the behavior depends on the resource bundle that is + * currently associated with the existing logger. + * + * <ul><li>If the existing logger uses the same resource bundle as + * specified by <code>resourceBundleName</code>, the existing logger + * is returned.</li> + * + * <li>If the existing logger currently does not localize messages, + * the existing logger is modified to use the bundle specified by + * <code>resourceBundleName</code>. The existing logger is then + * returned. Therefore, all subsystems currently using this logger + * will produce localized messages from now on.</li> + * + * <li>If the existing logger already has an associated resource + * bundle, but a different one than specified by + * <code>resourceBundleName</code>, an + * <code>IllegalArgumentException</code> is thrown.</li></ul> + * + * @param name the name for the logger, for example "java.awt" + * or "org.gnu.foo". The name should be based on + * the name of the package issuing log records + * and consist of dot-separated Java identifiers. + * + * @param resourceBundleName the name of a resource bundle + * for localizing messages, or <code>null</code> + * to indicate that messages do not need to be localized. + * + * @return a logger for the subsystem specified by <code>name</code>. + * + * @throws java.util.MissingResourceException if + * <code>resourceBundleName</code> is not <code>null</code> + * and no such bundle could be located. + * + * @throws IllegalArgumentException if a logger for the subsystem + * identified by <code>name</code> has already been created, + * but uses a different resource bundle for localizing + * messages. + * + * @throws NullPointerException if <code>name</code> is + * <code>null</code>. + */ + public static Logger getLogger(String name, String resourceBundleName) + { + LogManager lm = LogManager.getLogManager(); + Logger result; + + /* Throw NullPointerException if name is null. */ + name.getClass(); + + /* Without synchronized(lm), it could happen that another thread + * would create a logger between our calls to getLogger and + * addLogger. While addLogger would indicate this by returning + * false, we could not be sure that this other logger was still + * existing when we called getLogger a second time in order + * to retrieve it -- note that LogManager is only allowed to + * keep weak references to registered loggers, so Loggers + * can be garbage collected at any time in general, and between + * our call to addLogger and our second call go getLogger + * in particular. + * + * Of course, we assume here that LogManager.addLogger etc. + * are synchronizing on the global LogManager object. There + * is a comment in the implementation of LogManager.addLogger + * referring to this comment here, so that any change in + * the synchronization of LogManager will be reflected here. + */ + synchronized (lm) + { + result = lm.getLogger(name); + if (result == null) + { + boolean couldBeAdded; + + result = new Logger(name, resourceBundleName); + couldBeAdded = lm.addLogger(result); + if (!couldBeAdded) + throw new IllegalStateException("cannot register new logger"); + } + else + { + /* The logger already exists. Make sure it uses + * the same resource bundle for localizing messages. + */ + String existingBundleName = result.getResourceBundleName(); + + /* The Sun J2SE 1.4 reference implementation will return the + * registered logger object, even if it does not have a resource + * bundle associated with it. However, it seems to change the + * resourceBundle of the registered logger to the bundle + * whose name was passed to getLogger. + */ + if ((existingBundleName == null) && (resourceBundleName != null)) + { + /* If ResourceBundle.getBundle throws an exception, the + * existing logger will be unchanged. This would be + * different if the assignment to resourceBundleName + * came first. + */ + result.resourceBundle = ResourceBundle.getBundle(resourceBundleName); + result.resourceBundleName = resourceBundleName; + return result; + } + + if ((existingBundleName != resourceBundleName) + && ((existingBundleName == null) + || !existingBundleName.equals(resourceBundleName))) + { + throw new IllegalArgumentException(); + } + } + } + + return result; + } + + + /** + * Creates a new, unnamed logger. Unnamed loggers are not + * registered in the namespace of the LogManager, and no special + * security permission is required for changing their state. + * Therefore, untrusted applets are able to modify their private + * logger instance obtained through this method. + * + * <p>The parent of the newly created logger will the the root + * logger, from which the level threshold and the handlers are + * inherited. + */ + public static Logger getAnonymousLogger() + { + return getAnonymousLogger(null); + } + + + /** + * Creates a new, unnamed logger. Unnamed loggers are not + * registered in the namespace of the LogManager, and no special + * security permission is required for changing their state. + * Therefore, untrusted applets are able to modify their private + * logger instance obtained through this method. + * + * <p>The parent of the newly created logger will the the root + * logger, from which the level threshold and the handlers are + * inherited. + * + * @param resourceBundleName the name of a resource bundle + * for localizing messages, or <code>null</code> + * to indicate that messages do not need to be localized. + * + * @throws java.util.MissingResourceException if + * <code>resourceBundleName</code> is not <code>null</code> + * and no such bundle could be located. + */ + public static Logger getAnonymousLogger(String resourceBundleName) + throws MissingResourceException + { + Logger result; + + result = new Logger(null, resourceBundleName); + result.anonymous = true; + return result; + } + + + /** + * Returns the name of the resource bundle that is being used for + * localizing messages. + * + * @return the name of the resource bundle used for localizing messages, + * or <code>null</code> if the parent's resource bundle + * is used for this purpose. + */ + public synchronized String getResourceBundleName() + { + return resourceBundleName; + } + + + /** + * Returns the resource bundle that is being used for localizing + * messages. + * + * @return the resource bundle used for localizing messages, + * or <code>null</code> if the parent's resource bundle + * is used for this purpose. + */ + public synchronized ResourceBundle getResourceBundle() + { + return resourceBundle; + } + + + /** + * Returns the severity level threshold for this <code>Handler</code>. + * All log records with a lower severity level will be discarded; + * a log record of the same or a higher level will be published + * unless an installed <code>Filter</code> decides to discard it. + * + * @return the severity level below which all log messages will be + * discarded, or <code>null</code> if the logger inherits + * the threshold from its parent. + */ + public synchronized Level getLevel() + { + return level; + } + + + /** + * Returns whether or not a message of the specified level + * would be logged by this logger. + * + * @throws NullPointerException if <code>level</code> + * is <code>null</code>. + */ + public synchronized boolean isLoggable(Level level) + { + if (this.level != null) + return this.level.intValue() <= level.intValue(); + + if (parent != null) + return parent.isLoggable(level); + else + return false; + } + + + /** + * Sets the severity level threshold for this <code>Handler</code>. + * All log records with a lower severity level will be discarded + * immediately. A log record of the same or a higher level will be + * published unless an installed <code>Filter</code> decides to + * discard it. + * + * @param level the severity level below which all log messages + * will be discarded, or <code>null</code> to + * indicate that the logger should inherit the + * threshold from its parent. + * + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted + * the permission to control the logging infrastructure by + * having LoggingPermission("control"). Untrusted code can + * obtain an anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public synchronized void setLevel(Level level) + { + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + this.level = level; + } + + + public synchronized Filter getFilter() + { + return filter; + } + + + /** + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted + * the permission to control the logging infrastructure by + * having LoggingPermission("control"). Untrusted code can + * obtain an anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public synchronized void setFilter(Filter filter) + throws SecurityException + { + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + this.filter = filter; + } + + + + + /** + * Returns the name of this logger. + * + * @return the name of this logger, or <code>null</code> if + * the logger is anonymous. + */ + public String getName() + { + /* Note that the name of a logger cannot be changed during + * its lifetime, so no synchronization is needed. + */ + return name; + } + + + /** + * Passes a record to registered handlers, provided the record + * is considered as loggable both by {@link #isLoggable(Level)} + * and a possibly installed custom {@link #setFilter(Filter) filter}. + * + * <p>If the logger has been configured to use parent handlers, + * the record will be forwarded to the parent of this logger + * in addition to being processed by the handlers registered with + * this logger. + * + * <p>The other logging methods in this class are convenience methods + * that merely create a new LogRecord and pass it to this method. + * Therefore, subclasses usually just need to override this single + * method for customizing the logging behavior. + * + * @param record the log record to be inspected and possibly forwarded. + */ + public synchronized void log(LogRecord record) + { + if (!isLoggable(record.getLevel())) + return; + + if ((filter != null) && !filter.isLoggable(record)) + return; + + /* If no logger name has been set for the log record, + * use the name of this logger. + */ + if (record.getLoggerName() == null) + record.setLoggerName(name); + + /* Avoid that some other thread is changing the logger hierarchy + * while we are traversing it. + */ + synchronized (LogManager.getLogManager()) + { + Logger curLogger = this; + + do + { + /* The Sun J2SE 1.4 reference implementation seems to call the + * filter only for the logger whose log method is called, + * never for any of its parents. Also, parent loggers publish + * log record whatever their level might be. This is pretty + * weird, but GNU Classpath tries to be as compatible as + * possible to the reference implementation. + */ + for (int i = 0; i < curLogger.handlers.length; i++) + curLogger.handlers[i].publish(record); + + if (curLogger.getUseParentHandlers() == false) + break; + + curLogger = curLogger.getParent(); + } + while (parent != null); + } + } + + + public void log(Level level, String message) + { + log(level, message, (Object[]) null); + } + + + public synchronized void log(Level level, + String message, + Object param) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, + caller != null ? caller.getClassName() : "<unknown>", + caller != null ? caller.getMethodName() : "<unknown>", + message, + param); + } + + + public synchronized void log(Level level, + String message, + Object[] params) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, + caller != null ? caller.getClassName() : "<unknown>", + caller != null ? caller.getMethodName() : "<unknown>", + message, + params); + } + + + public synchronized void log(Level level, + String message, + Throwable thrown) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, + caller != null ? caller.getClassName() : "<unknown>", + caller != null ? caller.getMethodName() : "<unknown>", + message, + thrown); + } + + + public synchronized void logp(Level level, + String sourceClass, + String sourceMethod, + String message) + { + logp(level, sourceClass, sourceMethod, message, + (Object[]) null); + } + + + public synchronized void logp(Level level, + String sourceClass, + String sourceMethod, + String message, + Object param) + { + logp(level, sourceClass, sourceMethod, message, + new Object[] { param }); + } + + + private synchronized ResourceBundle findResourceBundle() + { + if (resourceBundle != null) + return resourceBundle; + + if (parent != null) + return parent.findResourceBundle(); + + return null; + } + + + private synchronized void logImpl(Level level, + String sourceClass, + String sourceMethod, + String message, + Object[] params) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundle(findResourceBundle()); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setParameters(params); + + log(rec); + } + + + public synchronized void logp(Level level, + String sourceClass, + String sourceMethod, + String message, + Object[] params) + { + logImpl(level, sourceClass, sourceMethod, message, params); + } + + + public synchronized void logp(Level level, + String sourceClass, + String sourceMethod, + String message, + Throwable thrown) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundle(resourceBundle); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setThrown(thrown); + + log(rec); + } + + + public synchronized void logrb(Level level, + String sourceClass, + String sourceMethod, + String bundleName, + String message) + { + logrb(level, sourceClass, sourceMethod, bundleName, + message, (Object[]) null); + } + + + public synchronized void logrb(Level level, + String sourceClass, + String sourceMethod, + String bundleName, + String message, + Object param) + { + logrb(level, sourceClass, sourceMethod, bundleName, + message, new Object[] { param }); + } + + + public synchronized void logrb(Level level, + String sourceClass, + String sourceMethod, + String bundleName, + String message, + Object[] params) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundleName(bundleName); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setParameters(params); + + log(rec); + } + + + public synchronized void logrb(Level level, + String sourceClass, + String sourceMethod, + String bundleName, + String message, + Throwable thrown) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundleName(bundleName); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setThrown(thrown); + + log(rec); + } + + + public synchronized void entering(String sourceClass, + String sourceMethod) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "ENTRY"); + } + + + public synchronized void entering(String sourceClass, + String sourceMethod, + Object param) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param); + } + + + public synchronized void entering(String sourceClass, + String sourceMethod, + Object[] params) + { + if (isLoggable(Level.FINER)) + { + StringBuffer buf = new StringBuffer(80); + buf.append("ENTRY"); + for (int i = 0; i < params.length; i++) + { + buf.append(" {"); + buf.append(i); + buf.append('}'); + } + + logp(Level.FINER, sourceClass, sourceMethod, buf.toString(), params); + } + } + + + public synchronized void exiting(String sourceClass, + String sourceMethod) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "RETURN"); + } + + + public synchronized void exiting(String sourceClass, + String sourceMethod, + Object result) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result); + } + + + public synchronized void throwing(String sourceClass, + String sourceMethod, + Throwable thrown) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "THROW", thrown); + } + + + /** + * Logs a message with severity level SEVERE, indicating a serious + * failure that prevents normal program execution. Messages at this + * level should be understandable to an inexperienced, non-technical + * end user. Ideally, they explain in simple words what actions the + * user can take in order to resolve the problem. + * + * @see Level#SEVERE + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void severe(String message) + { + if (isLoggable(Level.SEVERE)) + log(Level.SEVERE, message); + } + + + /** + * Logs a message with severity level WARNING, indicating a + * potential problem that does not prevent normal program execution. + * Messages at this level should be understandable to an + * inexperienced, non-technical end user. Ideally, they explain in + * simple words what actions the user can take in order to resolve + * the problem. + * + * @see Level#WARNING + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void warning(String message) + { + if (isLoggable(Level.WARNING)) + log(Level.WARNING, message); + } + + + /** + * Logs a message with severity level INFO. {@link Level#INFO} is + * intended for purely informational messages that do not indicate + * error or warning situations. In the default logging + * configuration, INFO messages will be written to the system + * console. For this reason, the INFO level should be used only for + * messages that are important to end users and system + * administrators. Messages at this level should be understandable + * to an inexperienced, non-technical user. + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void info(String message) + { + if (isLoggable(Level.INFO)) + log(Level.INFO, message); + } + + + /** + * Logs a message with severity level CONFIG. {@link Level#CONFIG} is + * intended for static configuration messages, for example about the + * windowing environment, the operating system version, etc. + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource bundle. While + * it is possible to pass <code>null</code>, this is not + * recommended, since a logging message without text is unlikely + * to be helpful. + */ + public synchronized void config(String message) + { + if (isLoggable(Level.CONFIG)) + log(Level.CONFIG, message); + } + + + /** + * Logs a message with severity level FINE. {@link Level#FINE} is + * intended for messages that are relevant for developers using + * the component generating log messages. Examples include minor, + * recoverable failures, or possible inefficiencies. + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void fine(String message) + { + if (isLoggable(Level.FINE)) + log(Level.FINE, message); + } + + + /** + * Logs a message with severity level FINER. {@link Level#FINER} is + * intended for rather detailed tracing, for example entering a + * method, returning from a method, or throwing an exception. + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void finer(String message) + { + if (isLoggable(Level.FINER)) + log(Level.FINER, message); + } + + + /** + * Logs a message with severity level FINEST. {@link Level#FINEST} + * is intended for highly detailed tracing, for example reaching a + * certain point inside the body of a method. + * + * @param message the message text, also used as look-up key if the + * logger is localizing messages with a resource + * bundle. While it is possible to pass + * <code>null</code>, this is not recommended, since + * a logging message without text is unlikely to be + * helpful. + */ + public synchronized void finest(String message) + { + if (isLoggable(Level.FINEST)) + log(Level.FINEST, message); + } + + + /** + * Adds a handler to the set of handlers that get notified + * when a log record is to be published. + * + * @param handler the handler to be added. + * + * @throws NullPointerException if <code>handler</code> + * is <code>null</code>. + * + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted + * the permission to control the logging infrastructure by + * having LoggingPermission("control"). Untrusted code can + * obtain an anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public synchronized void addHandler(Handler handler) + throws SecurityException + { + /* Throw a new NullPointerException if handler is null. */ + handler.getClass(); + + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + if (!handlerList.contains(handler)) + { + handlerList.add(handler); + handlers = getHandlers(); + } + } + + + /** + * Removes a handler from the set of handlers that get notified + * when a log record is to be published. + * + * @param handler the handler to be removed. + * + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted the + * permission to control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method {@link + * #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + * + * @throws NullPointerException if <code>handler</code> + * is <code>null</code>. + */ + public synchronized void removeHandler(Handler handler) + throws SecurityException + { + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + /* Throw a new NullPointerException if handler is null. */ + handler.getClass(); + + handlerList.remove(handler); + handlers = getHandlers(); + } + + + /** + * Returns the handlers currently registered for this Logger. + * When a log record has been deemed as being loggable, + * it will be passed to all registered handlers for + * publication. In addition, if the logger uses parent handlers + * (see {@link #getUseParentHandlers() getUseParentHandlers} + * and {@link #setUseParentHandlers(boolean) setUseParentHandlers}, + * the log record will be passed to the parent's handlers. + */ + public synchronized Handler[] getHandlers() + { + /* We cannot return our internal handlers array + * because we do not have any guarantee that the + * caller would not change the array entries. + */ + return (Handler[]) handlerList.toArray(new Handler[handlerList.size()]); + } + + + /** + * Returns whether or not this Logger forwards log records to + * handlers registered for its parent loggers. + * + * @return <code>false</code> if this Logger sends log records + * merely to Handlers registered with itself; + * <code>true</code> if this Logger sends log records + * not only to Handlers registered with itself, but also + * to those Handlers registered with parent loggers. + */ + public synchronized boolean getUseParentHandlers() + { + return useParentHandlers; + } + + + /** + * Sets whether or not this Logger forwards log records to + * handlers registered for its parent loggers. + * + * @param useParentHandlers <code>false</code> to let this + * Logger send log records merely to Handlers registered + * with itself; <code>true</code> to let this Logger + * send log records not only to Handlers registered + * with itself, but also to those Handlers registered with + * parent loggers. + * + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted + * the permission to control the logging infrastructure by + * having LoggingPermission("control"). Untrusted code can + * obtain an anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + * + */ + public synchronized void setUseParentHandlers(boolean useParentHandlers) + { + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + this.useParentHandlers = useParentHandlers; + } + + + /** + * Returns the parent of this logger. By default, the parent is + * assigned by the LogManager by inspecting the logger's name. + * + * @return the parent of this logger (as detemined by the LogManager + * by inspecting logger names), the root logger if no other + * logger has a name which is a prefix of this logger's name, or + * <code>null</code> for the root logger. + */ + public synchronized Logger getParent() + { + return parent; + } + + + /** + * Sets the parent of this logger. Usually, applications do not + * call this method directly. Instead, the LogManager will ensure + * that the tree of loggers reflects the hierarchical logger + * namespace. Basically, this method should not be public at all, + * but the GNU implementation follows the API specification. + * + * @throws NullPointerException if <code>parent</code> is + * <code>null</code>. + * + * @throws SecurityException if this logger is not anonymous, a + * security manager exists, and the caller is not granted + * the permission to control the logging infrastructure by + * having LoggingPermission("control"). Untrusted code can + * obtain an anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public synchronized void setParent(Logger parent) + { + LogManager lm; + + /* Throw a new NullPointerException if parent is null. */ + parent.getClass(); + + lm = LogManager.getLogManager(); + + if (this == lm.rootLogger) + throw new IllegalArgumentException( + "only the root logger can have a null parent"); + + /* An application is allowed to control an anonymous logger + * without having the permission to control the logging + * infrastructure. + */ + if (!anonymous) + LogManager.getLogManager().checkAccess(); + + this.parent = parent; + } + + /** + * Gets the StackTraceElement of the first class that is not this class. + * That should be the initial caller of a logging method. + * @return caller of the initial logging method or null if unknown. + */ + private StackTraceElement getCallerStackFrame() + { + Throwable t = new Throwable(); + StackTraceElement[] stackTrace = t.getStackTrace(); + int index = 0; + + // skip to stackentries until this class + while(index < stackTrace.length + && !stackTrace[index].getClassName().equals(getClass().getName())) + index++; + + // skip the stackentries of this class + while(index < stackTrace.length + && stackTrace[index].getClassName().equals(getClass().getName())) + index++; + + return index < stackTrace.length ? stackTrace[index] : null; + } + + /** + * Reset and close handlers attached to this logger. This function is package + * private because it must only be avaiable to the LogManager. + */ + void resetLogger() + { + for (int i = 0; i < handlers.length; i++) + { + handlers[i].close(); + handlerList.remove(handlers[i]); + } + handlers = getHandlers(); + } +} diff --git a/libjava/classpath/java/util/logging/LoggingPermission.java b/libjava/classpath/java/util/logging/LoggingPermission.java new file mode 100644 index 00000000000..c7a2255ecea --- /dev/null +++ b/libjava/classpath/java/util/logging/LoggingPermission.java @@ -0,0 +1,73 @@ +/* LoggingPermission.java -- a class for logging permissions. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +public final class LoggingPermission + extends java.security.BasicPermission +{ + /** + * Creates a new LoggingPermission. + * + * @param name the name of the permission, which must be "control". + * + * @param actions the list of actions for the permission, which + * must be either <code>null</code> or an empty + * string. + * + * @exception IllegalArgumentException if <code>name</code> + * is not "control", or <code>actions</code> is + * neither <code>null</code> nor empty. + */ + public LoggingPermission(String name, String actions) + { + super("control", ""); + + if (!"control".equals(name)) + { + throw new IllegalArgumentException( + "name of LoggingPermission must be \"control\""); + } + + if ((actions != null) && (actions.length() != 0)) + { + throw new IllegalArgumentException( + "actions of LoggingPermissions must be null or empty"); + } + } +} diff --git a/libjava/classpath/java/util/logging/MemoryHandler.java b/libjava/classpath/java/util/logging/MemoryHandler.java new file mode 100644 index 00000000000..ffa589f1668 --- /dev/null +++ b/libjava/classpath/java/util/logging/MemoryHandler.java @@ -0,0 +1,345 @@ +/* MemoryHandler.java -- a class for buffering log messages in a memory buffer + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.logging; + +/** + * A <code>MemoryHandler</code> maintains a circular buffer of + * log records. + * + * <p><strong>Configuration:</strong> Values of the subsequent + * <code>LogManager</code> properties are taken into consideration + * when a <code>MemoryHandler</code> is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + * <ul> + * <li><code>java.util.MemoryHandler.level</code> - specifies + * the initial severity level threshold. Default value: + * <code>Level.ALL</code>.</li> + * <li><code>java.util.MemoryHandler.filter</code> - specifies + * the name of a Filter class. Default value: No Filter.</li> + * <li><code>java.util.MemoryHandler.size</code> - specifies the + * maximum number of log records that are kept in the circular + * buffer. Default value: 1000.</li> + * <li><code>java.util.MemoryHandler.push</code> - specifies the + * <code>pushLevel</code>. Default value: + * <code>Level.SEVERE</code>.</li> + * <li><code>java.util.MemoryHandler.target</code> - specifies the + * name of a subclass of {@link Handler} that will be used as the + * target handler. There is no default value for this property; + * if it is not set, the no-argument MemoryHandler constructor + * will throw an exception.</li> + * </ul> + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class MemoryHandler + extends Handler +{ + /** + * The storage area used for buffering the unpushed log records in + * memory. + */ + private final LogRecord[] buffer; + + + /** + * The current position in the circular buffer. For a new + * MemoryHandler, or immediately after {@link #push()} was called, + * the value of this variable is zero. Each call to {@link + * #publish(LogRecord)} will store the published LogRecord into + * <code>buffer[position]</code> before position is incremented by + * one. If position becomes greater than the size of the buffer, it + * is reset to zero. + */ + private int position; + + + /** + * The number of log records which have been published, but not + * pushed yet to the target handler. + */ + private int numPublished; + + + /** + * The push level threshold for this <code>Handler</code>. When a + * record is published whose severity level is greater than or equal + * to the <code>pushLevel</code> of this <code>MemoryHandler</code>, + * the {@link #push()} method will be invoked for pushing the buffer + * contents to the target <code>Handler</code>. + */ + private Level pushLevel; + + + /** + * The Handler to which log records are forwarded for actual + * publication. + */ + private final Handler target; + + + /** + * Constructs a <code>MemoryHandler</code> for keeping a circular + * buffer of LogRecords; the initial configuration is determined by + * the <code>LogManager</code> properties described above. + */ + public MemoryHandler() + { + this((Handler) LogManager.getInstanceProperty( + "java.util.logging.MemoryHandler.target", + Handler.class, /* default */ null), + LogManager.getIntPropertyClamped( + "java.util.logging.MemoryHandler.size", + /* default */ 1000, + /* minimum value */ 1, + /* maximum value */ Integer.MAX_VALUE), + LogManager.getLevelProperty( + "java.util.logging.MemoryHandler.push", + /* default push level */ Level.SEVERE)); + } + + + /** + * Constructs a <code>MemoryHandler</code> for keeping a circular + * buffer of LogRecords, given some parameters. The values of the + * other parameters are taken from LogManager properties, as + * described above. + * + * @param target the target handler that will receive those + * log records that are passed on for publication. + * + * @param size the number of log records that are kept in the buffer. + * The value must be a at least one. + * + * @param pushLevel the push level threshold for this + * <code>MemoryHandler</code>. When a record is published whose + * severity level is greater than or equal to + * <code>pushLevel</code>, the {@link #push()} method will be + * invoked in order to push the bufffer contents to + * <code>target</code>. + * + * @throws java.lang.IllegalArgumentException if <code>size</code> + * is negative or zero. The GNU implementation also throws + * an IllegalArgumentException if <code>target</code> or + * <code>pushLevel</code> are <code>null</code>, but the + * API specification does not prescribe what should happen + * in those cases. + */ + public MemoryHandler(Handler target, int size, Level pushLevel) + { + if ((target == null) || (size <= 0) || (pushLevel == null)) + throw new IllegalArgumentException(); + + buffer = new LogRecord[size]; + this.pushLevel = pushLevel; + this.target = target; + + setLevel(LogManager.getLevelProperty( + "java.util.logging.MemoryHandler.level", + /* default value */ Level.ALL)); + + setFilter((Filter) LogManager.getInstanceProperty( + "java.util.logging.MemoryHandler.filter", + /* must be instance of */ Filter.class, + /* default value */ null)); + } + + + /** + * Stores a <code>LogRecord</code> in a fixed-size circular buffer, + * provided the record passes all tests for being loggable. If the + * buffer is full, the oldest record will be discarded. + * + * <p>If the record has a severity level which is greater than or + * equal to the <code>pushLevel</code> of this + * <code>MemoryHandler</code>, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * <code>Handler</code>. + * + * <p>Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will create + * LogRecords and distribute them to registered handlers. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + if (!isLoggable(record)) + return; + + buffer[position] = record; + position = (position + 1) % buffer.length; + numPublished = numPublished + 1; + + if (record.getLevel().intValue() >= pushLevel.intValue()) + push(); + } + + + /** + * Pushes the contents of the memory buffer to the target + * <code>Handler</code> and clears the buffer. Note that + * the target handler will discard those records that do + * not satisfy its own severity level threshold, or that are + * not considered loggable by an installed {@link Filter}. + * + * <p>In case of an I/O failure, the {@link ErrorManager} of the + * target <code>Handler</code> will be notified, but the caller of + * this method will not receive an exception. + */ + public void push() + { + int i; + + if (numPublished < buffer.length) + { + for (i = 0; i < position; i++) + target.publish(buffer[i]); + } + else + { + for (i = position; i < buffer.length; i++) + target.publish(buffer[i]); + for (i = 0; i < position; i++) + target.publish(buffer[i]); + } + + numPublished = 0; + position = 0; + } + + + /** + * Forces any data that may have been buffered by the target + * <code>Handler</code> to the underlying output device, but + * does <em>not</em> push the contents of the circular memory + * buffer to the target handler. + * + * <p>In case of an I/O failure, the {@link ErrorManager} of the + * target <code>Handler</code> will be notified, but the caller of + * this method will not receive an exception. + * + * @see #push() + */ + public void flush() + { + target.flush(); + } + + + /** + * Closes this <code>MemoryHandler</code> and its associated target + * handler, discarding the contents of the memory buffer. However, + * any data that may have been buffered by the target + * <code>Handler</code> is forced to the underlying output device. + * + * <p>As soon as <code>close</code> has been called, + * a <code>Handler</code> should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * <code>Handler</code> in any other way may throw runtime + * exceptions after calling <code>close</code>.</p> + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> of + * the associated target <code>Handler</code> will be informed, but + * the caller of this method will not receive an exception.</p> + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @see #push() + */ + public void close() + throws SecurityException + { + push(); + + /* This will check for LoggingPermission("control"). If the + * current security context does not grant this permission, + * push() has been executed, but this does not impose a + * security risk. + */ + target.close(); + } + + + + /** + * Returns the push level threshold for this <code>Handler</code>. + * When a record is published whose severity level is greater + * than or equal to the <code>pushLevel</code> of this + * <code>MemoryHandler</code>, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * <code>Handler</code>. + * + * @return the push level threshold for automatic pushing. + */ + public Level getPushLevel() + { + return pushLevel; + } + + + /** + * Sets the push level threshold for this <code>Handler</code>. + * When a record is published whose severity level is greater + * than or equal to the <code>pushLevel</code> of this + * <code>MemoryHandler</code>, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * <code>Handler</code>. + * + * @param pushLevel the push level threshold for automatic pushing. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @exception NullPointerException if <code>pushLevel</code> is + * <code>null</code>. + */ + public void setPushLevel(Level pushLevel) + { + LogManager.getLogManager().checkAccess(); + + /* Throws a NullPointerException if pushLevel is null. */ + pushLevel.getClass(); + + this.pushLevel = pushLevel; + } +} diff --git a/libjava/classpath/java/util/logging/SimpleFormatter.java b/libjava/classpath/java/util/logging/SimpleFormatter.java new file mode 100644 index 00000000000..f7a442792f9 --- /dev/null +++ b/libjava/classpath/java/util/logging/SimpleFormatter.java @@ -0,0 +1,119 @@ +/* SimpleFormatter.java -- + A class for formatting log records into short human-readable messages + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.text.DateFormat; +import java.util.Date; + +/** + * A <code>SimpleFormatter</code> formats log records into + * short human-readable messages, typically one or two lines. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class SimpleFormatter + extends Formatter +{ + /** + * Constructs a SimpleFormatter. + */ + public SimpleFormatter() + { + } + + + /** + * An instance of a DateFormatter that is used for formatting + * the time of a log record into a human-readable string, + * according to the rules of the current locale. The value + * is set after the first invocation of format, since it is + * common that a JVM will instantiate a SimpleFormatter without + * ever using it. + */ + private DateFormat dateFormat; + + /** + * The character sequence that is used to separate lines in the + * generated stream. Somewhat surprisingly, the Sun J2SE 1.4 + * reference implementation always uses UNIX line endings, even on + * platforms that have different line ending conventions (i.e., + * DOS). The GNU implementation does not replicate this bug. + * + * @see Sun bug parade, bug #4462871, + * "java.util.logging.SimpleFormatter uses hard-coded line separator". + */ + static final String lineSep = System.getProperty("line.separator"); + + + /** + * Formats a log record into a String. + * + * @param the log record to be formatted. + * + * @return a short human-readable message, typically one or two + * lines. Lines are separated using the default platform line + * separator. + * + * @throws NullPointerException if <code>record</code> + * is <code>null</code>. + */ + public String format(LogRecord record) + { + StringBuffer buf = new StringBuffer(180); + + if (dateFormat == null) + dateFormat = DateFormat.getDateTimeInstance(); + + buf.append(dateFormat.format(new Date(record.getMillis()))); + buf.append(' '); + buf.append(record.getSourceClassName()); + buf.append(' '); + buf.append(record.getSourceMethodName()); + buf.append(lineSep); + + buf.append(record.getLevel()); + buf.append(": "); + buf.append(formatMessage(record)); + + buf.append(lineSep); + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/util/logging/SocketHandler.java b/libjava/classpath/java/util/logging/SocketHandler.java new file mode 100644 index 00000000000..002dfdbbe5e --- /dev/null +++ b/libjava/classpath/java/util/logging/SocketHandler.java @@ -0,0 +1,221 @@ +/* SocketHandler.java -- a class for publishing log messages to network sockets + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + + +/** + * A <code>SocketHandler</code> publishes log records to + * a TCP/IP socket. + * + * <p><strong>Configuration:</strong> Values of the subsequent + * <code>LogManager</code> properties are taken into consideration + * when a <code>SocketHandler</code> is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + * <ul> + * + * <li><code>java.util.SocketHandler.level</code> - specifies + * the initial severity level threshold. Default value: + * <code>Level.ALL</code>.</li> + * + * <li><code>java.util.SocketHandler.filter</code> - specifies + * the name of a Filter class. Default value: No Filter.</li> + * + * <li><code>java.util.SocketHandler.formatter</code> - specifies + * the name of a Formatter class. Default value: + * <code>java.util.logging.XMLFormatter</code>.</li> + * + * <li><code>java.util.SocketHandler.encoding</code> - specifies + * the name of the character encoding. Default value: + * the default platform encoding.</li> + * + * <li><code>java.util.SocketHandler.host</code> - specifies + * the name of the host to which records are published. + * There is no default value for this property; if it is + * not set, the SocketHandler constructor will throw + * an exception.</li> + * + * <li><code>java.util.SocketHandler.port</code> - specifies + * the TCP/IP port to which records are published. + * There is no default value for this property; if it is + * not set, the SocketHandler constructor will throw + * an exception.</li> + * + * </ul> + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class SocketHandler + extends StreamHandler +{ + /** + * Constructs a <code>SocketHandler</code> that publishes log + * records to a TCP/IP socket. Tthe initial configuration is + * determined by the <code>LogManager</code> properties described + * above. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either the + * <code>java.util.logging.SocketHandler.host</code> + * or <code>java.util.logging.SocketHandler.port</code> + * LogManager properties is not defined, or specifies + * an invalid value. + */ + public SocketHandler() + throws java.io.IOException + { + this(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.host"), + getPortNumber()); + } + + + /** + * Constructs a <code>SocketHandler</code> that publishes log + * records to a TCP/IP socket. With the exception of the internet + * host and port, the initial configuration is determined by the + * <code>LogManager</code> properties described above. + * + * @param host the Internet host to which log records will be + * forwarded. + * + * @param port the port at the host which will accept a request + * for a TCP/IP connection. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either + * <code>host</code> or <code>port</code> specify + * an invalid value. + */ + public SocketHandler(String host, int port) + throws java.io.IOException + { + super(createSocket(host, port), + "java.util.logging.SocketHandler", + /* default level */ Level.ALL, + /* formatter */ null, + /* default formatter */ XMLFormatter.class); + } + + + /** + * Retrieves the port number from the java.util.logging.SocketHandler.port + * LogManager property. + * + * @throws IllegalArgumentException if the property is not defined or + * does not specify an integer value. + */ + private static int getPortNumber() + { + try { + return Integer.parseInt(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.port")); + } catch (Exception ex) { + throw new IllegalArgumentException(); + } + } + + + /** + * Creates an OutputStream for publishing log records to an Internet + * host and port. This private method is a helper for use by the + * constructor of SocketHandler. + * + * @param host the Internet host to which log records will be + * forwarded. + * + * @param port the port at the host which will accept a request + * for a TCP/IP connection. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either + * <code>host</code> or <code>port</code> specify + * an invalid value. + */ + private static java.io.OutputStream createSocket(String host, int port) + throws java.io.IOException, java.lang.IllegalArgumentException + { + java.net.Socket socket; + + if ((host == null) || (port < 1)) + throw new IllegalArgumentException(); + + socket = new java.net.Socket(host, port); + + socket.shutdownInput(); + + /* The architecture of the logging framework provides replaceable + * formatters. Because these formatters perform their task by + * returning one single String for each LogRecord to be formatted, + * there is no need to buffer. + */ + socket.setTcpNoDelay(true); + + return socket.getOutputStream(); + } + + + /** + * Publishes a <code>LogRecord</code> to the network socket, + * provided the record passes all tests for being loggable. + * In addition, all data that may have been buffered will + * be forced to the network stream. + * + * <p>Most applications do not need to call this method directly. + * Instead, they will use a {@link Logger} instance, which will + * create LogRecords and distribute them to registered handlers. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>SocketHandler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + super.publish(record); + flush(); + } +} + diff --git a/libjava/classpath/java/util/logging/StreamHandler.java b/libjava/classpath/java/util/logging/StreamHandler.java new file mode 100644 index 00000000000..5c35c1e481d --- /dev/null +++ b/libjava/classpath/java/util/logging/StreamHandler.java @@ -0,0 +1,521 @@ +/* StreamHandler.java -- + A class for publishing log messages to instances of java.io.OutputStream + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +/** + * A <code>StreamHandler</code> publishes <code>LogRecords</code> to + * a instances of <code>java.io.OutputStream</code>. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class StreamHandler + extends Handler +{ + private OutputStream out; + private Writer writer; + + + /** + * Indicates the current state of this StreamHandler. The value + * should be one of STATE_FRESH, STATE_PUBLISHED, or STATE_CLOSED. + */ + private int streamState = STATE_FRESH; + + + /** + * streamState having this value indicates that the StreamHandler + * has been created, but the publish(LogRecord) method has not been + * called yet. If the StreamHandler has been constructed without an + * OutputStream, writer will be null, otherwise it is set to a + * freshly created OutputStreamWriter. + */ + private static final int STATE_FRESH = 0; + + + /** + * streamState having this value indicates that the publish(LocRecord) + * method has been called at least once. + */ + private static final int STATE_PUBLISHED = 1; + + + /** + * streamState having this value indicates that the close() method + * has been called. + */ + private static final int STATE_CLOSED = 2; + + + /** + * Creates a <code>StreamHandler</code> without an output stream. + * Subclasses can later use {@link + * #setOutputStream(java.io.OutputStream)} to associate an output + * stream with this StreamHandler. + */ + public StreamHandler() + { + this(null, null); + } + + + /** + * Creates a <code>StreamHandler</code> that formats log messages + * with the specified Formatter and publishes them to the specified + * output stream. + * + * @param out the output stream to which the formatted log messages + * are published. + * + * @param formatter the <code>Formatter</code> that will be used + * to format log messages. + */ + public StreamHandler(OutputStream out, Formatter formatter) + { + this(out, "java.util.logging.StreamHandler", Level.INFO, + formatter, SimpleFormatter.class); + } + + + StreamHandler( + OutputStream out, + String propertyPrefix, + Level defaultLevel, + Formatter formatter, Class defaultFormatterClass) + { + this.level = LogManager.getLevelProperty(propertyPrefix + ".level", + defaultLevel); + + this.filter = (Filter) LogManager.getInstanceProperty( + propertyPrefix + ".filter", + /* must be instance of */ Filter.class, + /* default: new instance of */ null); + + if (formatter != null) + this.formatter = formatter; + else + this.formatter = (Formatter) LogManager.getInstanceProperty( + propertyPrefix + ".formatter", + /* must be instance of */ Formatter.class, + /* default: new instance of */ defaultFormatterClass); + + try + { + String enc = LogManager.getLogManager().getProperty(propertyPrefix + + ".encoding"); + + /* make sure enc actually is a valid encoding */ + if ((enc != null) && (enc.length() > 0)) + new String(new byte[0], enc); + + this.encoding = enc; + } + catch (Exception _) + { + } + + if (out != null) + { + try + { + changeWriter(out, getEncoding()); + } + catch (UnsupportedEncodingException uex) + { + /* This should never happen, since the validity of the encoding + * name has been checked above. + */ + throw new RuntimeException(uex.getMessage()); + } + } + } + + + private void checkOpen() + { + if (streamState == STATE_CLOSED) + throw new IllegalStateException(this.toString() + " has been closed"); + } + + private void checkFresh() + { + checkOpen(); + if (streamState != STATE_FRESH) + throw new IllegalStateException("some log records have been published to " + this); + } + + + private void changeWriter(OutputStream out, String encoding) + throws UnsupportedEncodingException + { + OutputStreamWriter writer; + + /* The logging API says that a null encoding means the default + * platform encoding. However, java.io.OutputStreamWriter needs + * another constructor for the default platform encoding, + * passing null would throw an exception. + */ + if (encoding == null) + writer = new OutputStreamWriter(out); + else + writer = new OutputStreamWriter(out, encoding); + + /* Closing the stream has side effects -- do this only after + * creating a new writer has been successful. + */ + if ((streamState != STATE_FRESH) || (this.writer != null)) + close(); + + this.writer = writer; + this.out = out; + this.encoding = encoding; + streamState = STATE_FRESH; + } + + + /** + * Sets the character encoding which this handler uses for publishing + * log records. The encoding of a <code>StreamHandler</code> must be + * set before any log records have been published. + * + * @param encoding the name of a character encoding, or <code>null</code> + * for the default encoding. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control the + * the logging infrastructure. + * + * @exception IllegalStateException if any log records have been + * published to this <code>StreamHandler</code> before. Please + * be aware that this is a pecularity of the GNU implementation. + * While the API specification indicates that it is an error + * if the encoding is set after records have been published, + * it does not mandate any specific behavior for that case. + */ + public void setEncoding(String encoding) + throws SecurityException, UnsupportedEncodingException + { + /* The inherited implementation first checks whether the invoking + * code indeed has the permission to control the logging infra- + * structure, and throws a SecurityException if this was not the + * case. + * + * Next, it verifies that the encoding is supported and throws + * an UnsupportedEncodingExcpetion otherwise. Finally, it remembers + * the name of the encoding. + */ + super.setEncoding(encoding); + + checkFresh(); + + /* If out is null, setEncoding is being called before an output + * stream has been set. In that case, we need to check that the + * encoding is valid, and remember it if this is the case. Since + * this is exactly what the inherited implementation of + * Handler.setEncoding does, we can delegate. + */ + if (out != null) + { + /* The logging API says that a null encoding means the default + * platform encoding. However, java.io.OutputStreamWriter needs + * another constructor for the default platform encoding, passing + * null would throw an exception. + */ + if (encoding == null) + writer = new OutputStreamWriter(out); + else + writer = new OutputStreamWriter(out, encoding); + } + } + + + /** + * Changes the output stream to which this handler publishes + * logging records. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @throws NullPointerException if <code>out</code> + * is <code>null</code>. + */ + protected void setOutputStream(OutputStream out) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + /* Throw a NullPointerException if out is null. */ + out.getClass(); + + try + { + changeWriter(out, getEncoding()); + } + catch (UnsupportedEncodingException ex) + { + /* This seems quite unlikely to happen, unless the underlying + * implementation of java.io.OutputStreamWriter changes its + * mind (at runtime) about the set of supported character + * encodings. + */ + throw new RuntimeException(ex.getMessage()); + } + } + + + /** + * Publishes a <code>LogRecord</code> to the associated output + * stream, provided the record passes all tests for being loggable. + * The <code>StreamHandler</code> will localize the message of the + * log record and substitute any message parameters. + * + * <p>Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will create + * LogRecords and distribute them to registered handlers. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * <p>If a log record is being published to a + * <code>StreamHandler</code> that has been closed earlier, the Sun + * J2SE 1.4 reference can be observed to silently ignore the + * call. The GNU implementation, however, intentionally behaves + * differently by informing the <code>ErrorManager</code> associated + * with this <code>StreamHandler</code>. Since the condition + * indicates a programming error, the programmer should be + * informed. It also seems extremely unlikely that any application + * would depend on the exact behavior in this rather obscure, + * erroneous case -- especially since the API specification does not + * prescribe what is supposed to happen. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + String formattedMessage; + + if (!isLoggable(record)) + return; + + if (streamState == STATE_FRESH) + { + try + { + writer.write(formatter.getHead(this)); + } + catch (java.io.IOException ex) + { + reportError(null, ex, ErrorManager.WRITE_FAILURE); + return; + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.GENERIC_FAILURE); + return; + } + + streamState = STATE_PUBLISHED; + } + + try + { + formattedMessage = formatter.format(record); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.FORMAT_FAILURE); + return; + } + + try + { + writer.write(formattedMessage); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.WRITE_FAILURE); + } + } + + + /** + * Checks whether or not a <code>LogRecord</code> would be logged + * if it was passed to this <code>StreamHandler</code> for publication. + * + * <p>The <code>StreamHandler</code> implementation first checks + * whether a writer is present and the handler's level is greater + * than or equal to the severity level threshold. In a second step, + * if a {@link Filter} has been installed, its {@link + * Filter#isLoggable(LogRecord) isLoggable} method is + * invoked. Subclasses of <code>StreamHandler</code> can override + * this method to impose their own constraints. + * + * @param record the <code>LogRecord</code> to be checked. + * + * @return <code>true</code> if <code>record</code> would + * be published by {@link #publish(LogRecord) publish}, + * <code>false</code> if it would be discarded. + * + * @see #setLevel(Level) + * @see #setFilter(Filter) + * @see Filter#isLoggable(LogRecord) + * + * @throws NullPointerException if <code>record</code> is + * <code>null</code>. */ + public boolean isLoggable(LogRecord record) + { + return (writer != null) && super.isLoggable(record); + } + + + /** + * Forces any data that may have been buffered to the underlying + * output device. + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception. + * + * <p>If a <code>StreamHandler</code> that has been closed earlier + * is closed a second time, the Sun J2SE 1.4 reference can be + * observed to silently ignore the call. The GNU implementation, + * however, intentionally behaves differently by informing the + * <code>ErrorManager</code> associated with this + * <code>StreamHandler</code>. Since the condition indicates a + * programming error, the programmer should be informed. It also + * seems extremely unlikely that any application would depend on the + * exact behavior in this rather obscure, erroneous case -- + * especially since the API specification does not prescribe what is + * supposed to happen. + */ + public void flush() + { + try + { + checkOpen(); + if (writer != null) + writer.flush(); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.FLUSH_FAILURE); + } + } + + + /** + * Closes this <code>StreamHandler</code> after having forced any + * data that may have been buffered to the underlying output + * device. + * + * <p>As soon as <code>close</code> has been called, + * a <code>Handler</code> should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * <code>Handler</code> in any other way may throw runtime + * exceptions after calling <code>close</code>.</p> + * + * <p>In case of an I/O failure, the <code>ErrorManager</code> + * of this <code>Handler</code> will be informed, but the caller + * of this method will not receive an exception.</p> + * + * <p>If a <code>StreamHandler</code> that has been closed earlier + * is closed a second time, the Sun J2SE 1.4 reference can be + * observed to silently ignore the call. The GNU implementation, + * however, intentionally behaves differently by informing the + * <code>ErrorManager</code> associated with this + * <code>StreamHandler</code>. Since the condition indicates a + * programming error, the programmer should be informed. It also + * seems extremely unlikely that any application would depend on the + * exact behavior in this rather obscure, erroneous case -- + * especially since the API specification does not prescribe what is + * supposed to happen. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public void close() + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + try + { + /* Although flush also calls checkOpen, it catches + * any exceptions and reports them to the ErrorManager + * as flush failures. However, we want to report + * a closed stream as a close failure, not as a + * flush failure here. Therefore, we call checkOpen() + * before flush(). + */ + checkOpen(); + flush(); + + if (writer != null) + { + if (formatter != null) + { + /* Even if the StreamHandler has never published a record, + * it emits head and tail upon closing. An earlier version + * of the GNU Classpath implementation did not emitted + * anything. However, this had caused XML log files to be + * entirely empty instead of containing no log records. + */ + if (streamState == STATE_FRESH) + writer.write(formatter.getHead(this)); + if (streamState != STATE_CLOSED) + writer.write(formatter.getTail(this)); + } + streamState = STATE_CLOSED; + writer.close(); + } + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.CLOSE_FAILURE); + } + } +} diff --git a/libjava/classpath/java/util/logging/XMLFormatter.java b/libjava/classpath/java/util/logging/XMLFormatter.java new file mode 100644 index 00000000000..4dd63281727 --- /dev/null +++ b/libjava/classpath/java/util/logging/XMLFormatter.java @@ -0,0 +1,387 @@ +/* XMLFormatter.java -- + A class for formatting log messages into a standard XML format + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.ResourceBundle; + +/** + * An <code>XMLFormatter</code> formats LogRecords into + * a standard XML format. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class XMLFormatter + extends Formatter +{ + /** + * Constructs a new XMLFormatter. + */ + public XMLFormatter() + { + } + + + /** + * The character sequence that is used to separate lines in the + * generated XML stream. Somewhat surprisingly, the Sun J2SE 1.4 + * reference implementation always uses UNIX line endings, even on + * platforms that have different line ending conventions (i.e., + * DOS). The GNU Classpath implementation does not replicates this + * bug. + * + * See also the Sun bug parade, bug #4462871, + * "java.util.logging.SimpleFormatter uses hard-coded line separator". + */ + private static final String lineSep = SimpleFormatter.lineSep; + + + /** + * A DateFormat for emitting time in the ISO 8601 format. + * Since the API specification of SimpleDateFormat does not talk + * about its thread-safety, we cannot share a singleton instance. + */ + private final SimpleDateFormat iso8601 + = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + + /** + * Appends a line consisting of indentation, opening element tag, + * element content, closing element tag and line separator to + * a StringBuffer, provided that the element content is + * actually existing. + * + * @param buf the StringBuffer to which the line will be appended. + * + * @param indent the indentation level. + * + * @param tag the element tag name, for instance <code>method</code>. + * + * @param content the element content, or <code>null</code> to + * have no output whatsoever appended to <code>buf</code>. + */ + private static void appendTag(StringBuffer buf, int indent, + String tag, String content) + { + int i; + + if (content == null) + return; + + for (i = 0; i < indent * 2; i++) + buf.append(' '); + + buf.append("<"); + buf.append(tag); + buf.append('>'); + + /* Append the content, but escape for XML by replacing + * '&', '<', '>' and all non-ASCII characters with + * appropriate escape sequences. + * The Sun J2SE 1.4 reference implementation does not + * escape non-ASCII characters. This is a bug in their + * implementation which has been reported in the Java + * bug parade as bug number (FIXME: Insert number here). + */ + for (i = 0; i < content.length(); i++) + { + char c = content.charAt(i); + switch (c) + { + case '&': + buf.append("&"); + break; + + case '<': + buf.append("<"); + break; + + case '>': + buf.append(">"); + break; + + default: + if (((c >= 0x20) && (c <= 0x7e)) + || (c == /* line feed */ 10) + || (c == /* carriage return */ 13)) + buf.append(c); + else + { + buf.append("&#"); + buf.append((int) c); + buf.append(';'); + } + break; + } /* switch (c) */ + } /* for i */ + + buf.append("</"); + buf.append(tag); + buf.append(">"); + buf.append(lineSep); + } + + + /** + * Appends a line consisting of indentation, opening element tag, + * numeric element content, closing element tag and line separator + * to a StringBuffer. + * + * @param buf the StringBuffer to which the line will be appended. + * + * @param indent the indentation level. + * + * @param tag the element tag name, for instance <code>method</code>. + * + * @param content the element content. + */ + private static void appendTag(StringBuffer buf, int indent, + String tag, long content) + { + appendTag(buf, indent, tag, Long.toString(content)); + } + + + public String format(LogRecord record) + { + StringBuffer buf = new StringBuffer(400); + Level level = record.getLevel(); + long millis = record.getMillis(); + Object[] params = record.getParameters(); + ResourceBundle bundle = record.getResourceBundle(); + String message; + + buf.append("<record>"); + buf.append(lineSep); + + + appendTag(buf, 1, "date", iso8601.format(new Date(millis))); + appendTag(buf, 1, "millis", record.getMillis()); + appendTag(buf, 1, "sequence", record.getSequenceNumber()); + appendTag(buf, 1, "logger", record.getLoggerName()); + + if (level.isStandardLevel()) + appendTag(buf, 1, "level", level.toString()); + else + appendTag(buf, 1, "level", level.intValue()); + + appendTag(buf, 1, "class", record.getSourceClassName()); + appendTag(buf, 1, "method", record.getSourceMethodName()); + appendTag(buf, 1, "thread", record.getThreadID()); + + /* The Sun J2SE 1.4 reference implementation does not emit the + * message in localized form. This is in violation of the API + * specification. The GNU Classpath implementation intentionally + * replicates the buggy behavior of the Sun implementation, as + * different log files might be a big nuisance to users. + */ + try + { + record.setResourceBundle(null); + message = formatMessage(record); + } + finally + { + record.setResourceBundle(bundle); + } + appendTag(buf, 1, "message", message); + + /* The Sun J2SE 1.4 reference implementation does not + * emit key, catalog and param tags. This is in violation + * of the API specification. The Classpath implementation + * intentionally replicates the buggy behavior of the + * Sun implementation, as different log files might be + * a big nuisance to users. + * + * FIXME: File a bug report with Sun. Insert bug number here. + * + * + * key = record.getMessage(); + * if (key == null) + * key = ""; + * + * if ((bundle != null) && !key.equals(message)) + * { + * appendTag(buf, 1, "key", key); + * appendTag(buf, 1, "catalog", record.getResourceBundleName()); + * } + * + * if (params != null) + * { + * for (int i = 0; i < params.length; i++) + * appendTag(buf, 1, "param", params[i].toString()); + * } + */ + + /* FIXME: We have no way to obtain the stacktrace before free JVMs + * support the corresponding method in java.lang.Throwable. Well, + * it would be possible to parse the output of printStackTrace, + * but this would be pretty kludgy. Instead, we postpose the + * implementation until Throwable has made progress. + */ + Throwable thrown = record.getThrown(); + if (thrown != null) + { + buf.append(" <exception>"); + buf.append(lineSep); + + /* The API specification is not clear about what exactly + * goes into the XML record for a thrown exception: It + * could be the result of getMessage(), getLocalizedMessage(), + * or toString(). Therefore, it was necessary to write a + * Mauve testlet and run it with the Sun J2SE 1.4 reference + * implementation. It turned out that the we need to call + * toString(). + * + * FIXME: File a bug report with Sun, asking for clearer + * specs. + */ + appendTag(buf, 2, "message", thrown.toString()); + + /* FIXME: The Logging DTD specifies: + * + * <!ELEMENT exception (message?, frame+)> + * + * However, java.lang.Throwable.getStackTrace() is + * allowed to return an empty array. So, what frame should + * be emitted for an empty stack trace? We probably + * should file a bug report with Sun, asking for the DTD + * to be changed. + */ + + buf.append(" </exception>"); + buf.append(lineSep); + } + + + buf.append("</record>"); + buf.append(lineSep); + + return buf.toString(); + } + + + /** + * Returns a string that handlers are supposed to emit before + * the first log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable header. + * + * @return a string for the header. + * + * @param handler the handler which will prepend the returned + * string in front of the first log record. This method + * will inspect certain properties of the handler, for + * example its encoding, in order to construct the header. + */ + public String getHead(Handler h) + { + StringBuffer buf; + String encoding; + + buf = new StringBuffer(80); + buf.append("<?xml version=\"1.0\" encoding=\""); + + encoding = h.getEncoding(); + + /* file.encoding is a system property with the Sun JVM, indicating + * the platform-default file encoding. Unfortunately, the API + * specification for java.lang.System.getProperties() does not + * list this property. + */ + if (encoding == null) + encoding = System.getProperty("file.encoding"); + + /* Since file.encoding is not listed with the API specification of + * java.lang.System.getProperties(), there might be some VMs that + * do not define this system property. Therefore, we use UTF-8 as + * a reasonable default. Please note that if the platform encoding + * uses the same codepoints as US-ASCII for the US-ASCII character + * set (e.g, 65 for A), it does not matter whether we emit the + * wrong encoding into the XML header -- the GNU Classpath will + * emit XML escape sequences like Ӓ for any non-ASCII + * character. Virtually all character encodings use the same code + * points as US-ASCII for ASCII characters. Probably, EBCDIC is + * the only exception. + */ + if (encoding == null) + encoding = "UTF-8"; + + /* On Windows XP localized for Swiss German (this is one of + * my [Sascha Brawer's] test machines), the default encoding + * has the canonical name "windows-1252". The "historical" name + * of this encoding is "Cp1252" (see the Javadoc for the class + * java.nio.charset.Charset for the distinction). Now, that class + * does have a method for mapping historical to canonical encoding + * names. However, if we used it here, we would be come dependent + * on java.nio.*, which was only introduced with J2SE 1.4. + * Thus, we do this little hack here. As soon as Classpath supports + * java.nio.charset.CharSet, this hack should be replaced by + * code that correctly canonicalizes the encoding name. + */ + if ((encoding.length() > 2) && encoding.startsWith("Cp")) + encoding = "windows-" + encoding.substring(2); + + buf.append(encoding); + + buf.append("\" standalone=\"no\"?>"); + buf.append(lineSep); + + /* SYSTEM is not a fully qualified URL so that validating + * XML parsers do not need to connect to the Internet in + * order to read in a log file. See also the Sun Bug Parade, + * bug #4372790, "Logging APIs: need to use relative URL for XML + * doctype". + */ + buf.append("<!DOCTYPE log SYSTEM \"logger.dtd\">"); + buf.append(lineSep); + buf.append("<log>"); + buf.append(lineSep); + + return buf.toString(); + } + + + public String getTail(Handler h) + { + return "</log>" + lineSep; + } +} diff --git a/libjava/classpath/java/util/logging/package.html b/libjava/classpath/java/util/logging/package.html new file mode 100644 index 00000000000..31f0494fcd7 --- /dev/null +++ b/libjava/classpath/java/util/logging/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.logging package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.logging</title></head> + +<body> +<p>Utility classes for logging events.</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/package.html b/libjava/classpath/java/util/package.html new file mode 100644 index 00000000000..ff2919c550c --- /dev/null +++ b/libjava/classpath/java/util/package.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util</title></head> + +<body> +<p>Utility classes such as collections (maps, sets, lists, dictionaries and +stacks), calendars, dates, locales, properties, timers, resource bundles and +event objects.</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/prefs/AbstractPreferences.java b/libjava/classpath/java/util/prefs/AbstractPreferences.java new file mode 100644 index 00000000000..3f70400f5d1 --- /dev/null +++ b/libjava/classpath/java/util/prefs/AbstractPreferences.java @@ -0,0 +1,1272 @@ +/* AbstractPreferences -- Partial implementation of a Preference node + Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.prefs; + +import gnu.java.util.prefs.NodeWriter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; + +/** + * Partial implementation of a Preference node. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class AbstractPreferences extends Preferences { + + // protected fields + + /** + * Object used to lock this preference node. Any thread only locks nodes + * downwards when it has the lock on the current node. No method should + * synchronize on the lock of any of its parent nodes while holding the + * lock on the current node. + */ + protected final Object lock = new Object(); + + /** + * Set to true in the contructor if the node did not exist in the backing + * store when this preference node object was created. Should be set in + * the contructor of a subclass. Defaults to false. Used to fire node + * changed events. + */ + protected boolean newNode = false; + + // private fields + + /** + * The parent preferences node or null when this is the root node. + */ + private final AbstractPreferences parent; + + /** + * The name of this node. + * Only when this is a root node (parent == null) the name is empty. + * It has a maximum of 80 characters and cannot contain any '/' characters. + */ + private final String name; + + /** True when this node has been remove, false otherwise. */ + private boolean removed = false; + + /** + * Holds all the child names and nodes of this node that have been + * accessed by earlier <code>getChild()</code> or <code>childSpi()</code> + * invocations and that have not been removed. + */ + private HashMap childCache = new HashMap(); + + // constructor + + /** + * Creates a new AbstractPreferences node with the given parent and name. + * + * @param parent the parent of this node or null when this is the root node + * @param name the name of this node, can not be null, only 80 characters + * maximum, must be empty when parent is null and cannot + * contain any '/' characters + * @exception IllegalArgumentException when name is null, greater then 80 + * characters, not the empty string but parent is null or + * contains a '/' character + */ + protected AbstractPreferences(AbstractPreferences parent, String name) { + if ( (name == null) // name should be given + || (name.length() > MAX_NAME_LENGTH) // 80 characters max + || (parent == null && name.length() != 0) // root has no name + || (parent != null && name.length() == 0) // all other nodes do + || (name.indexOf('/') != -1)) // must not contain '/' + throw new IllegalArgumentException("Illegal name argument '" + + name + + "' (parent is " + + (parent == null ? "" : "not ") + + "null)"); + this.parent = parent; + this.name = name; + } + + // identification methods + + /** + * Returns the absolute path name of this preference node. + * The absolute path name of a node is the path name of its parent node + * plus a '/' plus its own name. If the node is the root node and has no + * parent then its path name is "" and its absolute path name is "/". + */ + public String absolutePath() { + if (parent == null) + return "/"; + else + return parent.path() + '/' + name; + } + + /** + * Private helper method for absolutePath. Returns the empty string for a + * root node and otherwise the parentPath of its parent plus a '/'. + */ + private String path() { + if (parent == null) + return ""; + else + return parent.path() + '/' + name; + } + + /** + * Returns true if this node comes from the user preferences tree, false + * if it comes from the system preferences tree. + */ + public boolean isUserNode() { + AbstractPreferences root = this; + while (root.parent != null) + root = root.parent; + return root == Preferences.userRoot(); + } + + /** + * Returns the name of this preferences node. The name of the node cannot + * be null, can be mostly 80 characters and cannot contain any '/' + * characters. The root node has as name "". + */ + public String name() { + return name; + } + + /** + * Returns the String given by + * <code> + * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath() + * </code> + */ + public String toString() { + return (isUserNode() ? "User":"System") + + " Preference Node: " + + absolutePath(); + } + + /** + * Returns all known unremoved children of this node. + * + * @return All known unremoved children of this node + */ + protected final AbstractPreferences[] cachedChildren() + { + return (AbstractPreferences[]) childCache.values().toArray(); + } + + /** + * Returns all the direct sub nodes of this preferences node. + * Needs access to the backing store to give a meaningfull answer. + * <p> + * This implementation locks this node, checks if the node has not yet + * been removed and throws an <code>IllegalStateException</code> when it + * has been. Then it creates a new <code>TreeSet</code> and adds any + * already cached child nodes names. To get any uncached names it calls + * <code>childrenNamesSpi()</code> and adds the result to the set. Finally + * it calls <code>toArray()</code> on the created set. When the call to + * <code>childrenNamesSpi</code> thows an <code>BackingStoreException</code> + * this method will not catch that exception but propagate the exception + * to the caller. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException when this node has been removed + */ + public String[] childrenNames() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + TreeSet childrenNames = new TreeSet(); + + // First get all cached node names + childrenNames.addAll(childCache.keySet()); + + // Then add any others + String names[] = childrenNamesSpi(); + for (int i = 0; i < names.length; i++) { + childrenNames.add(names[i]); + } + + // And return the array of names + String[] children = new String[childrenNames.size()]; + childrenNames.toArray(children); + return children; + + } + } + + /** + * Returns a sub node of this preferences node if the given path is + * relative (does not start with a '/') or a sub node of the root + * if the path is absolute (does start with a '/'). + * <p> + * This method first locks this node and checks if the node has not been + * removed, if it has been removed it throws an exception. Then if the + * path is relative (does not start with a '/') it checks if the path is + * legal (does not end with a '/' and has no consecutive '/' characters). + * Then it recursively gets a name from the path, gets the child node + * from the child-cache of this node or calls the <code>childSpi()</code> + * method to create a new child sub node. This is done recursively on the + * newly created sub node with the rest of the path till the path is empty. + * If the path is absolute (starts with a '/') the lock on this node is + * droped and this method is called on the root of the preferences tree + * with as argument the complete path minus the first '/'. + * + * @exception IllegalStateException if this node has been removed + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public Preferences node(String path) { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + // Is it a relative path? + if (!path.startsWith("/")) { + + // Check if it is a valid path + if (path.indexOf("//") != -1 || path.endsWith("/")) + throw new IllegalArgumentException(path); + + return getNode(path); + } + } + + // path started with a '/' so it is absolute + // we drop the lock and start from the root (omitting the first '/') + Preferences root = isUserNode() ? userRoot() : systemRoot(); + return root.node(path.substring(1)); + + } + + /** + * Private helper method for <code>node()</code>. Called with this node + * locked. Returns this node when path is the empty string, if it is not + * empty the next node name is taken from the path (all chars till the + * next '/' or end of path string) and the node is either taken from the + * child-cache of this node or the <code>childSpi()</code> method is called + * on this node with the name as argument. Then this method is called + * recursively on the just constructed child node with the rest of the + * path. + * + * @param path should not end with a '/' character and should not contain + * consecutive '/' characters + * @exception IllegalArgumentException if path begins with a name that is + * larger then 80 characters. + */ + private Preferences getNode(String path) { + // if mark is dom then goto end + + // Empty String "" indicates this node + if (path.length() == 0) + return this; + + // Calculate child name and rest of path + String childName; + String childPath; + int nextSlash = path.indexOf('/'); + if (nextSlash == -1) { + childName = path; + childPath = ""; + } else { + childName = path.substring(0, nextSlash); + childPath = path.substring(nextSlash+1); + } + + // Get the child node + AbstractPreferences child; + child = (AbstractPreferences)childCache.get(childName); + if (child == null) { + + if (childName.length() > MAX_NAME_LENGTH) + throw new IllegalArgumentException(childName); + + // Not in childCache yet so create a new sub node + child = childSpi(childName); + // XXX - check if node is new + childCache.put(childName, child); + } + + // Lock the child and go down + synchronized(child.lock) { + return child.getNode(childPath); + } + } + + /** + * Returns true if the node that the path points to exists in memory or + * in the backing store. Otherwise it returns false or an exception is + * thrown. When this node is removed the only valid parameter is the + * empty string (indicating this node), the return value in that case + * will be false. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + * and the path is not the empty string (indicating this node) + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public boolean nodeExists(String path) throws BackingStoreException { + synchronized(lock) { + if (isRemoved() && path.length() != 0) + throw new IllegalStateException("Node removed"); + + // Is it a relative path? + if (!path.startsWith("/")) { + + // Check if it is a valid path + if (path.indexOf("//") != -1 || path.endsWith("/")) + throw new IllegalArgumentException(path); + + return existsNode(path); + } + } + + // path started with a '/' so it is absolute + // we drop the lock and start from the root (omitting the first '/') + Preferences root = isUserNode() ? userRoot() : systemRoot(); + return root.nodeExists(path.substring(1)); + + } + + private boolean existsNode(String path) throws BackingStoreException { + + // Empty String "" indicates this node + if (path.length() == 0) + return(!isRemoved()); + + // Calculate child name and rest of path + String childName; + String childPath; + int nextSlash = path.indexOf('/'); + if (nextSlash == -1) { + childName = path; + childPath = ""; + } else { + childName = path.substring(0, nextSlash); + childPath = path.substring(nextSlash+1); + } + + // Get the child node + AbstractPreferences child; + child = (AbstractPreferences)childCache.get(childName); + if (child == null) { + + if (childName.length() > MAX_NAME_LENGTH) + throw new IllegalArgumentException(childName); + + // Not in childCache yet so create a new sub node + child = getChild(childName); + + if (child == null) + return false; + + childCache.put(childName, child); + } + + // Lock the child and go down + synchronized(child.lock) { + return child.existsNode(childPath); + } + } + + /** + * Returns the child sub node if it exists in the backing store or null + * if it does not exist. Called (indirectly) by <code>nodeExists()</code> + * when a child node name can not be found in the cache. + * <p> + * Gets the lock on this node, calls <code>childrenNamesSpi()</code> to + * get an array of all (possibly uncached) children and compares the + * given name with the names in the array. If the name is found in the + * array <code>childSpi()</code> is called to get an instance, otherwise + * null is returned. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + protected AbstractPreferences getChild(String name) + throws BackingStoreException + { + synchronized(lock) { + // Get all the names (not yet in the cache) + String[] names = childrenNamesSpi(); + for (int i=0; i < names.length; i++) + if (name.equals(names[i])) + return childSpi(name); + + // No child with that name found + return null; + } + } + + /** + * Returns true if this node has been removed with the + * <code>removeNode()</code> method, false otherwise. + * <p> + * Gets the lock on this node and then returns a boolean field set by + * <code>removeNode</code> methods. + */ + protected boolean isRemoved() { + synchronized(lock) { + return removed; + } + } + + /** + * Returns the parent preferences node of this node or null if this is + * the root of the preferences tree. + * <p> + * Gets the lock on this node, checks that the node has not been removed + * and returns the parent given to the constructor. + * + * @exception IllegalStateException if this node has been removed + */ + public Preferences parent() { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + return parent; + } + } + + // export methods + + /** + * XXX + */ + public void exportNode(OutputStream os) + throws BackingStoreException, + IOException + { + NodeWriter nodeWriter = new NodeWriter(this, os); + nodeWriter.writePrefs(); + } + + /** + * XXX + */ + public void exportSubtree(OutputStream os) + throws BackingStoreException, + IOException + { + NodeWriter nodeWriter = new NodeWriter(this, os); + nodeWriter.writePrefsTree(); + } + + // preference entry manipulation methods + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + * <p> + * This method locks this node and checks if the node has not been + * removed, if it has been removed it throws an exception, then it returns + * the result of calling <code>keysSpi()</code>. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public String[] keys() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + return keysSpi(); + } + } + + + /** + * Returns the value associated with the key in this preferences node. If + * the default value of the key cannot be found in the preferences node + * entries or something goes wrong with the backing store the supplied + * default value is returned. + * <p> + * Checks that key is not null and not larger then 80 characters, + * locks this node, and checks that the node has not been removed. + * Then it calls <code>keySpi()</code> and returns + * the result of that method or the given default value if it returned + * null or throwed an exception. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public String get(String key, String defaultVal) { + if (key.length() > MAX_KEY_LENGTH) + throw new IllegalArgumentException(key); + + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + String value; + try { + value = getSpi(key); + } catch (ThreadDeath death) { + throw death; + } catch (Throwable t) { + value = null; + } + + if (value != null) { + return value; + } else { + return defaultVal; + } + } + } + + /** + * Convenience method for getting the given entry as a boolean. + * When the string representation of the requested entry is either + * "true" or "false" (ignoring case) then that value is returned, + * otherwise the given default boolean value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public boolean getBoolean(String key, boolean defaultVal) { + String value = get(key, null); + + if ("true".equalsIgnoreCase(value)) + return true; + + if ("false".equalsIgnoreCase(value)) + return false; + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a byte array. + * When the string representation of the requested entry is a valid + * Base64 encoded string (without any other characters, such as newlines) + * then the decoded Base64 string is returned as byte array, + * otherwise the given default byte array value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public byte[] getByteArray(String key, byte[] defaultVal) { + String value = get(key, null); + + byte[] b = null; + if (value != null) { + b = decode64(value); + } + + if (b != null) + return b; + else + return defaultVal; + } + + /** + * Helper method for decoding a Base64 string as an byte array. + * Returns null on encoding error. This method does not allow any other + * characters present in the string then the 65 special base64 chars. + */ + private static byte[] decode64(String s) { + ByteArrayOutputStream bs = new ByteArrayOutputStream((s.length()/4)*3); + char[] c = new char[s.length()]; + s.getChars(0, s.length(), c, 0); + + // Convert from base64 chars + int endchar = -1; + for(int j = 0; j < c.length && endchar == -1; j++) { + if (c[j] >= 'A' && c[j] <= 'Z') { + c[j] -= 'A'; + } else if (c[j] >= 'a' && c[j] <= 'z') { + c[j] = (char) (c[j] + 26 - 'a'); + } else if (c[j] >= '0' && c[j] <= '9') { + c[j] = (char) (c[j] + 52 - '0'); + } else if (c[j] == '+') { + c[j] = 62; + } else if (c[j] == '/') { + c[j] = 63; + } else if (c[j] == '=') { + endchar = j; + } else { + return null; // encoding exception + } + } + + int remaining = endchar == -1 ? c.length : endchar; + int i = 0; + while (remaining > 0) { + // Four input chars (6 bits) are decoded as three bytes as + // 000000 001111 111122 222222 + + byte b0 = (byte) (c[i] << 2); + if (remaining >= 2) { + b0 += (c[i+1] & 0x30) >> 4; + } + bs.write(b0); + + if (remaining >= 3) { + byte b1 = (byte) ((c[i+1] & 0x0F) << 4); + b1 += (byte) ((c[i+2] & 0x3C) >> 2); + bs.write(b1); + } + + if (remaining >= 4) { + byte b2 = (byte) ((c[i+2] & 0x03) << 6); + b2 += c[i+3]; + bs.write(b2); + } + + i += 4; + remaining -= 4; + } + + return bs.toByteArray(); + } + + /** + * Convenience method for getting the given entry as a double. + * When the string representation of the requested entry can be decoded + * with <code>Double.parseDouble()</code> then that double is returned, + * otherwise the given default double value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public double getDouble(String key, double defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Double.parseDouble(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a float. + * When the string representation of the requested entry can be decoded + * with <code>Float.parseFloat()</code> then that float is returned, + * otherwise the given default float value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public float getFloat(String key, float defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Float.parseFloat(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as an integer. + * When the string representation of the requested entry can be decoded + * with <code>Integer.parseInt()</code> then that integer is returned, + * otherwise the given default integer value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public int getInt(String key, int defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a long. + * When the string representation of the requested entry can be decoded + * with <code>Long.parseLong()</code> then that long is returned, + * otherwise the given default long value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public long getLong(String key, long defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Long.parseLong(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Sets the value of the given preferences entry for this node. + * Key and value cannot be null, the key cannot exceed 80 characters + * and the value cannot exceed 8192 characters. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * <p> + * Checks that key and value are valid, locks this node, and checks that + * the node has not been removed. Then it calls <code>putSpi()</code>. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public void put(String key, String value) { + if (key.length() > MAX_KEY_LENGTH + || value.length() > MAX_VALUE_LENGTH) + throw new IllegalArgumentException("key (" + + key.length() + ")" + + " or value (" + + value.length() + ")" + + " to large"); + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + putSpi(key, value); + + // XXX - fire events + } + + } + + /** + * Convenience method for setting the given entry as a boolean. + * The boolean is converted with <code>Boolean.toString(value)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putBoolean(String key, boolean value) { + put(key, String.valueOf(value)); + // XXX - Use when using 1.4 compatible Boolean + // put(key, Boolean.toString(value)); + } + + /** + * Convenience method for setting the given entry as an array of bytes. + * The byte array is converted to a Base64 encoded string + * and then stored in the preference entry as that string. + * <p> + * Note that a byte array encoded as a Base64 string will be about 1.3 + * times larger then the original length of the byte array, which means + * that the byte array may not be larger about 6 KB. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public void putByteArray(String key, byte[] value) { + put(key, encode64(value)); + } + + /** + * Helper method for encoding an array of bytes as a Base64 String. + */ + private static String encode64(byte[] b) { + StringBuffer sb = new StringBuffer((b.length/3)*4); + + int i = 0; + int remaining = b.length; + char c[] = new char[4]; + while (remaining > 0) { + // Three input bytes are encoded as four chars (6 bits) as + // 00000011 11112222 22333333 + + c[0] = (char) ((b[i] & 0xFC) >> 2); + c[1] = (char) ((b[i] & 0x03) << 4); + if (remaining >= 2) { + c[1] += (char) ((b[i+1] & 0xF0) >> 4); + c[2] = (char) ((b[i+1] & 0x0F) << 2); + if (remaining >= 3) { + c[2] += (char) ((b[i+2] & 0xC0) >> 6); + c[3] = (char) (b[i+2] & 0x3F); + } else { + c[3] = 64; + } + } else { + c[2] = 64; + c[3] = 64; + } + + // Convert to base64 chars + for(int j = 0; j < 4; j++) { + if (c[j] < 26) { + c[j] += 'A'; + } else if (c[j] < 52) { + c[j] = (char) (c[j] - 26 + 'a'); + } else if (c[j] < 62) { + c[j] = (char) (c[j] - 52 + '0'); + } else if (c[j] == 62) { + c[j] = '+'; + } else if (c[j] == 63) { + c[j] = '/'; + } else { + c[j] = '='; + } + } + + sb.append(c); + i += 3; + remaining -= 3; + } + + return sb.toString(); + } + + /** + * Convenience method for setting the given entry as a double. + * The double is converted with <code>Double.toString(double)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putDouble(String key, double value) { + put(key, Double.toString(value)); + } + + /** + * Convenience method for setting the given entry as a float. + * The float is converted with <code>Float.toString(float)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putFloat(String key, float value) { + put(key, Float.toString(value)); + } + + /** + * Convenience method for setting the given entry as an integer. + * The integer is converted with <code>Integer.toString(int)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putInt(String key, int value) { + put(key, Integer.toString(value)); + } + + /** + * Convenience method for setting the given entry as a long. + * The long is converted with <code>Long.toString(long)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putLong(String key, long value) { + put(key, Long.toString(value)); + } + + /** + * Removes the preferences entry from this preferences node. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * <p> + * This implementation checks that the key is not larger then 80 + * characters, gets the lock of this node, checks that the node has + * not been removed and calls <code>removeSpi</code> with the given key. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void remove(String key) { + if (key.length() > MAX_KEY_LENGTH) + throw new IllegalArgumentException(key); + + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + removeSpi(key); + } + } + + /** + * Removes all entries from this preferences node. May need access to the + * backing store to get and clear all entries. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * <p> + * This implementation locks this node, checks that the node has not been + * removed and calls <code>keys()</code> to get a complete array of keys + * for this node. For every key found <code>removeSpi()</code> is called. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public void clear() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node Removed"); + + String[] keys = keys(); + for (int i = 0; i < keys.length; i++) { + removeSpi(keys[i]); + } + } + } + + /** + * Writes all preference changes on this and any subnode that have not + * yet been written to the backing store. This has no effect on the + * preference entries in this VM, but it makes sure that all changes + * are visible to other programs (other VMs might need to call the + * <code>sync()</code> method to actually see the changes to the backing + * store. + * <p> + * Locks this node, calls the <code>flushSpi()</code> method, gets all + * the (cached - already existing in this VM) subnodes and then calls + * <code>flushSpi()</code> on every subnode with this node unlocked and + * only that particular subnode locked. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + public void flush() throws BackingStoreException { + flushNode(false); + } + + /** + * Writes and reads all preference changes to and from this and any + * subnodes. This makes sure that all local changes are written to the + * backing store and that all changes to the backing store are visible + * in this preference node (and all subnodes). + * <p> + * Checks that this node is not removed, locks this node, calls the + * <code>syncSpi()</code> method, gets all the subnodes and then calls + * <code>syncSpi()</code> on every subnode with this node unlocked and + * only that particular subnode locked. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public void sync() throws BackingStoreException { + flushNode(true); + } + + + /** + * Private helper method that locks this node and calls either + * <code>flushSpi()</code> if <code>sync</code> is false, or + * <code>flushSpi()</code> if <code>sync</code> is true. Then it gets all + * the currently cached subnodes. For every subnode it calls this method + * recursively with this node no longer locked. + * <p> + * Called by either <code>flush()</code> or <code>sync()</code> + */ + private void flushNode(boolean sync) throws BackingStoreException { + String[] keys = null; + synchronized(lock) { + if (sync) { + syncSpi(); + } else { + flushSpi(); + } + keys = (String[]) childCache.keySet().toArray(new String[]{}); + } + + if (keys != null) { + for (int i = 0; i < keys.length; i++) { + // Have to lock this node again to access the childCache + AbstractPreferences subNode; + synchronized(this) { + subNode = (AbstractPreferences) childCache.get(keys[i]); + } + + // The child could already have been removed from the cache + if (subNode != null) { + subNode.flushNode(sync); + } + } + } + } + + /** + * Removes this and all subnodes from the backing store and clears all + * entries. After removal this instance will not be useable (except for + * a few methods that don't throw a <code>InvalidStateException</code>), + * even when a new node with the same path name is created this instance + * will not be usable again. + * <p> + * Checks that this is not a root node. If not it locks the parent node, + * then locks this node and checks that the node has not yet been removed. + * Then it makes sure that all subnodes of this node are in the child cache, + * by calling <code>childSpi()</code> on any children not yet in the cache. + * Then for all children it locks the subnode and removes it. After all + * subnodes have been purged the child cache is cleared, this nodes removed + * flag is set and any listeners are called. Finally this node is removed + * from the child cache of the parent node. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has already been removed + * @exception UnsupportedOperationException if this is a root node + */ + public void removeNode() throws BackingStoreException { + // Check if it is a root node + if (parent == null) + throw new UnsupportedOperationException("Cannot remove root node"); + + synchronized(parent) { + synchronized(this) { + if (isRemoved()) + throw new IllegalStateException("Node Removed"); + + purge(); + } + parent.childCache.remove(name); + } + } + + /** + * Private helper method used to completely remove this node. + * Called by <code>removeNode</code> with the parent node and this node + * locked. + * <p> + * Makes sure that all subnodes of this node are in the child cache, + * by calling <code>childSpi()</code> on any children not yet in the + * cache. Then for all children it locks the subnode and calls this method + * on that node. After all subnodes have been purged the child cache is + * cleared, this nodes removed flag is set and any listeners are called. + */ + private void purge() throws BackingStoreException + { + // Make sure all children have an AbstractPreferences node in cache + String children[] = childrenNamesSpi(); + for (int i = 0; i < children.length; i++) { + if (childCache.get(children[i]) == null) + childCache.put(children[i], childSpi(children[i])); + } + + // purge all children + Iterator i = childCache.values().iterator(); + while (i.hasNext()) { + AbstractPreferences node = (AbstractPreferences) i.next(); + synchronized(node) { + node.purge(); + } + } + + // Cache is empty now + childCache.clear(); + + // remove this node + removeNodeSpi(); + removed = true; + + // XXX - check for listeners + } + + // listener methods + + /** + * XXX + */ + public void addNodeChangeListener(NodeChangeListener listener) { + // XXX + } + + public void addPreferenceChangeListener(PreferenceChangeListener listener) { + // XXX + } + + public void removeNodeChangeListener(NodeChangeListener listener) { + // XXX + } + + public void removePreferenceChangeListener + (PreferenceChangeListener listener) + { + // XXX + } + + // abstract spi methods + + /** + * Returns the names of the sub nodes of this preference node. + * This method only has to return any not yet cached child names, + * but may return all names if that is easier. It must not return + * null when there are no children, it has to return an empty array + * in that case. Since this method must consult the backing store to + * get all the sub node names it may throw a BackingStoreException. + * <p> + * Called by <code>childrenNames()</code> with this node locked. + */ + protected abstract String[] childrenNamesSpi() throws BackingStoreException; + + /** + * Returns a child note with the given name. + * This method is called by the <code>node()</code> method (indirectly + * through the <code>getNode()</code> helper method) with this node locked + * if a sub node with this name does not already exist in the child cache. + * If the child node did not aleady exist in the backing store the boolean + * field <code>newNode</code> of the returned node should be set. + * <p> + * Note that this method should even return a non-null child node if the + * backing store is not available since it may not throw a + * <code>BackingStoreException</code>. + */ + protected abstract AbstractPreferences childSpi(String name); + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + * <p> + * Called by <code>keys()</code> with this node locked if this node has + * not been removed. May throw an exception when the backing store cannot + * be accessed. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + protected abstract String[] keysSpi() throws BackingStoreException; + + /** + * Returns the value associated with the key in this preferences node or + * null when the key does not exist in this preferences node. + * <p> + * Called by <code>key()</code> with this node locked after checking that + * key is valid, not null and that the node has not been removed. + * <code>key()</code> will catch any exceptions that this method throws. + */ + protected abstract String getSpi(String key); + + /** + * Sets the value of the given preferences entry for this node. + * The implementation is not required to propagate the change to the + * backing store immediatly. It may not throw an exception when it tries + * to write to the backing store and that operation fails, the failure + * should be registered so a later invocation of <code>flush()</code> + * or <code>sync()</code> can signal the failure. + * <p> + * Called by <code>put()</code> with this node locked after checking that + * key and value are valid and non-null. + */ + protected abstract void putSpi(String key, String value); + + /** + * Removes the given key entry from this preferences node. + * The implementation is not required to propagate the change to the + * backing store immediatly. It may not throw an exception when it tries + * to write to the backing store and that operation fails, the failure + * should be registered so a later invocation of <code>flush()</code> + * or <code>sync()</code> can signal the failure. + * <p> + * Called by <code>remove()</code> with this node locked after checking + * that the key is valid and non-null. + */ + protected abstract void removeSpi(String key); + + /** + * Writes all entries of this preferences node that have not yet been + * written to the backing store and possibly creates this node in the + * backing store, if it does not yet exist. Should only write changes to + * this node and not write changes to any subnodes. + * Note that the node can be already removed in this VM. To check if + * that is the case the implementation can call <code>isRemoved()</code>. + * <p> + * Called (indirectly) by <code>flush()</code> with this node locked. + */ + protected abstract void flushSpi() throws BackingStoreException; + + /** + * Writes all entries of this preferences node that have not yet been + * written to the backing store and reads any entries that have changed + * in the backing store but that are not yet visible in this VM. + * Should only sync this node and not change any of the subnodes. + * Note that the node can be already removed in this VM. To check if + * that is the case the implementation can call <code>isRemoved()</code>. + * <p> + * Called (indirectly) by <code>sync()</code> with this node locked. + */ + protected abstract void syncSpi() throws BackingStoreException; + + /** + * Clears this node from this VM and removes it from the backing store. + * After this method has been called the node is marked as removed. + * <p> + * Called (indirectly) by <code>removeNode()</code> with this node locked + * after all the sub nodes of this node have already been removed. + */ + protected abstract void removeNodeSpi() throws BackingStoreException; +} diff --git a/libjava/classpath/java/util/prefs/BackingStoreException.java b/libjava/classpath/java/util/prefs/BackingStoreException.java new file mode 100644 index 00000000000..0ba358a5688 --- /dev/null +++ b/libjava/classpath/java/util/prefs/BackingStoreException.java @@ -0,0 +1,104 @@ +/* BackingStoreException.java - chained exception thrown when backing store + fails + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Chained exception thrown when backing store fails. This exception is + * only thrown from methods that actually have to access the backing store, + * such as <code>clear(), keys(), childrenNames(), nodeExists(), removeNode(), + * flush(), sync(), exportNode(), exportSubTree()</code>; normal operations + * do not throw BackingStoreExceptions. + * + * <p>Note that although this class inherits the Serializable interface, an + * attempt to serialize will fail with a <code>NotSerializableException</code>. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.4 + * @status updated to 1.4 + */ +public class BackingStoreException extends Exception +{ + static final long serialVersionUID = 859796500401108469L; + + /** + * Creates a new exception with a descriptive message. + * + * @param message the message + */ + public BackingStoreException(String message) + { + super(message); + } + + /** + * Create a new exception with the given cause. + * + * @param cause the cause + */ + public BackingStoreException(Throwable cause) + { + super(cause); + } + + /** + * This class should not be serialized. + * + * @param o the output stream + */ + private void writeObject(ObjectOutputStream o) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.BackingStoreException"); + } + + /** + * This class should not be serialized. + * + * @param i the input stream + */ + private void readObject(ObjectInputStream i) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.BackingStoreException"); + } +} diff --git a/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java new file mode 100644 index 00000000000..f929b56f68e --- /dev/null +++ b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java @@ -0,0 +1,116 @@ +/* InvalidPreferencesFormatException - indicates reading prefs from stream + failed + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Indicates reading prefs from stream failed. Thrown by the + * <code>importPreferences()</code> method when the supplied input stream + * could not be read because it was not in the correct XML format. + * + * <p>Note that although this class inherits the Serializable interface, an + * attempt to serialize will fail with a <code>NotSerializableException</code>. + * </p> + * + * @author Mark Wielaard (mark@klomp.org) + * @see Preferences + * @since 1.4 + * @status updated to 1.4 + */ +public class InvalidPreferencesFormatException extends Exception +{ + static final long serialVersionUID = -791715184232119669L; + + /** + * Creates a new exception with a descriptive message. The cause remains + * uninitialized. + * + * @param message the message + */ + public InvalidPreferencesFormatException(String message) + { + super(message); + } + + /** + * Creates a new exception with the given cause. + * + * @param cause the cause + */ + public InvalidPreferencesFormatException(Throwable cause) + { + super(cause); + } + + /** + * Creates a new exception with a descriptive message and a cause. + * + * @param message the message + * @param cause the cause + */ + public InvalidPreferencesFormatException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * This class should not be serialized. + * + * @param o the output stream + */ + private void writeObject(ObjectOutputStream o) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.InvalidPreferencesFormatException"); + } + + /** + * This class should not be serialized. + * + * @param i the input stream + */ + private void readObject(ObjectInputStream i) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.InvalidPreferencesFormatException"); + } +} diff --git a/libjava/classpath/java/util/prefs/NodeChangeEvent.java b/libjava/classpath/java/util/prefs/NodeChangeEvent.java new file mode 100644 index 00000000000..89986db88b3 --- /dev/null +++ b/libjava/classpath/java/util/prefs/NodeChangeEvent.java @@ -0,0 +1,91 @@ +/* NodeChangeEvent - ObjectEvent fired when a Preference node is added/removed + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventObject; + +/** + * ObjectEvent fired when a Preference node is added/removed. + * This event is only generated when a new subnode is added or a subnode is + * removed from a preference node. Changes in the entries of a preference node + * are indicated with a <code>PreferenceChangeEvent</code>. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public class NodeChangeEvent extends EventObject { + + private static final long serialVersionUID =8068949086596572957L; + + /** + * The sub node that was added or removed. + * Defined transient just like <code>EventObject.source</code> since + * this object should be serializable, but Preferences is in general not + * serializable. + */ + private final transient Preferences child; + + /** + * Creates a new NodeChangeEvent. + * + * @param parentNode The source preference node from which a subnode was + * added or removed + * @param childNode The preference node that was added or removed + */ + public NodeChangeEvent(Preferences parentNode, Preferences childNode) { + super(parentNode); + child = childNode; + } + + /** + * Returns the source parent preference node from which a subnode was + * added or removed. + */ + public Preferences getParent() { + return (Preferences) source; + } + + /** + * Returns the child preference subnode that was added or removed. + * To see wether it is still a valid preference node one has to call + * <code>event.getChild().nodeExists("")</code>. + */ + public Preferences getChild() { + return child; + } +} diff --git a/libjava/classpath/java/util/prefs/NodeChangeListener.java b/libjava/classpath/java/util/prefs/NodeChangeListener.java new file mode 100644 index 00000000000..19664c6652d --- /dev/null +++ b/libjava/classpath/java/util/prefs/NodeChangeListener.java @@ -0,0 +1,64 @@ +/* NodeChangeListener - EventListener for Preferences node addition/removal + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventListener; + +/** + * EventListener for Preferences node addition/removal. + * <p> + * Note that these events are only generated for the addition and removal + * of sub nodes from the preference node. Entry changes in the preference + * node can be monitored with a <code>PreferenceChangeListener</code>. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface NodeChangeListener extends EventListener { + + /** + * Fired when a sub node is added to the preference node. + */ + void childAdded(NodeChangeEvent event); + + /** + * Fired when a sub node is removed from the preference node. + */ + void childRemoved(NodeChangeEvent event); + +} diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java new file mode 100644 index 00000000000..fe371f15e4e --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java @@ -0,0 +1,105 @@ +/* PreferenceChangeEvent - ObjectEvent fired when a Preferences entry changes + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventObject; + +/** + * ObjectEvent fired when a Preferences entry changes. + * This event is generated when a entry is added, changed or removed. + * When an entry is removed then <code>getNewValue</code> will return null. + * <p> + * Preference change events are only generated for entries in one particular + * preference node. Notification of subnode addition/removal is given by a + * <code>NodeChangeEvent</code>. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public class PreferenceChangeEvent extends EventObject { + + private static final long serialVersionUID = 793724513368024975L; + + /** + * The key of the changed entry. + */ + private final String key; + + /** + * The new value of the changed entry, or null when the entry was removed. + */ + private final String newValue; + + /** + * Creates a new PreferenceChangeEvent. + * + * @param node The source preference node for which an entry was added, + * changed or removed + * @param key The key of the entry that was added, changed or removed + * @param value The new value of the entry that was added or changed, or + * null when the entry was removed + */ + public PreferenceChangeEvent(Preferences node, String key, String value) { + super(node); + this.key = key; + this.newValue = value; + } + + /** + * Returns the source Preference node from which an entry was added, + * changed or removed. + */ + public Preferences getNode() { + return (Preferences) source; + } + + /** + * Returns the key of the entry that was added, changed or removed. + */ + public String getKey() { + return key; + } + + /** + * Returns the new value of the entry that was added or changed, or + * returns null when the entry was removed. + */ + public String getNewValue() { + return newValue; + } +} diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeListener.java b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java new file mode 100644 index 00000000000..adff3582079 --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java @@ -0,0 +1,60 @@ +/* PreferenceChangeListener - EventListener for Preferences entry changes + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventListener; + +/** + * EventListener for Preferences entry addition, change or removal. + * <p> + * Preference change events are only generated for entries in one particular + * preference node. Notification of subnode addition/removal can be monitored + * with a <code>NodeChangeListener</code>. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface PreferenceChangeListener extends EventListener { + + /** + * Fired when a entry has been added, changed or removed from the + * preference node. + */ + void preferenceChange(PreferenceChangeEvent event); + +} diff --git a/libjava/classpath/java/util/prefs/Preferences.java b/libjava/classpath/java/util/prefs/Preferences.java new file mode 100644 index 00000000000..c407ae6127a --- /dev/null +++ b/libjava/classpath/java/util/prefs/Preferences.java @@ -0,0 +1,668 @@ +/* Preferences -- Preference node containing key value entries and subnodes + Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import gnu.java.util.prefs.NodeReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; + +/** + * Preference node containing key value entries and subnodes. + * <p> + * There are two preference node trees, a system tree which can be accessed + * by calling <code>systemRoot()</code> containing system preferences usefull + * for all users, and a user tree that can be accessed by calling + * <code>userRoot()</code> containing preferences that can differ between + * different users. How different users are identified is implementation + * depended. It can be determined by Thread, Access Control Context or Subject. + * <p> + * This implementation uses the "java.util.prefs.PreferencesFactory" system + * property to find a class that implement <code>PreferencesFactory</code> + * and initialized that class (if it has a public no arguments contructor) + * to get at the actual system or user root. If the system property is not set, + * or the class cannot be initialized it uses the default implementation + * <code>gnu.java.util.prefs.FileBasedFactory</code>. + * <p> + * Besides the two static method above to get the roots of the system and user + * preference node trees there are also two convenience methods to access the + * default preference node for a particular package an object is in. These are + * <code>userNodeForPackage()</code> and <code>systemNodeForPackage()</code>. + * Both methods take an Object as an argument so accessing preferences values + * can be as easy as calling <code>Preferences.userNodeForPackage(this)</code>. + * <p> + * Note that if a security manager is installed all static methods check for + * <code>RuntimePermission("preferences")</code>. But if this permission is + * given to the code then it can access and change all (user) preference nodes + * and entries. So you should be carefull not to store to sensitive information + * or make security decissions based on preference values since there is no + * more fine grained control over what preference values can be changed once + * code has been given the correct runtime permission. + * <p> + * XXX + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class Preferences { + + // Static Fields + + /** + * Default PreferencesFactory class used when the system property + * "java.util.prefs.PreferencesFactory" is not set. + * <p> + * XXX - Currently set to MemoryBasedFactory, should be changed + * when FileBasedPreferences backend works. + */ + private static final String defaultFactoryClass + = "gnu.java.util.prefs.MemoryBasedFactory"; + + /** Permission needed to access system or user root. */ + private static final Permission prefsPermission + = new RuntimePermission("preferences"); + + /** + * The preferences factory object that supplies the system and user root. + * Set and returned by the getFactory() method. + */ + private static PreferencesFactory factory; + + /** Maximum node name length. 80 characters. */ + public static final int MAX_NAME_LENGTH = 80; + + /** Maximum entry key length. 80 characters. */ + public static final int MAX_KEY_LENGTH = 80; + + /** Maximum entry value length. 8192 characters. */ + public static final int MAX_VALUE_LENGTH = 8192; + + // Constructors + + /** + * Creates a new Preferences node. Can only be used by subclasses. + * Empty implementation. + */ + protected Preferences() {} + + // Static methods + + /** + * Returns the system preferences root node containing usefull preferences + * for all users. It is save to cache this value since it should always + * return the same preference node. + * + * @return the root system preference node + * @exception SecurityException when a security manager is installed and + * the caller does not have <code>RuntimePermission("preferences")</code>. + */ + public static Preferences systemRoot() throws SecurityException { + // Get the preferences factory and check for permission + PreferencesFactory factory = getFactory(); + + return factory.systemRoot(); + } + + /** + * Returns the user preferences root node containing preferences for the + * the current user. How different users are identified is implementation + * depended. It can be determined by Thread, Access Control Context or + * Subject. + * + * @return the root user preference node + * @exception SecurityException when a security manager is installed and + * the caller does not have <code>RuntimePermission("preferences")</code>. + */ + public static Preferences userRoot() throws SecurityException { + // Get the preferences factory and check for permission + PreferencesFactory factory = getFactory(); + return factory.userRoot(); + } + + /** + * Private helper method for <code>systemRoot()</code> and + * <code>userRoot()</code>. Checks security permission and instantiates the + * correct factory if it has not yet been set. + * <p> + * When the preferences factory has not yet been set this method first + * tries to get the system propery "java.util.prefs.PreferencesFactory" + * and tries to initializes that class. If the system property is not set + * or initialization fails it returns an instance of the default factory + * <code>gnu.java.util.prefs.FileBasedPreferencesFactory</code>. + * + * @return the preferences factory to use + * @exception SecurityException when a security manager is installed and + * the caller does not have <code>RuntimePermission("preferences")</code>. + */ + private static PreferencesFactory getFactory() throws SecurityException { + + // First check for permission + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(prefsPermission); + } + + // Get the factory + if (factory == null) { + // Caller might not have enough permissions + factory = (PreferencesFactory) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + PreferencesFactory pf = null; + String className = System.getProperty + ("java.util.prefs.PreferencesFactory"); + if (className != null) { + try { + Class fc = Class.forName(className); + Object o = fc.newInstance(); + pf = (PreferencesFactory) o; + } catch (ClassNotFoundException cnfe) + {/*ignore*/} + catch (InstantiationException ie) + {/*ignore*/} + catch (IllegalAccessException iae) + {/*ignore*/} + catch (ClassCastException cce) + {/*ignore*/} + } + return pf; + } + }); + + // Still no factory? Use our default. + if (factory == null) + { + try + { + Class cls = Class.forName (defaultFactoryClass); + factory = (PreferencesFactory) cls.newInstance(); + } + catch (Exception e) + { + throw new RuntimeException ("Couldn't load default factory" + + " '"+ defaultFactoryClass +"'"); + // XXX - when using 1.4 compatible throwables add cause + } + } + + } + + return factory; + } + + /** + * Returns the system preferences node for the package of an object. + * The package node name of the object is determined by dropping the + * class name of the object of the fully quallified class name and + * replacing all '.' to '/' in the package name. If the class of the + * object has no package then the package node name is "<unnamed>". + * The returened node is <code>systemRoot().node(packageNodeName)</code>. + * + * @param o Object whose default system preference node is requested + * @returns system preferences node that should be used by object o + * @exception SecurityException when a security manager is installed and + * the caller does not have <code>RuntimePermission("preferences")</code>. + */ + public static Preferences systemNodeForPackage(Class c) + throws SecurityException + { + return nodeForPackage(c, systemRoot()); + } + + /** + * Returns the user preferences node for the package of an object. + * The package node name of the object is determined by dropping the + * class name of the object of the fully quallified class name and + * replacing all '.' to '/' in the package name. If the class of the + * object has no package then the package node name is "<unnamed>". + * The returened node is <code>userRoot().node(packageNodeName)</code>. + * + * @param o Object whose default user preference node is requested + * @returns user preferences node that should be used by object o + * @exception SecurityException when a security manager is installed and + * the caller does not have <code>RuntimePermission("preferences")</code>. + */ + public static Preferences userNodeForPackage(Class c) + throws SecurityException + { + return nodeForPackage(c, userRoot()); + } + + /** + * Private helper method for <code>systemNodeForPackage()</code> and + * <code>userNodeForPackage()</code>. Given the correct system or user + * root it returns the correct Preference node for the package node name + * of the given object. + */ + private static Preferences nodeForPackage(Class c, Preferences root) { + // Get the package path + String className = c.getName(); + String packagePath; + int index = className.lastIndexOf('.'); + if(index == -1) { + packagePath = "<unnamed>"; + } else { + packagePath = className.substring(0,index).replace('.','/'); + } + + return root.node(packagePath); + } + + /** + * XXX + */ + public static void importPreferences(InputStream is) + throws InvalidPreferencesFormatException, + IOException + { + PreferencesFactory factory = getFactory(); + NodeReader reader = new NodeReader(is, factory); + reader.importPreferences(); + } + + // abstract methods (identification) + + /** + * Returns the absolute path name of this preference node. + * The absolute path name of a node is the path name of its parent node + * plus a '/' plus its own name. If the node is the root node and has no + * parent then its name is "" and its absolute path name is "/". + */ + public abstract String absolutePath(); + + /** + * Returns true if this node comes from the user preferences tree, false + * if it comes from the system preferences tree. + */ + public abstract boolean isUserNode(); + + /** + * Returns the name of this preferences node. The name of the node cannot + * be null, can be mostly 80 characters and cannot contain any '/' + * characters. The root node has as name "". + */ + public abstract String name(); + + /** + * Returns the String given by + * <code> + * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath() + * </code> + */ + public abstract String toString(); + + // abstract methods (navigation) + + /** + * Returns all the direct sub nodes of this preferences node. + * Needs access to the backing store to give a meaningfull answer. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException when this node has been removed + */ + public abstract String[] childrenNames() throws BackingStoreException; + + /** + * Returns a sub node of this preferences node if the given path is + * relative (does not start with a '/') or a sub node of the root + * if the path is absolute (does start with a '/'). + * + * @exception IllegalStateException if this node has been removed + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public abstract Preferences node(String path); + + /** + * Returns true if the node that the path points to exists in memory or + * in the backing store. Otherwise it returns false or an exception is + * thrown. When this node is removed the only valid parameter is the + * empty string (indicating this node), the return value in that case + * will be false. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + * and the path is not the empty string (indicating this node) + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public abstract boolean nodeExists(String path) + throws BackingStoreException; + + /** + * Returns the parent preferences node of this node or null if this is + * the root of the preferences tree. + * + * @exception IllegalStateException if this node has been removed + */ + public abstract Preferences parent(); + + // abstract methods (export) + + /** + * XXX + */ + public abstract void exportNode(OutputStream os) + throws BackingStoreException, + IOException; + + /** + * XXX + */ + public abstract void exportSubtree(OutputStream os) + throws BackingStoreException, + IOException; + + // abstract methods (preference entry manipulation) + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract String[] keys() throws BackingStoreException; + + /** + * Returns the value associated with the key in this preferences node. If + * the default value of the key cannot be found in the preferences node + * entries or something goes wrong with the backing store the supplied + * default value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract String get(String key, String defaultVal); + + /** + * Convenience method for getting the given entry as a boolean. + * When the string representation of the requested entry is either + * "true" or "false" (ignoring case) then that value is returned, + * otherwise the given default boolean value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract boolean getBoolean(String key, boolean defaultVal); + + /** + * Convenience method for getting the given entry as a byte array. + * When the string representation of the requested entry is a valid + * Base64 encoded string (without any other characters, such as newlines) + * then the decoded Base64 string is returned as byte array, + * otherwise the given default byte array value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract byte[] getByteArray(String key, byte[] defaultVal); + + /** + * Convenience method for getting the given entry as a double. + * When the string representation of the requested entry can be decoded + * with <code>Double.parseDouble()</code> then that double is returned, + * otherwise the given default double value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract double getDouble(String key, double defaultVal); + + /** + * Convenience method for getting the given entry as a float. + * When the string representation of the requested entry can be decoded + * with <code>Float.parseFloat()</code> then that float is returned, + * otherwise the given default float value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract float getFloat(String key, float defaultVal); + + /** + * Convenience method for getting the given entry as an integer. + * When the string representation of the requested entry can be decoded + * with <code>Integer.parseInt()</code> then that integer is returned, + * otherwise the given default integer value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract int getInt(String key, int defaultVal); + + /** + * Convenience method for getting the given entry as a long. + * When the string representation of the requested entry can be decoded + * with <code>Long.parseLong()</code> then that long is returned, + * otherwise the given default long value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract long getLong(String key, long defaultVal); + + /** + * Sets the value of the given preferences entry for this node. + * Key and value cannot be null, the key cannot exceed 80 characters + * and the value cannot exceed 8192 characters. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void put(String key, String value); + + /** + * Convenience method for setting the given entry as a boolean. + * The boolean is converted with <code>Boolean.toString(value)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putBoolean(String key, boolean value); + + /** + * Convenience method for setting the given entry as an array of bytes. + * The byte array is converted to a Base64 encoded string + * and then stored in the preference entry as that string. + * <p> + * Note that a byte array encoded as a Base64 string will be about 1.3 + * times larger then the original length of the byte array, which means + * that the byte array may not be larger about 6 KB. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putByteArray(String key, byte[] value); + + /** + * Convenience method for setting the given entry as a double. + * The double is converted with <code>Double.toString(double)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putDouble(String key, double value); + + /** + * Convenience method for setting the given entry as a float. + * The float is converted with <code>Float.toString(float)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putFloat(String key, float value); + + /** + * Convenience method for setting the given entry as an integer. + * The integer is converted with <code>Integer.toString(int)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putInt(String key, int value); + + /** + * Convenience method for setting the given entry as a long. + * The long is converted with <code>Long.toString(long)</code> + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putLong(String key, long value); + + /** + * Removes the preferences entry from this preferences node. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void remove(String key); + + // abstract methods (preference node manipulation) + + /** + * Removes all entries from this preferences node. May need access to the + * backing store to get and clear all entries. + * <p> + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void clear() throws BackingStoreException; + + /** + * Writes all preference changes on this and any subnode that have not + * yet been written to the backing store. This has no effect on the + * preference entries in this VM, but it makes sure that all changes + * are visible to other programs (other VMs might need to call the + * <code>sync()</code> method to actually see the changes to the backing + * store. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void flush() throws BackingStoreException; + + /** + * Writes and reads all preference changes to and from this and any + * subnodes. This makes sure that all local changes are written to the + * backing store and that all changes to the backing store are visible + * in this preference node (and all subnodes). + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void sync() throws BackingStoreException; + + /** + * Removes this and all subnodes from the backing store and clears all + * entries. After removal this instance will not be useable (except for + * a few methods that don't throw a <code>InvalidStateException</code>), + * even when a new node with the same path name is created this instance + * will not be usable again. The root (system or user) may never be removed. + * <p> + * Note that according to the specification an implementation may delay + * removal of the node from the backing store till the <code>flush()</code> + * method is called. But the <code>flush()</code> method may throw a + * <code>IllegalStateException</code> when the node has been removed. + * So most implementations will actually remove the node and any subnodes + * from the backing store immediatly. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has already been removed + * @exception UnsupportedOperationException if this is a root node + */ + public abstract void removeNode() throws BackingStoreException; + + // abstract methods (listeners) + + public abstract void addNodeChangeListener(NodeChangeListener listener); + + public abstract void addPreferenceChangeListener + (PreferenceChangeListener listener); + + public abstract void removeNodeChangeListener(NodeChangeListener listener); + + public abstract void removePreferenceChangeListener + (PreferenceChangeListener listener); +} + diff --git a/libjava/classpath/java/util/prefs/PreferencesFactory.java b/libjava/classpath/java/util/prefs/PreferencesFactory.java new file mode 100644 index 00000000000..f4fe7e37ffe --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferencesFactory.java @@ -0,0 +1,65 @@ +/* PreferencesFactory - Preferences system and user root factory interface + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.prefs; + +/** + * Preferences system and user root factory interface. Defines how to get + * to the system and user root preferences objects. Should be implemented by + * new preferences backends. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface PreferencesFactory { + + /** + * Returns the system root preferences node. Should always return the + * same object. + */ + Preferences systemRoot(); + + /** + * Returns the user root preferences node. May return different objects + * depending on the user that called this method. The user may for example + * be determined by the current Thread or the Subject associated with the + * current AccessControllContext. + */ + Preferences userRoot(); + +} diff --git a/libjava/classpath/java/util/prefs/package.html b/libjava/classpath/java/util/prefs/package.html new file mode 100644 index 00000000000..65fc1ac7991 --- /dev/null +++ b/libjava/classpath/java/util/prefs/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.prefs package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.prefs</title></head> + +<body> +<p>Utility classes for storing and retrieving user and system preferences.</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/regex/Matcher.java b/libjava/classpath/java/util/regex/Matcher.java new file mode 100644 index 00000000000..bd97ace54a8 --- /dev/null +++ b/libjava/classpath/java/util/regex/Matcher.java @@ -0,0 +1,301 @@ +/* Matcher.java -- Instance of a regular expression applied to a char sequence. + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.regex; + +import gnu.regexp.REMatch; + +/** + * Instance of a regular expression applied to a char sequence. + * + * @since 1.4 + */ +public final class Matcher +{ + private Pattern pattern; + private CharSequence input; + private int position; + private int appendPosition; + private REMatch match; + + Matcher(Pattern pattern, CharSequence input) + { + this.pattern = pattern; + this.input = input; + } + + /** + * @param sb The target string buffer + * @param replacement The replacement string + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public Matcher appendReplacement (StringBuffer sb, String replacement) + throws IllegalStateException + { + assertMatchOp(); + sb.append(input.subSequence(appendPosition, + match.getStartIndex()).toString()); + sb.append(match.substituteInto(replacement)); + appendPosition = match.getEndIndex(); + return this; + } + + /** + * @param sb The target string buffer + */ + public StringBuffer appendTail (StringBuffer sb) + { + sb.append(input.subSequence(appendPosition, input.length()).toString()); + return sb; + } + + /** + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public int end () + throws IllegalStateException + { + assertMatchOp(); + return match.getEndIndex(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public int end (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.getEndIndex(group); + } + + public boolean find () + { + boolean first = (match == null); + match = pattern.getRE().getMatch(input, position); + if (match != null) + { + int endIndex = match.getEndIndex(); + // Are we stuck at the same position? + if (!first && endIndex == position) + { + match = null; + // Not at the end of the input yet? + if (position < input.length() - 1) + { + position++; + return find(position); + } + else + return false; + } + position = endIndex; + return true; + } + return false; + } + + /** + * @param start The index to start the new pattern matching + * + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public boolean find (int start) + { + match = pattern.getRE().getMatch(input, start); + if (match != null) + { + position = match.getEndIndex(); + return true; + } + return false; + } + + /** + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public String group () + { + assertMatchOp(); + return match.toString(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public String group (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.toString(group); + } + + /** + * @param replacement The replacement string + */ + public String replaceFirst (String replacement) + { + reset(); + // Semantics might not quite match + return pattern.getRE().substitute(input, replacement, position); + } + + /** + * @param replacement The replacement string + */ + public String replaceAll (String replacement) + { + reset(); + return pattern.getRE().substituteAll(input, replacement, position); + } + + public int groupCount () + { + return pattern.getRE().getNumSubs(); + } + + public boolean lookingAt () + { + match = pattern.getRE().getMatch(input, 0); + if (match != null) + { + if (match.getStartIndex() == 0) + { + position = match.getEndIndex(); + return true; + } + match = null; + } + return false; + } + + /** + * Attempts to match the entire input sequence against the pattern. + * + * If the match succeeds then more information can be obtained via the + * start, end, and group methods. + * + * @see #start + * @see #end + * @see #group + */ + public boolean matches () + { + if (lookingAt()) + { + if (position == input.length()) + return true; + match = null; + } + return false; + } + + /** + * Returns the Pattern that is interpreted by this Matcher + */ + public Pattern pattern () + { + return pattern; + } + + public Matcher reset () + { + position = 0; + match = null; + return this; + } + + /** + * @param input The new input character sequence + */ + public Matcher reset (CharSequence input) + { + this.input = input; + return reset(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public int start () + throws IllegalStateException + { + assertMatchOp(); + return match.getStartIndex(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public int start (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.getStartIndex(group); + } + + private void assertMatchOp() + { + if (match == null) throw new IllegalStateException(); + } +} diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java new file mode 100644 index 00000000000..6a31ef9bfd2 --- /dev/null +++ b/libjava/classpath/java/util/regex/Pattern.java @@ -0,0 +1,254 @@ +/* Pattern.java -- Compiled regular expression ready to be applied. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.regex; + +import gnu.regexp.RE; +import gnu.regexp.REException; +import gnu.regexp.RESyntax; + +import java.io.Serializable; +import java.util.ArrayList; + + +/** + * Compiled regular expression ready to be applied. + * + * @since 1.4 + */ +public final class Pattern implements Serializable +{ + private static final long serialVersionUID = 5073258162644648461L; + + public static final int CANON_EQ = 128; + public static final int CASE_INSENSITIVE = 2; + public static final int COMMENTS = 4; + public static final int DOTALL = 32; + public static final int MULTILINE = 8; + public static final int UNICODE_CASE = 64; + public static final int UNIX_LINES = 1; + + private final String regex; + private final int flags; + + private final RE re; + + private Pattern (String regex, int flags) + throws PatternSyntaxException + { + this.regex = regex; + this.flags = flags; + + int gnuFlags = 0; + if ((flags & CASE_INSENSITIVE) != 0) + gnuFlags |= RE.REG_ICASE; + if ((flags & MULTILINE) != 0) + gnuFlags |= RE.REG_MULTILINE; + if ((flags & DOTALL) != 0) + gnuFlags |= RE.REG_DOT_NEWLINE; + // not yet supported: + // if ((flags & UNICODE_CASE) != 0) gnuFlags = + // if ((flags & CANON_EQ) != 0) gnuFlags = + + RESyntax syntax = RESyntax.RE_SYNTAX_JAVA_1_4; + if ((flags & UNIX_LINES) != 0) + { + // Use a syntax set with \n for linefeeds? + syntax = new RESyntax(syntax); + syntax.setLineSeparator("\n"); + } + + if ((flags & COMMENTS) != 0) + { + // Use a syntax with support for comments? + } + + try + { + this.re = new RE(regex, gnuFlags, syntax); + } + catch (REException e) + { + throw new PatternSyntaxException(e.getMessage(), + regex, e.getPosition()); + } + } + + // package private accessor method + RE getRE() + { + return re; + } + + /** + * @param regex The regular expression + * + * @exception PatternSyntaxException If the expression's syntax is invalid + */ + public static Pattern compile (String regex) + throws PatternSyntaxException + { + return compile(regex, 0); + } + + /** + * @param regex The regular expression + * @param flags The match flags, a bit mask + * + * @exception PatternSyntaxException If the expression's syntax is invalid + * @exception IllegalArgumentException If bit values other than those + * corresponding to the defined match flags are set in flags + */ + public static Pattern compile (String regex, int flags) + throws PatternSyntaxException + { + // FIXME: check which flags are really accepted + if ((flags & ~0xEF) != 0) + throw new IllegalArgumentException (); + + return new Pattern (regex, flags); + } + + public int flags () + { + return this.flags; + } + + /** + * @param regex The regular expression + * @param input The character sequence to be matched + * + * @exception PatternSyntaxException If the expression's syntax is invalid + */ + public static boolean matches (String regex, CharSequence input) + { + return compile(regex).matcher(input).matches(); + } + + /** + * @param input The character sequence to be matched + */ + public Matcher matcher (CharSequence input) + { + return new Matcher(this, input); + } + + /** + * @param input The character sequence to be matched + */ + public String[] split (CharSequence input) + { + return split(input, 0); + } + + /** + * @param input The character sequence to be matched + * @param limit The result threshold + */ + public String[] split (CharSequence input, int limit) + { + Matcher matcher = new Matcher(this, input); + ArrayList list = new ArrayList(); + int empties = 0; + int count = 0; + int start = 0; + int end; + boolean matched = matcher.find(); + + while (matched && (limit <= 0 || count < limit - 1)) + { + ++count; + end = matcher.start(); + if (start == end) + empties++; + else + { + while (empties > 0) + { + list.add(""); + empties--; + } + + String text = input.subSequence(start, end).toString(); + list.add(text); + } + start = matcher.end(); + matched = matcher.find(); + } + + // We matched nothing. + if (!matched && count == 0) + return new String[] { input.toString() }; + + // Is the last token empty? + boolean emptyLast = (start == input.length()); + + // Can/Must we add empties or an extra last token at the end? + if (list.size() < limit || limit < 0 || (limit == 0 && !emptyLast)) + { + if (limit > list.size()) + { + int max = limit - list.size(); + empties = (empties > max) ? max : empties; + } + while (empties > 0) + { + list.add(""); + empties--; + } + } + + // last token at end + if (limit != 0 || (limit == 0 && !emptyLast)) + { + String t = input.subSequence(start, input.length()).toString(); + if ("".equals(t) && limit == 0) + ; // Don't add. + else + list.add(t); + } + + String[] output = new String [list.size()]; + list.toArray(output); + return output; + } + + public String pattern () + { + return regex; + } +} diff --git a/libjava/classpath/java/util/regex/PatternSyntaxException.java b/libjava/classpath/java/util/regex/PatternSyntaxException.java new file mode 100644 index 00000000000..0c80e119c37 --- /dev/null +++ b/libjava/classpath/java/util/regex/PatternSyntaxException.java @@ -0,0 +1,132 @@ +/* PatternSyntaxException - Indicates illegal pattern for regular expression. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.regex; + +/** + * Indicates illegal pattern for regular expression. + * Includes state to inspect the pattern and what and where the expression + * was not valid regular expression. + */ +public class PatternSyntaxException extends IllegalArgumentException +{ + private static final long serialVersionUID = -3864639126226059218L; + + /** + * Human readable escription of the syntax error. + */ + private final String desc; + + /** + * The original pattern that contained the syntax error. + */ + private final String pattern; + + /** + * Index of the first character in the String that was probably invalid, + * or -1 when unknown. + */ + private final int index; + + /** + * Creates a new PatternSyntaxException. + * + * @param description Human readable escription of the syntax error. + * @param pattern The original pattern that contained the syntax error. + * @param index Index of the first character in the String that was + * probably invalid, or -1 when unknown. + */ + public PatternSyntaxException(String description, + String pattern, + int index) + { + super(description); + this.desc = description; + this.pattern = pattern; + this.index = index; + } + + /** + * Returns a human readable escription of the syntax error. + */ + public String getDescription() + { + return desc; + } + + /** + * Returns the original pattern that contained the syntax error. + */ + public String getPattern() + { + return pattern; + } + + /** + * Returns the index of the first character in the String that was probably + * invalid, or -1 when unknown. + */ + public int getIndex() + { + return index; + } + + /** + * Returns a string containing a line with the description, a line with + * the original pattern and a line indicating with a ^ which character is + * probably the first invalid character in the pattern if the index is not + * negative. + */ + public String getMessage() + { + String lineSep = System.getProperty("line.separator"); + StringBuffer sb = new StringBuffer(desc); + sb.append(lineSep); + sb.append('\t'); + sb.append(pattern); + if (index != -1) + { + sb.append(lineSep); + sb.append('\t'); + for (int i=0; i<index; i++) + sb.append(' '); + sb.append('^'); + } + return sb.toString(); + } + +} diff --git a/libjava/classpath/java/util/regex/package.html b/libjava/classpath/java/util/regex/package.html new file mode 100644 index 00000000000..0573a360c38 --- /dev/null +++ b/libjava/classpath/java/util/regex/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.regex package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.regex</title></head> + +<body> +<p>Regular expression patterns and matchers.</p> + +</body> +</html> diff --git a/libjava/classpath/java/util/zip/Adler32.java b/libjava/classpath/java/util/zip/Adler32.java new file mode 100644 index 00000000000..7c411384057 --- /dev/null +++ b/libjava/classpath/java/util/zip/Adler32.java @@ -0,0 +1,205 @@ +/* Adler32.java - Computes Adler32 data checksum of a data stream + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * The actual Adler32 algorithm is taken from RFC 1950. + * Status: Believed complete and correct. + */ + +/** + * Computes Adler32 checksum for a stream of data. An Adler32 + * checksum is not as reliable as a CRC32 checksum, but a lot faster to + * compute. + *<p> + * The specification for Adler32 may be found in RFC 1950. + * (ZLIB Compressed Data Format Specification version 3.3) + *<p> + *<p> + * From that document: + *<p> + * "ADLER32 (Adler-32 checksum) + * This contains a checksum value of the uncompressed data + * (excluding any dictionary data) computed according to Adler-32 + * algorithm. This algorithm is a 32-bit extension and improvement + * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + * standard. + *<p> + * Adler-32 is composed of two sums accumulated per byte: s1 is + * the sum of all bytes, s2 is the sum of all s1 values. Both sums + * are done modulo 65521. s1 is initialized to 1, s2 to zero. The + * Adler-32 checksum is stored as s2*65536 + s1 in most- + * significant-byte first (network) order." + *<p> + * "8.2. The Adler-32 algorithm + *<p> + * The Adler-32 algorithm is much faster than the CRC32 algorithm yet + * still provides an extremely low probability of undetected errors. + *<p> + * The modulo on unsigned long accumulators can be delayed for 5552 + * bytes, so the modulo operation time is negligible. If the bytes + * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + * and order sensitive, unlike the first sum, which is just a + * checksum. That 65521 is prime is important to avoid a possible + * large class of two-byte errors that leave the check unchanged. + * (The Fletcher checksum uses 255, which is not prime and which also + * makes the Fletcher check insensitive to single byte changes 0 <-> + * 255.) + *<p> + * The sum s1 is initialized to 1 instead of zero to make the length + * of the sequence part of s2, so that the length does not have to be + * checked separately. (Any sequence of zeroes has a Fletcher + * checksum of zero.)" + * + * @author John Leuner, Per Bothner + * @since JDK 1.1 + * + * @see InflaterInputStream + * @see DeflaterOutputStream + */ +public class Adler32 implements Checksum +{ + + /** largest prime smaller than 65536 */ + private static final int BASE = 65521; + + private int checksum; //we do all in int. + + //Note that java doesn't have unsigned integers, + //so we have to be careful with what arithmetic + //we do. We return the checksum as a long to + //avoid sign confusion. + + /** + * Creates a new instance of the <code>Adler32</code> class. + * The checksum starts off with a value of 1. + */ + public Adler32 () + { + reset(); + } + + /** + * Resets the Adler32 checksum to the initial value. + */ + public void reset () + { + checksum = 1; //Initialize to 1 + } + + /** + * Updates the checksum with the byte b. + * + * @param bval the data value to add. The high byte of the int is ignored. + */ + public void update (int bval) + { + //We could make a length 1 byte array and call update again, but I + //would rather not have that overhead + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + s1 = (s1 + (bval & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checksum = (s2 << 16) + s1; + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buffer an array of bytes + */ + public void update (byte[] buffer) + { + update(buffer, 0, buffer.length); + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buf an array of bytes + * @param off the start of the data used for this update + * @param len the number of bytes to use for this update + */ + public void update (byte[] buf, int off, int len) + { + //(By Per Bothner) + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + while (len > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > len) + n = len; + len -= n; + while (--n >= 0) + { + s1 = s1 + (buf[off++] & 0xFF); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + + /*Old implementation, borrowed from somewhere: + int n; + + while (len-- > 0) { + + s1 = (s1 + (bs[offset++] & 0xff)) % BASE; + s2 = (s2 + s1) % BASE; + }*/ + + checksum = (s2 << 16) | s1; + } + + /** + * Returns the Adler32 data checksum computed so far. + */ + public long getValue() + { + return (long) checksum & 0xffffffffL; + } +} diff --git a/libjava/classpath/java/util/zip/CRC32.java b/libjava/classpath/java/util/zip/CRC32.java new file mode 100644 index 00000000000..1c2b3973852 --- /dev/null +++ b/libjava/classpath/java/util/zip/CRC32.java @@ -0,0 +1,132 @@ +/* CRC32.java - Computes CRC32 data checksum of a data stream + Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * The actual CRC32 algorithm is taken from RFC 1952. + * Status: Believed complete and correct. + */ + +/** + * Computes CRC32 data checksum of a data stream. + * The actual CRC32 algorithm is described in RFC 1952 + * (GZIP file format specification version 4.3). + * Can be used to get the CRC32 over a stream if used with checked input/output + * streams. + * + * @see InflaterInputStream + * @see DeflaterOutputStream + * + * @author Per Bothner + * @date April 1, 1999. + */ +public class CRC32 implements Checksum +{ + /** The crc data checksum so far. */ + private int crc = 0; + + /** The fast CRC table. Computed once when the CRC32 class is loaded. */ + private static int[] crc_table = make_crc_table(); + + /** Make the table for a fast CRC. */ + private static int[] make_crc_table () + { + int[] crc_table = new int[256]; + for (int n = 0; n < 256; n++) + { + int c = n; + for (int k = 8; --k >= 0; ) + { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + return crc_table; + } + + /** + * Returns the CRC32 data checksum computed so far. + */ + public long getValue () + { + return (long) crc & 0xffffffffL; + } + + /** + * Resets the CRC32 data checksum as if no update was ever called. + */ + public void reset () { crc = 0; } + + /** + * Updates the checksum with the int bval. + * + * @param bval (the byte is taken as the lower 8 bits of bval) + */ + + public void update (int bval) + { + int c = ~crc; + c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the byte array to the data checksum. + * + * @param buf the buffer which contains the data + * @param off the offset in the buffer where the data starts + * @param len the length of the data + */ + public void update (byte[] buf, int off, int len) + { + int c = ~crc; + while (--len >= 0) + c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the complete byte array to the data checksum. + */ + public void update (byte[] buf) { update(buf, 0, buf.length); } +} diff --git a/libjava/classpath/java/util/zip/CheckedInputStream.java b/libjava/classpath/java/util/zip/CheckedInputStream.java new file mode 100644 index 00000000000..d743fbb2447 --- /dev/null +++ b/libjava/classpath/java/util/zip/CheckedInputStream.java @@ -0,0 +1,135 @@ +/* CheckedInputStream.java - Compute checksum of data being read + Copyright (C) 1999, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * InputStream that computes a checksum of the data being read using a + * supplied Checksum object. + * + * @see Checksum + * + * @author Tom Tromey + * @date May 17, 1999 + */ +public class CheckedInputStream extends FilterInputStream +{ + /** + * Creates a new CheckInputStream on top of the supplied OutputStream + * using the supplied Checksum. + */ + public CheckedInputStream (InputStream in, Checksum sum) + { + super (in); + this.sum = sum; + } + + /** + * Returns the Checksum object used. To get the data checksum computed so + * far call <code>getChecksum.getValue()</code>. + */ + public Checksum getChecksum () + { + return sum; + } + + /** + * Reads one byte, updates the checksum and returns the read byte + * (or -1 when the end of file was reached). + */ + public int read () throws IOException + { + int x = in.read(); + if (x != -1) + sum.update(x); + return x; + } + + /** + * Reads at most len bytes in the supplied buffer and updates the checksum + * with it. Returns the number of bytes actually read or -1 when the end + * of file was reached. + */ + public int read (byte[] buf, int off, int len) throws IOException + { + int r = in.read(buf, off, len); + if (r != -1) + sum.update(buf, off, r); + return r; + } + + /** + * Skips n bytes by reading them in a temporary buffer and updating the + * the checksum with that buffer. Returns the actual number of bytes skiped + * which can be less then requested when the end of file is reached. + */ + public long skip (long n) throws IOException + { + if (n == 0) + return 0; + + int min = (int) Math.min(n, 1024); + byte[] buf = new byte[min]; + + long s = 0; + while (n > 0) + { + int r = in.read(buf, 0, min); + if (r == -1) + break; + n -= r; + s += r; + min = (int) Math.min(n, 1024); + sum.update(buf, 0, r); + } + + return s; + } + + /** The checksum object. */ + private Checksum sum; +} diff --git a/libjava/classpath/java/util/zip/CheckedOutputStream.java b/libjava/classpath/java/util/zip/CheckedOutputStream.java new file mode 100644 index 00000000000..a3c19292fae --- /dev/null +++ b/libjava/classpath/java/util/zip/CheckedOutputStream.java @@ -0,0 +1,100 @@ +/* CheckedOutputStream.java - Compute checksum of data being written. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * OutputStream that computes a checksum of data being written using a + * supplied Checksum object. + * + * @see Checksum + * + * @author Tom Tromey + * @date May 17, 1999 + */ +public class CheckedOutputStream extends FilterOutputStream +{ + /** + * Creates a new CheckInputStream on top of the supplied OutputStream + * using the supplied Checksum. + */ + public CheckedOutputStream (OutputStream out, Checksum cksum) + { + super (out); + this.sum = cksum; + } + + /** + * Returns the Checksum object used. To get the data checksum computed so + * far call <code>getChecksum.getValue()</code>. + */ + public Checksum getChecksum () + { + return sum; + } + + /** + * Writes one byte to the OutputStream and updates the Checksum. + */ + public void write (int bval) throws IOException + { + out.write(bval); + sum.update(bval); + } + + /** + * Writes the byte array to the OutputStream and updates the Checksum. + */ + public void write (byte[] buf, int off, int len) throws IOException + { + out.write(buf, off, len); + sum.update(buf, off, len); + } + + /** The checksum object. */ + private Checksum sum; +} diff --git a/libjava/classpath/java/util/zip/Checksum.java b/libjava/classpath/java/util/zip/Checksum.java new file mode 100644 index 00000000000..3342ba3a609 --- /dev/null +++ b/libjava/classpath/java/util/zip/Checksum.java @@ -0,0 +1,86 @@ +/* Checksum.java - Interface to compute a data checksum + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * Interface to compute a data checksum used by checked input/output streams. + * A data checksum can be updated by one byte or with a byte array. After each + * update the value of the current checksum can be returned by calling + * <code>getValue</code>. The complete checksum object can also be reset + * so it can be used again with new data. + * + * @see CheckedInputStream + * @see CheckedOutputStream + * + * @author Per Bothner + * @author Jochen Hoenicke + */ +public interface Checksum +{ + /** + * Returns the data checksum computed so far. + */ + long getValue(); + + /** + * Resets the data checksum as if no update was ever called. + */ + void reset(); + + /** + * Adds one byte to the data checksum. + * + * @param bval the data value to add. The high byte of the int is ignored. + */ + void update (int bval); + + /** + * Adds the byte array to the data checksum. + * + * @param buf the buffer which contains the data + * @param off the offset in the buffer where the data starts + * @param len the length of the data + */ + void update (byte[] buf, int off, int len); +} diff --git a/libjava/classpath/java/util/zip/DataFormatException.java b/libjava/classpath/java/util/zip/DataFormatException.java new file mode 100644 index 00000000000..dc5b10dec90 --- /dev/null +++ b/libjava/classpath/java/util/zip/DataFormatException.java @@ -0,0 +1,71 @@ +/* DataformatException.java -- thrown when compressed data is corrupt + Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * Exception thrown when compressed data is corrupt. + * + * @author Tom Tromey + * @author John Leuner + * @since 1.1 + * @status updated to 1.4 + */ +public class DataFormatException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2219632870893641452L; + + /** + * Create an exception without a message. + */ + public DataFormatException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public DataFormatException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/zip/Deflater.java b/libjava/classpath/java/util/zip/Deflater.java new file mode 100644 index 00000000000..7bc1a1911eb --- /dev/null +++ b/libjava/classpath/java/util/zip/Deflater.java @@ -0,0 +1,520 @@ +/* Deflater.java - Compress a data stream + Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This is the Deflater class. The deflater class compresses input + * with the deflate algorithm described in RFC 1951. It has several + * compression levels and three different strategies described below. + * + * This class is <i>not</i> thread safe. This is inherent in the API, due + * to the split of deflate and setInput. + * + * @author Jochen Hoenicke + * @author Tom Tromey + */ +public class Deflater +{ + /** + * The best and slowest compression level. This tries to find very + * long and distant string repetitions. + */ + public static final int BEST_COMPRESSION = 9; + /** + * The worst but fastest compression level. + */ + public static final int BEST_SPEED = 1; + /** + * The default compression level. + */ + public static final int DEFAULT_COMPRESSION = -1; + /** + * This level won't compress at all but output uncompressed blocks. + */ + public static final int NO_COMPRESSION = 0; + + /** + * The default strategy. + */ + public static final int DEFAULT_STRATEGY = 0; + /** + * This strategy will only allow longer string repetitions. It is + * useful for random data with a small character set. + */ + public static final int FILTERED = 1; + + /** + * This strategy will not look for string repetitions at all. It + * only encodes with Huffman trees (which means, that more common + * characters get a smaller encoding. + */ + public static final int HUFFMAN_ONLY = 2; + + /** + * The compression method. This is the only method supported so far. + * There is no need to use this constant at all. + */ + public static final int DEFLATED = 8; + + /* + * The Deflater can do the following state transitions: + * + * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. + * / | (2) (5) | + * / v (5) | + * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) + * \ | (3) | ,-------' + * | | | (3) / + * v v (5) v v + * (1) -> BUSY_STATE ----> FINISHING_STATE + * | (6) + * v + * FINISHED_STATE + * \_____________________________________/ + * | (7) + * v + * CLOSED_STATE + * + * (1) If we should produce a header we start in INIT_STATE, otherwise + * we start in BUSY_STATE. + * (2) A dictionary may be set only when we are in INIT_STATE, then + * we change the state as indicated. + * (3) Whether a dictionary is set or not, on the first call of deflate + * we change to BUSY_STATE. + * (4) -- intentionally left blank -- :) + * (5) FINISHING_STATE is entered, when flush() is called to indicate that + * there is no more INPUT. There are also states indicating, that + * the header wasn't written yet. + * (6) FINISHED_STATE is entered, when everything has been flushed to the + * internal pending output buffer. + * (7) At any time (7) + * + */ + + private static final int IS_SETDICT = 0x01; + private static final int IS_FLUSHING = 0x04; + private static final int IS_FINISHING = 0x08; + + private static final int INIT_STATE = 0x00; + private static final int SETDICT_STATE = 0x01; + private static final int INIT_FINISHING_STATE = 0x08; + private static final int SETDICT_FINISHING_STATE = 0x09; + private static final int BUSY_STATE = 0x10; + private static final int FLUSHING_STATE = 0x14; + private static final int FINISHING_STATE = 0x1c; + private static final int FINISHED_STATE = 0x1e; + private static final int CLOSED_STATE = 0x7f; + + /** Compression level. */ + private int level; + + /** should we include a header. */ + private boolean noHeader; + + /** The current state. */ + private int state; + + /** The total bytes of output written. */ + private int totalOut; + + /** The pending output. */ + private DeflaterPending pending; + + /** The deflater engine. */ + private DeflaterEngine engine; + + /** + * Creates a new deflater with default compression level. + */ + public Deflater() + { + this(DEFAULT_COMPRESSION, false); + } + + /** + * Creates a new deflater with given compression level. + * @param lvl the compression level, a value between NO_COMPRESSION + * and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + * @exception IllegalArgumentException if lvl is out of range. + */ + public Deflater(int lvl) + { + this(lvl, false); + } + + /** + * Creates a new deflater with given compression level. + * @param lvl the compression level, a value between NO_COMPRESSION + * and BEST_COMPRESSION. + * @param nowrap true, iff we should suppress the deflate header at the + * beginning and the adler checksum at the end of the output. This is + * useful for the GZIP format. + * @exception IllegalArgumentException if lvl is out of range. + */ + public Deflater(int lvl, boolean nowrap) + { + if (lvl == DEFAULT_COMPRESSION) + lvl = 6; + else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) + throw new IllegalArgumentException(); + + pending = new DeflaterPending(); + engine = new DeflaterEngine(pending); + this.noHeader = nowrap; + setStrategy(DEFAULT_STRATEGY); + setLevel(lvl); + reset(); + } + + /** + * Resets the deflater. The deflater acts afterwards as if it was + * just created with the same compression level and strategy as it + * had before. + */ + public void reset() + { + state = (noHeader ? BUSY_STATE : INIT_STATE); + totalOut = 0; + pending.reset(); + engine.reset(); + } + + /** + * Frees all objects allocated by the compressor. There's no + * reason to call this, since you can just rely on garbage + * collection. Exists only for compatibility against Sun's JDK, + * where the compressor allocates native memory. + * If you call any method (even reset) afterwards the behaviour is + * <i>undefined</i>. + * @deprecated Just clear all references to deflater instead. + */ + public void end() + { + engine = null; + pending = null; + state = CLOSED_STATE; + } + + /** + * Gets the current adler checksum of the data that was processed so + * far. + */ + public int getAdler() + { + return engine.getAdler(); + } + + /** + * Gets the number of input bytes processed so far. + */ + public int getTotalIn() + { + return engine.getTotalIn(); + } + + /** + * Gets the number of output bytes so far. + */ + public int getTotalOut() + { + return totalOut; + } + + /** + * Finalizes this object. + */ + protected void finalize() + { + /* Exists solely for compatibility. We don't have any native state. */ + } + + /** + * Flushes the current input block. Further calls to deflate() will + * produce enough output to inflate everything in the current input + * block. This is not part of Sun's JDK so I have made it package + * private. It is used by DeflaterOutputStream to implement + * flush(). + */ + void flush() { + state |= IS_FLUSHING; + } + + /** + * Finishes the deflater with the current input block. It is an error + * to give more input after this method was called. This method must + * be called to force all bytes to be flushed. + */ + public void finish() { + state |= IS_FLUSHING | IS_FINISHING; + } + + /** + * Returns true iff the stream was finished and no more output bytes + * are available. + */ + public boolean finished() + { + return state == FINISHED_STATE && pending.isFlushed(); + } + + /** + * Returns true, if the input buffer is empty. + * You should then call setInput(). <br> + * + * <em>NOTE</em>: This method can also return true when the stream + * was finished. + */ + public boolean needsInput() + { + return engine.needsInput(); + } + + /** + * Sets the data which should be compressed next. This should be only + * called when needsInput indicates that more input is needed. + * If you call setInput when needsInput() returns false, the + * previous input that is still pending will be thrown away. + * The given byte array should not be changed, before needsInput() returns + * true again. + * This call is equivalent to <code>setInput(input, 0, input.length)</code>. + * @param input the buffer containing the input data. + * @exception IllegalStateException if the buffer was finished() or ended(). + */ + public void setInput(byte[] input) + { + setInput(input, 0, input.length); + } + + /** + * Sets the data which should be compressed next. This should be + * only called when needsInput indicates that more input is needed. + * The given byte array should not be changed, before needsInput() returns + * true again. + * @param input the buffer containing the input data. + * @param off the start of the data. + * @param len the length of the data. + * @exception IllegalStateException if the buffer was finished() or ended() + * or if previous input is still pending. + */ + public void setInput(byte[] input, int off, int len) + { + if ((state & IS_FINISHING) != 0) + throw new IllegalStateException("finish()/end() already called"); + engine.setInput(input, off, len); + } + + /** + * Sets the compression level. There is no guarantee of the exact + * position of the change, but if you call this when needsInput is + * true the change of compression level will occur somewhere near + * before the end of the so far given input. + * @param lvl the new compression level. + */ + public void setLevel(int lvl) + { + if (lvl == DEFAULT_COMPRESSION) + lvl = 6; + else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) + throw new IllegalArgumentException(); + + + if (level != lvl) + { + level = lvl; + engine.setLevel(lvl); + } + } + + /** + * Sets the compression strategy. Strategy is one of + * DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact + * position where the strategy is changed, the same as for + * setLevel() applies. + * @param stgy the new compression strategy. + */ + public void setStrategy(int stgy) + { + if (stgy != DEFAULT_STRATEGY && stgy != FILTERED + && stgy != HUFFMAN_ONLY) + throw new IllegalArgumentException(); + engine.setStrategy(stgy); + } + + /** + * Deflates the current input block to the given array. It returns + * the number of bytes compressed, or 0 if either + * needsInput() or finished() returns true or length is zero. + * @param output the buffer where to write the compressed data. + */ + public int deflate(byte[] output) + { + return deflate(output, 0, output.length); + } + + /** + * Deflates the current input block to the given array. It returns + * the number of bytes compressed, or 0 if either + * needsInput() or finished() returns true or length is zero. + * @param output the buffer where to write the compressed data. + * @param offset the offset into the output array. + * @param length the maximum number of bytes that may be written. + * @exception IllegalStateException if end() was called. + * @exception IndexOutOfBoundsException if offset and/or length + * don't match the array length. + */ + public int deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (state == CLOSED_STATE) + throw new IllegalStateException("Deflater closed"); + + if (state < BUSY_STATE) + { + /* output header */ + int header = (DEFLATED + + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; + int level_flags = (level - 1) >> 1; + if (level_flags < 0 || level_flags > 3) + level_flags = 3; + header |= level_flags << 6; + if ((state & IS_SETDICT) != 0) + /* Dictionary was set */ + header |= DeflaterConstants.PRESET_DICT; + header += 31 - (header % 31); + + pending.writeShortMSB(header); + if ((state & IS_SETDICT) != 0) + { + int chksum = engine.getAdler(); + engine.resetAdler(); + pending.writeShortMSB(chksum >> 16); + pending.writeShortMSB(chksum & 0xffff); + } + + state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + } + + for (;;) + { + int count = pending.flush(output, offset, length); + offset += count; + totalOut += count; + length -= count; + if (length == 0 || state == FINISHED_STATE) + break; + + if (!engine.deflate((state & IS_FLUSHING) != 0, + (state & IS_FINISHING) != 0)) + { + if (state == BUSY_STATE) + /* We need more input now */ + return origLength - length; + else if (state == FLUSHING_STATE) + { + if (level != NO_COMPRESSION) + { + /* We have to supply some lookahead. 8 bit lookahead + * are needed by the zlib inflater, and we must fill + * the next byte, so that all bits are flushed. + */ + int neededbits = 8 + ((-pending.getBitCount()) & 7); + while (neededbits > 0) + { + /* write a static tree block consisting solely of + * an EOF: + */ + pending.writeBits(2, 10); + neededbits -= 10; + } + } + state = BUSY_STATE; + } + else if (state == FINISHING_STATE) + { + pending.alignToByte(); + /* We have completed the stream */ + if (!noHeader) + { + int adler = engine.getAdler(); + pending.writeShortMSB(adler >> 16); + pending.writeShortMSB(adler & 0xffff); + } + state = FINISHED_STATE; + } + } + } + + return origLength - length; + } + + /** + * Sets the dictionary which should be used in the deflate process. + * This call is equivalent to <code>setDictionary(dict, 0, + * dict.length)</code>. + * @param dict the dictionary. + * @exception IllegalStateException if setInput () or deflate () + * were already called or another dictionary was already set. + */ + public void setDictionary(byte[] dict) + { + setDictionary(dict, 0, dict.length); + } + + /** + * Sets the dictionary which should be used in the deflate process. + * The dictionary should be a byte array containing strings that are + * likely to occur in the data which should be compressed. The + * dictionary is not stored in the compressed output, only a + * checksum. To decompress the output you need to supply the same + * dictionary again. + * @param dict the dictionary. + * @param offset an offset into the dictionary. + * @param length the length of the dictionary. + * @exception IllegalStateException if setInput () or deflate () were + * already called or another dictionary was already set. + */ + public void setDictionary(byte[] dict, int offset, int length) + { + if (state != INIT_STATE) + throw new IllegalStateException(); + + state = SETDICT_STATE; + engine.setDictionary(dict, offset, length); + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterConstants.java b/libjava/classpath/java/util/zip/DeflaterConstants.java new file mode 100644 index 00000000000..bfef86344a6 --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterConstants.java @@ -0,0 +1,78 @@ +/* java.util.zip.DeflaterConstants + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +interface DeflaterConstants +{ + boolean DEBUGGING = false; + + int STORED_BLOCK = 0; + int STATIC_TREES = 1; + int DYN_TREES = 2; + int PRESET_DICT = 0x20; + + int DEFAULT_MEM_LEVEL = 8; + + int MAX_MATCH = 258; + int MIN_MATCH = 3; + + int MAX_WBITS = 15; + int WSIZE = 1 << MAX_WBITS; + int WMASK = WSIZE - 1; + + int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + int HASH_SIZE = 1 << HASH_BITS; + int HASH_MASK = HASH_SIZE - 1; + int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5); + + int DEFLATE_STORED = 0; + int DEFLATE_FAST = 1; + int DEFLATE_SLOW = 2; + + int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 }; + int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 }; + int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 }; + int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 }; + int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 }; +} diff --git a/libjava/classpath/java/util/zip/DeflaterEngine.java b/libjava/classpath/java/util/zip/DeflaterEngine.java new file mode 100644 index 00000000000..3eea7c250fc --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterEngine.java @@ -0,0 +1,696 @@ +/* DeflaterEngine.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class DeflaterEngine implements DeflaterConstants +{ + private static final int TOO_FAR = 4096; + + private int ins_h; + + /** + * Hashtable, hashing three characters to an index for window, so + * that window[index]..window[index+2] have this hash code. + * Note that the array should really be unsigned short, so you need + * to and the values with 0xffff. + */ + private short[] head; + + /** + * prev[index & WMASK] points to the previous index that has the + * same hash code as the string starting at index. This way + * entries with the same hash code are in a linked list. + * Note that the array should really be unsigned short, so you need + * to and the values with 0xffff. + */ + private short[] prev; + + private int matchStart, matchLen; + private boolean prevAvailable; + private int blockStart; + + /** + * strstart points to the current character in window. + */ + private int strstart; + + /** + * lookahead is the number of characters starting at strstart in + * window that are valid. + * So window[strstart] until window[strstart+lookahead-1] are valid + * characters. + */ + private int lookahead; + + /** + * This array contains the part of the uncompressed stream that + * is of relevance. The current character is indexed by strstart. + */ + private byte[] window; + + private int strategy, max_chain, max_lazy, niceLength, goodLength; + + /** The current compression function. */ + private int comprFunc; + + /** The input data for compression. */ + private byte[] inputBuf; + + /** The total bytes of input read. */ + private int totalIn; + + /** The offset into inputBuf, where input data starts. */ + private int inputOff; + + /** The end offset of the input data. */ + private int inputEnd; + + private DeflaterPending pending; + private DeflaterHuffman huffman; + + /** The adler checksum */ + private Adler32 adler; + + /* DEFLATE ALGORITHM: + * + * The uncompressed stream is inserted into the window array. When + * the window array is full the first half is thrown away and the + * second half is copied to the beginning. + * + * The head array is a hash table. Three characters build a hash value + * and they the value points to the corresponding index in window of + * the last string with this hash. The prev array implements a + * linked list of matches with the same hash: prev[index & WMASK] points + * to the previous index with the same hash. + * + * + */ + + + DeflaterEngine(DeflaterPending pending) { + this.pending = pending; + huffman = new DeflaterHuffman(pending); + adler = new Adler32(); + + window = new byte[2*WSIZE]; + head = new short[HASH_SIZE]; + prev = new short[WSIZE]; + + /* We start at index 1, to avoid a implementation deficiency, that + * we cannot build a repeat pattern at index 0. + */ + blockStart = strstart = 1; + } + + public void reset() + { + huffman.reset(); + adler.reset(); + blockStart = strstart = 1; + lookahead = 0; + totalIn = 0; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + for (int i = 0; i < HASH_SIZE; i++) + head[i] = 0; + for (int i = 0; i < WSIZE; i++) + prev[i] = 0; + } + + public final void resetAdler() + { + adler.reset(); + } + + public final int getAdler() + { + int chksum = (int) adler.getValue(); + return chksum; + } + + public final int getTotalIn() + { + return totalIn; + } + + public final void setStrategy(int strat) + { + strategy = strat; + } + + public void setLevel(int lvl) + { + goodLength = DeflaterConstants.GOOD_LENGTH[lvl]; + max_lazy = DeflaterConstants.MAX_LAZY[lvl]; + niceLength = DeflaterConstants.NICE_LENGTH[lvl]; + max_chain = DeflaterConstants.MAX_CHAIN[lvl]; + + if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("Change from "+comprFunc +" to " + + DeflaterConstants.COMPR_FUNC[lvl]); + switch (comprFunc) + { + case DEFLATE_STORED: + if (strstart > blockStart) + { + huffman.flushStoredBlock(window, blockStart, + strstart - blockStart, false); + blockStart = strstart; + } + updateHash(); + break; + case DEFLATE_FAST: + if (strstart > blockStart) + { + huffman.flushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + break; + case DEFLATE_SLOW: + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + if (strstart > blockStart) + { + huffman.flushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + prevAvailable = false; + matchLen = MIN_MATCH - 1; + break; + } + comprFunc = COMPR_FUNC[lvl]; + } + } + + private void updateHash() { + if (DEBUGGING) + System.err.println("updateHash: "+strstart); + ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; + } + + /** + * Inserts the current string in the head hash and returns the previous + * value for this hash. + */ + private int insertString() { + short match; + int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) + & HASH_MASK; + + if (DEBUGGING) + { + if (hash != (((window[strstart] << (2*HASH_SHIFT)) + ^ (window[strstart + 1] << HASH_SHIFT) + ^ (window[strstart + 2])) & HASH_MASK)) + throw new InternalError("hash inconsistent: "+hash+"/" + +window[strstart]+"," + +window[strstart+1]+"," + +window[strstart+2]+","+HASH_SHIFT); + } + + prev[strstart & WMASK] = match = head[hash]; + head[hash] = (short) strstart; + ins_h = hash; + return match & 0xffff; + } + + private void slideWindow() + { + System.arraycopy(window, WSIZE, window, 0, WSIZE); + matchStart -= WSIZE; + strstart -= WSIZE; + blockStart -= WSIZE; + + /* Slide the hash table (could be avoided with 32 bit values + * at the expense of memory usage). + */ + for (int i = 0; i < HASH_SIZE; i++) + { + int m = head[i] & 0xffff; + head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; + } + + /* Slide the prev table. + */ + for (int i = 0; i < WSIZE; i++) + { + int m = prev[i] & 0xffff; + prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; + } + } + + /** + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * OUT assertions: strstart + lookahead <= 2*WSIZE + * lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd + */ + private void fillWindow() + { + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (strstart >= WSIZE + MAX_DIST) + slideWindow(); + + /* If there is not enough lookahead, but still some input left, + * read in the input + */ + while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) + { + int more = 2*WSIZE - lookahead - strstart; + + if (more > inputEnd - inputOff) + more = inputEnd - inputOff; + + System.arraycopy(inputBuf, inputOff, + window, strstart + lookahead, more); + adler.update(inputBuf, inputOff, more); + inputOff += more; + totalIn += more; + lookahead += more; + } + + if (lookahead >= MIN_MATCH) + updateHash(); + } + + /** + * Find the best (longest) string in the window matching the + * string starting at strstart. + * + * Preconditions: + * strstart + MAX_MATCH <= window.length. + * + * + * @param curMatch + */ + private boolean findLongestMatch(int curMatch) { + int chainLength = this.max_chain; + int niceLength = this.niceLength; + short[] prev = this.prev; + int scan = this.strstart; + int match; + int best_end = this.strstart + matchLen; + int best_len = Math.max(matchLen, MIN_MATCH - 1); + + int limit = Math.max(strstart - MAX_DIST, 0); + + int strend = scan + MAX_MATCH - 1; + byte scan_end1 = window[best_end - 1]; + byte scan_end = window[best_end]; + + /* Do not waste too much time if we already have a good match: */ + if (best_len >= this.goodLength) + chainLength >>= 2; + + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (niceLength > lookahead) + niceLength = lookahead; + + if (DeflaterConstants.DEBUGGING + && strstart > 2*WSIZE - MIN_LOOKAHEAD) + throw new InternalError("need lookahead"); + + do { + if (DeflaterConstants.DEBUGGING && curMatch >= strstart) + throw new InternalError("future match"); + if (window[curMatch + best_len] != scan_end + || window[curMatch + best_len - 1] != scan_end1 + || window[curMatch] != window[scan] + || window[curMatch+1] != window[scan + 1]) + continue; + + match = curMatch + 2; + scan += 2; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + while (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && scan < strend); + + if (scan > best_end) { +// if (DeflaterConstants.DEBUGGING && ins_h == 0) +// System.err.println("Found match: "+curMatch+"-"+(scan-strstart)); + matchStart = curMatch; + best_end = scan; + best_len = scan - strstart; + if (best_len >= niceLength) + break; + + scan_end1 = window[best_end-1]; + scan_end = window[best_end]; + } + scan = strstart; + } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit + && --chainLength != 0); + + matchLen = Math.min(best_len, lookahead); + return matchLen >= MIN_MATCH; + } + + void setDictionary(byte[] buffer, int offset, int length) { + if (DeflaterConstants.DEBUGGING && strstart != 1) + throw new IllegalStateException("strstart not 1"); + adler.update(buffer, offset, length); + if (length < MIN_MATCH) + return; + if (length > MAX_DIST) { + offset += length - MAX_DIST; + length = MAX_DIST; + } + + System.arraycopy(buffer, offset, window, strstart, length); + + updateHash(); + length--; + while (--length > 0) + { + insertString(); + strstart++; + } + strstart += 2; + blockStart = strstart; + } + + private boolean deflateStored(boolean flush, boolean finish) + { + if (!flush && lookahead == 0) + return false; + + strstart += lookahead; + lookahead = 0; + + int storedLen = strstart - blockStart; + + if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) + /* Block is full */ + || (blockStart < WSIZE && storedLen >= MAX_DIST) + /* Block may move out of window */ + || flush) + { + boolean lastBlock = finish; + if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) + { + storedLen = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + + if (DeflaterConstants.DEBUGGING) + System.err.println("storedBlock["+storedLen+","+lastBlock+"]"); + + huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock); + blockStart += storedLen; + return !lastBlock; + } + return true; + } + + private boolean deflateFast(boolean flush, boolean finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + return false; + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + /* We are flushing everything */ + huffman.flushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as findLongestMatch need this. + * This should only happen when flushing and the window + * is almost full. + */ + slideWindow(); + } + + int hashHead; + if (lookahead >= MIN_MATCH + && (hashHead = insertString()) != 0 + && strategy != Deflater.HUFFMAN_ONLY + && strstart - hashHead <= MAX_DIST + && findLongestMatch(hashHead)) + { + /* longestMatch sets matchStart and matchLen */ + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) + { + if (window[strstart+i] != window[matchStart + i]) + throw new InternalError(); + } + } + huffman.tallyDist(strstart - matchStart, matchLen); + + lookahead -= matchLen; + if (matchLen <= max_lazy && lookahead >= MIN_MATCH) + { + while (--matchLen > 0) + { + strstart++; + insertString(); + } + strstart++; + } + else + { + strstart += matchLen; + if (lookahead >= MIN_MATCH - 1) + updateHash(); + } + matchLen = MIN_MATCH - 1; + continue; + } + else + { + /* No match found */ + huffman.tallyLit(window[strstart] & 0xff); + strstart++; + lookahead--; + } + + if (huffman.isFull()) + { + boolean lastBlock = finish && lookahead == 0; + huffman.flushBlock(window, blockStart, strstart - blockStart, + lastBlock); + blockStart = strstart; + return !lastBlock; + } + } + return true; + } + + private boolean deflateSlow(boolean flush, boolean finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + return false; + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + prevAvailable = false; + + /* We are flushing everything */ + if (DeflaterConstants.DEBUGGING && !flush) + throw new InternalError("Not flushing, but no lookahead"); + huffman.flushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as findLongestMatch need this. + * This should only happen when flushing and the window + * is almost full. + */ + slideWindow(); + } + + int prevMatch = matchStart; + int prevLen = matchLen; + if (lookahead >= MIN_MATCH) + { + int hashHead = insertString(); + if (strategy != Deflater.HUFFMAN_ONLY + && hashHead != 0 && strstart - hashHead <= MAX_DIST + && findLongestMatch(hashHead)) + { + /* longestMatch sets matchStart and matchLen */ + + /* Discard match if too small and too far away */ + if (matchLen <= 5 + && (strategy == Deflater.FILTERED + || (matchLen == MIN_MATCH + && strstart - matchStart > TOO_FAR))) { + matchLen = MIN_MATCH - 1; + } + } + } + + /* previous match was better */ + if (prevLen >= MIN_MATCH && matchLen <= prevLen) + { + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) + { + if (window[strstart-1+i] != window[prevMatch + i]) + throw new InternalError(); + } + } + huffman.tallyDist(strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do + { + strstart++; + lookahead--; + if (lookahead >= MIN_MATCH) + insertString(); + } + while (--prevLen > 0); + strstart ++; + lookahead--; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + } + else + { + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + prevAvailable = true; + strstart++; + lookahead--; + } + + if (huffman.isFull()) + { + int len = strstart - blockStart; + if (prevAvailable) + len--; + boolean lastBlock = (finish && lookahead == 0 && !prevAvailable); + huffman.flushBlock(window, blockStart, len, lastBlock); + blockStart += len; + return !lastBlock; + } + } + return true; + } + + public boolean deflate(boolean flush, boolean finish) + { + boolean progress; + do + { + fillWindow(); + boolean canFlush = flush && inputOff == inputEnd; + if (DeflaterConstants.DEBUGGING) + System.err.println("window: ["+blockStart+","+strstart+"," + +lookahead+"], "+comprFunc+","+canFlush); + switch (comprFunc) + { + case DEFLATE_STORED: + progress = deflateStored(canFlush, finish); + break; + case DEFLATE_FAST: + progress = deflateFast(canFlush, finish); + break; + case DEFLATE_SLOW: + progress = deflateSlow(canFlush, finish); + break; + default: + throw new InternalError(); + } + } + while (pending.isFlushed() /* repeat while we have no pending output */ + && progress); /* and progress was made */ + + return progress; + } + + public void setInput(byte[] buf, int off, int len) + { + if (inputOff < inputEnd) + throw new IllegalStateException + ("Old input was not completely processed"); + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + inputBuf = buf; + inputOff = off; + inputEnd = end; + } + + public final boolean needsInput() + { + return inputEnd == inputOff; + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterHuffman.java b/libjava/classpath/java/util/zip/DeflaterHuffman.java new file mode 100644 index 00000000000..d040dde485d --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterHuffman.java @@ -0,0 +1,776 @@ +/* DeflaterHuffman.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This is the DeflaterHuffman class. + * + * This class is <i>not</i> thread safe. This is inherent in the API, due + * to the split of deflate and setInput. + * + * @author Jochen Hoenicke + * @date Jan 6, 2000 + */ +class DeflaterHuffman +{ + private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + private static final int LITERAL_NUM = 286; + private static final int DIST_NUM = 30; + private static final int BITLEN_NUM = 19; + private static final int REP_3_6 = 16; + private static final int REP_3_10 = 17; + private static final int REP_11_138 = 18; + private static final int EOF_SYMBOL = 256; + private static final int[] BL_ORDER = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static String bit4Reverse = + "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; + + class Tree { + short[] freqs; + short[] codes; + byte[] length; + int[] bl_counts; + int minNumCodes, numCodes; + int maxLength; + + Tree(int elems, int minCodes, int maxLength) { + this.minNumCodes = minCodes; + this.maxLength = maxLength; + freqs = new short[elems]; + bl_counts = new int[maxLength]; + } + + void reset() { + for (int i = 0; i < freqs.length; i++) + freqs[i] = 0; + codes = null; + length = null; + } + + final void writeSymbol(int code) + { + if (DeflaterConstants.DEBUGGING) + { + freqs[code]--; +// System.err.print("writeSymbol("+freqs.length+","+code+"): "); + } + pending.writeBits(codes[code] & 0xffff, length[code]); + } + + final void checkEmpty() + { + boolean empty = true; + for (int i = 0; i < freqs.length; i++) + if (freqs[i] != 0) + { + System.err.println("freqs["+i+"] == "+freqs[i]); + empty = false; + } + if (!empty) + throw new InternalError(); + System.err.println("checkEmpty suceeded!"); + } + + void setStaticCodes(short[] stCodes, byte[] stLength) + { + codes = stCodes; + length = stLength; + } + + public void buildCodes() { + int[] nextCode = new int[maxLength]; + int code = 0; + codes = new short[freqs.length]; + + if (DeflaterConstants.DEBUGGING) + System.err.println("buildCodes: "+freqs.length); + for (int bits = 0; bits < maxLength; bits++) + { + nextCode[bits] = code; + code += bl_counts[bits] << (15 - bits); + if (DeflaterConstants.DEBUGGING) + System.err.println("bits: "+(bits+1)+" count: "+bl_counts[bits] + +" nextCode: "+Integer.toHexString(code)); + } + if (DeflaterConstants.DEBUGGING && code != 65536) + throw new RuntimeException("Inconsistent bl_counts!"); + + for (int i=0; i < numCodes; i++) + { + int bits = length[i]; + if (bits > 0) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("codes["+i+"] = rev(" + +Integer.toHexString(nextCode[bits-1])+")," + +bits); + codes[i] = bitReverse(nextCode[bits-1]); + nextCode[bits-1] += 1 << (16 - bits); + } + } + } + + private void buildLength(int childs[]) + { + this.length = new byte [freqs.length]; + int numNodes = childs.length / 2; + int numLeafs = (numNodes + 1) / 2; + int overflow = 0; + + for (int i = 0; i < maxLength; i++) + bl_counts[i] = 0; + + /* First calculate optimal bit lengths */ + int lengths[] = new int[numNodes]; + lengths[numNodes-1] = 0; + for (int i = numNodes - 1; i >= 0; i--) + { + if (childs[2*i+1] != -1) + { + int bitLength = lengths[i] + 1; + if (bitLength > maxLength) + { + bitLength = maxLength; + overflow++; + } + lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength; + } + else + { + /* A leaf node */ + int bitLength = lengths[i]; + bl_counts[bitLength - 1]++; + this.length[childs[2*i]] = (byte) lengths[i]; + } + } + + if (DeflaterConstants.DEBUGGING) + { + System.err.println("Tree "+freqs.length+" lengths:"); + for (int i=0; i < numLeafs; i++) + System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + + " len: "+length[childs[2*i]]); + } + + if (overflow == 0) + return; + + int incrBitLen = maxLength - 1; + do + { + /* Find the first bit length which could increase: */ + while (bl_counts[--incrBitLen] == 0) + ; + + /* Move this node one down and remove a corresponding + * amount of overflow nodes. + */ + do + { + bl_counts[incrBitLen]--; + bl_counts[++incrBitLen]++; + overflow -= 1 << (maxLength - 1 - incrBitLen); + } + while (overflow > 0 && incrBitLen < maxLength - 1); + } + while (overflow > 0); + + /* We may have overshot above. Move some nodes from maxLength to + * maxLength-1 in that case. + */ + bl_counts[maxLength-1] += overflow; + bl_counts[maxLength-2] -= overflow; + + /* Now recompute all bit lengths, scanning in increasing + * frequency. It is simpler to reconstruct all lengths instead of + * fixing only the wrong ones. This idea is taken from 'ar' + * written by Haruhiko Okumura. + * + * The nodes were inserted with decreasing frequency into the childs + * array. + */ + int nodePtr = 2 * numLeafs; + for (int bits = maxLength; bits != 0; bits--) + { + int n = bl_counts[bits-1]; + while (n > 0) + { + int childPtr = 2*childs[nodePtr++]; + if (childs[childPtr + 1] == -1) + { + /* We found another leaf */ + length[childs[childPtr]] = (byte) bits; + n--; + } + } + } + if (DeflaterConstants.DEBUGGING) + { + System.err.println("*** After overflow elimination. ***"); + for (int i=0; i < numLeafs; i++) + System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + + " len: "+length[childs[2*i]]); + } + } + + void buildTree() + { + int numSymbols = freqs.length; + + /* heap is a priority queue, sorted by frequency, least frequent + * nodes first. The heap is a binary tree, with the property, that + * the parent node is smaller than both child nodes. This assures + * that the smallest node is the first parent. + * + * The binary tree is encoded in an array: 0 is root node and + * the nodes 2*n+1, 2*n+2 are the child nodes of node n. + */ + int[] heap = new int[numSymbols]; + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) + { + int freq = freqs[n]; + if (freq != 0) + { + /* Insert n into heap */ + int pos = heapLen++; + int ppos; + while (pos > 0 && + freqs[heap[ppos = (pos - 1) / 2]] > freq) { + heap[pos] = heap[ppos]; + pos = ppos; + } + heap[pos] = n; + maxCode = n; + } + } + + /* We could encode a single literal with 0 bits but then we + * don't see the literals. Therefore we force at least two + * literals to avoid this case. We don't care about order in + * this case, both literals get a 1 bit code. + */ + while (heapLen < 2) + { + int node = maxCode < 2 ? ++maxCode : 0; + heap[heapLen++] = node; + } + + numCodes = Math.max(maxCode + 1, minNumCodes); + + int numLeafs = heapLen; + int[] childs = new int[4*heapLen - 2]; + int[] values = new int[2*heapLen - 1]; + int numNodes = numLeafs; + for (int i = 0; i < heapLen; i++) + { + int node = heap[i]; + childs[2*i] = node; + childs[2*i+1] = -1; + values[i] = freqs[node] << 8; + heap[i] = i; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do + { + int first = heap[0]; + int last = heap[--heapLen]; + + /* Propagate the hole to the leafs of the heap */ + int ppos = 0; + int path = 1; + while (path < heapLen) + { + if (path + 1 < heapLen + && values[heap[path]] > values[heap[path+1]]) + path++; + + heap[ppos] = heap[path]; + ppos = path; + path = path * 2 + 1; + } + + /* Now propagate the last element down along path. Normally + * it shouldn't go too deep. + */ + int lastVal = values[last]; + while ((path = ppos) > 0 + && values[heap[ppos = (path - 1)/2]] > lastVal) + heap[path] = heap[ppos]; + heap[path] = last; + + + int second = heap[0]; + + /* Create a new node father of first and second */ + last = numNodes++; + childs[2*last] = first; + childs[2*last+1] = second; + int mindepth = Math.min(values[first] & 0xff, values[second] & 0xff); + values[last] = lastVal = values[first] + values[second] - mindepth + 1; + + /* Again, propagate the hole to the leafs */ + ppos = 0; + path = 1; + while (path < heapLen) + { + if (path + 1 < heapLen + && values[heap[path]] > values[heap[path+1]]) + path++; + + heap[ppos] = heap[path]; + ppos = path; + path = ppos * 2 + 1; + } + + /* Now propagate the new element down along path */ + while ((path = ppos) > 0 + && values[heap[ppos = (path - 1)/2]] > lastVal) + heap[path] = heap[ppos]; + heap[path] = last; + } + while (heapLen > 1); + + if (heap[0] != childs.length / 2 - 1) + throw new RuntimeException("Weird!"); + + buildLength(childs); + } + + int getEncodedLength() + { + int len = 0; + for (int i = 0; i < freqs.length; i++) + len += freqs[i] * length[i]; + return len; + } + + void calcBLFreq(Tree blTree) { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.freqs[nextlen]++; + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + break; + } + + if (count < min_count) + blTree.freqs[curlen] += count; + else if (curlen != 0) + blTree.freqs[REP_3_6]++; + else if (count <= 10) + blTree.freqs[REP_3_10]++; + else + blTree.freqs[REP_11_138]++; + } + } + + void writeTree(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.writeSymbol(nextlen); + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + break; + } + + if (count < min_count) + { + while (count-- > 0) + blTree.writeSymbol(curlen); + } + else if (curlen != 0) + { + blTree.writeSymbol(REP_3_6); + pending.writeBits(count - 3, 2); + } + else if (count <= 10) + { + blTree.writeSymbol(REP_3_10); + pending.writeBits(count - 3, 3); + } + else + { + blTree.writeSymbol(REP_11_138); + pending.writeBits(count - 11, 7); + } + } + } + } + + + + DeflaterPending pending; + private Tree literalTree, distTree, blTree; + + private short d_buf[]; + private byte l_buf[]; + private int last_lit; + private int extra_bits; + + private static short staticLCodes[]; + private static byte staticLLength[]; + private static short staticDCodes[]; + private static byte staticDLength[]; + + /** + * Reverse the bits of a 16 bit value. + */ + static short bitReverse(int value) { + return (short) (bit4Reverse.charAt(value & 0xf) << 12 + | bit4Reverse.charAt((value >> 4) & 0xf) << 8 + | bit4Reverse.charAt((value >> 8) & 0xf) << 4 + | bit4Reverse.charAt(value >> 12)); + } + + static { + /* See RFC 1951 3.2.6 */ + /* Literal codes */ + staticLCodes = new short[LITERAL_NUM]; + staticLLength = new byte[LITERAL_NUM]; + int i = 0; + while (i < 144) { + staticLCodes[i] = bitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + while (i < 256) { + staticLCodes[i] = bitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + while (i < 280) { + staticLCodes[i] = bitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + while (i < LITERAL_NUM) { + staticLCodes[i] = bitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + /* Distant codes */ + staticDCodes = new short[DIST_NUM]; + staticDLength = new byte[DIST_NUM]; + for (i = 0; i < DIST_NUM; i++) { + staticDCodes[i] = bitReverse(i << 11); + staticDLength[i] = 5; + } + } + + public DeflaterHuffman(DeflaterPending pending) + { + this.pending = pending; + + literalTree = new Tree(LITERAL_NUM, 257, 15); + distTree = new Tree(DIST_NUM, 1, 15); + blTree = new Tree(BITLEN_NUM, 4, 7); + + d_buf = new short[BUFSIZE]; + l_buf = new byte [BUFSIZE]; + } + + public final void reset() { + last_lit = 0; + extra_bits = 0; + literalTree.reset(); + distTree.reset(); + blTree.reset(); + } + + private int l_code(int len) { + if (len == 255) + return 285; + + int code = 257; + while (len >= 8) + { + code += 4; + len >>= 1; + } + return code + len; + } + + private int d_code(int distance) { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + return code + distance; + } + + public void sendAllTrees(int blTreeCodes) { + blTree.buildCodes(); + literalTree.buildCodes(); + distTree.buildCodes(); + pending.writeBits(literalTree.numCodes - 257, 5); + pending.writeBits(distTree.numCodes - 1, 5); + pending.writeBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + pending.writeBits(blTree.length[BL_ORDER[rank]], 3); + literalTree.writeTree(blTree); + distTree.writeTree(blTree); + if (DeflaterConstants.DEBUGGING) + blTree.checkEmpty(); + } + + public void compressBlock() { + for (int i = 0; i < last_lit; i++) + { + int litlen = l_buf[i] & 0xff; + int dist = d_buf[i]; + if (dist-- != 0) + { + if (DeflaterConstants.DEBUGGING) + System.err.print("["+(dist+1)+","+(litlen+3)+"]: "); + + int lc = l_code(litlen); + literalTree.writeSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + pending.writeBits(litlen & ((1 << bits) - 1), bits); + + int dc = d_code(dist); + distTree.writeSymbol(dc); + + bits = dc / 2 - 1; + if (bits > 0) + pending.writeBits(dist & ((1 << bits) - 1), bits); + } + else + { + if (DeflaterConstants.DEBUGGING) + { + if (litlen > 32 && litlen < 127) + System.err.print("("+(char)litlen+"): "); + else + System.err.print("{"+litlen+"}: "); + } + literalTree.writeSymbol(litlen); + } + } + if (DeflaterConstants.DEBUGGING) + System.err.print("EOF: "); + literalTree.writeSymbol(EOF_SYMBOL); + if (DeflaterConstants.DEBUGGING) + { + literalTree.checkEmpty(); + distTree.checkEmpty(); + } + } + + public void flushStoredBlock(byte[] stored, + int stored_offset, int stored_len, + boolean lastBlock) { + if (DeflaterConstants.DEBUGGING) + System.err.println("Flushing stored block "+ stored_len); + pending.writeBits((DeflaterConstants.STORED_BLOCK << 1) + + (lastBlock ? 1 : 0), 3); + pending.alignToByte(); + pending.writeShort(stored_len); + pending.writeShort(~stored_len); + pending.writeBlock(stored, stored_offset, stored_len); + reset(); + } + + public void flushBlock(byte[] stored, int stored_offset, int stored_len, + boolean lastBlock) { + literalTree.freqs[EOF_SYMBOL]++; + + /* Build trees */ + literalTree.buildTree(); + distTree.buildTree(); + + /* Calculate bitlen frequency */ + literalTree.calcBLFreq(blTree); + distTree.calcBLFreq(blTree); + + /* Build bitlen tree */ + blTree.buildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (blTree.length[BL_ORDER[i]] > 0) + blTreeCodes = i+1; + } + int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength() + + literalTree.getEncodedLength() + distTree.getEncodedLength() + + extra_bits; + + int static_len = extra_bits; + for (int i = 0; i < LITERAL_NUM; i++) + static_len += literalTree.freqs[i] * staticLLength[i]; + for (int i = 0; i < DIST_NUM; i++) + static_len += distTree.freqs[i] * staticDLength[i]; + if (opt_len >= static_len) + { + /* Force static trees */ + opt_len = static_len; + } + + if (stored_offset >= 0 && stored_len+4 < opt_len >> 3) + { + /* Store Block */ + if (DeflaterConstants.DEBUGGING) + System.err.println("Storing, since " + stored_len + " < " + opt_len + + " <= " + static_len); + flushStoredBlock(stored, stored_offset, stored_len, lastBlock); + } + else if (opt_len == static_len) + { + /* Encode with static tree */ + pending.writeBits((DeflaterConstants.STATIC_TREES << 1) + + (lastBlock ? 1 : 0), 3); + literalTree.setStaticCodes(staticLCodes, staticLLength); + distTree.setStaticCodes(staticDCodes, staticDLength); + compressBlock(); + reset(); + } + else + { + /* Encode with dynamic tree */ + pending.writeBits((DeflaterConstants.DYN_TREES << 1) + + (lastBlock ? 1 : 0), 3); + sendAllTrees(blTreeCodes); + compressBlock(); + reset(); + } + } + + public final boolean isFull() + { + return last_lit == BUFSIZE; + } + + public final boolean tallyLit(int lit) + { + if (DeflaterConstants.DEBUGGING) + { + if (lit > 32 && lit < 127) + System.err.println("("+(char)lit+")"); + else + System.err.println("{"+lit+"}"); + } + d_buf[last_lit] = 0; + l_buf[last_lit++] = (byte) lit; + literalTree.freqs[lit]++; + return last_lit == BUFSIZE; + } + + public final boolean tallyDist(int dist, int len) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("["+dist+","+len+"]"); + + d_buf[last_lit] = (short) dist; + l_buf[last_lit++] = (byte) (len - 3); + + int lc = l_code(len-3); + literalTree.freqs[lc]++; + if (lc >= 265 && lc < 285) + extra_bits += (lc - 261) / 4; + + int dc = d_code(dist-1); + distTree.freqs[dc]++; + if (dc >= 4) + extra_bits += dc / 2 - 1; + return last_lit == BUFSIZE; + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterOutputStream.java b/libjava/classpath/java/util/zip/DeflaterOutputStream.java new file mode 100644 index 00000000000..b10100d8a28 --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterOutputStream.java @@ -0,0 +1,198 @@ +/* DeflaterOutputStream.java - Output filter for compressing. + Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * This is a special FilterOutputStream deflating the bytes that are + * written through it. It uses the Deflater for deflating. + * + * A special thing to be noted is that flush() doesn't flush + * everything in Sun's JDK, but it does so in jazzlib. This is because + * Sun's Deflater doesn't have a way to flush() everything, without + * finishing the stream. + * + * @author Tom Tromey, Jochen Hoenicke + * @date Jan 11, 2001 + */ +public class DeflaterOutputStream extends FilterOutputStream +{ + /** + * This buffer is used temporarily to retrieve the bytes from the + * deflater and write them to the underlying output stream. + */ + protected byte[] buf; + + /** + * The deflater which is used to deflate the stream. + */ + protected Deflater def; + + /** + * Deflates everything in the def's input buffers. This will call + * <code>def.deflate()</code> until all bytes from the input buffers + * are processed. + */ + protected void deflate() throws IOException + { + while (! def.needsInput()) + { + int len = def.deflate(buf, 0, buf.length); + + // System.err.println("DOS deflated " + len + " out of " + buf.length); + if (len <= 0) + break; + out.write(buf, 0, len); + } + + if (! def.needsInput()) + throw new InternalError("Can't deflate all input?"); + } + + /** + * Creates a new DeflaterOutputStream with a default Deflater and + * default buffer size. + * @param out the output stream where deflated output should be written. + */ + public DeflaterOutputStream(OutputStream out) + { + this(out, new Deflater(), 512); + } + + /** + * Creates a new DeflaterOutputStream with the given Deflater and + * default buffer size. + * @param out the output stream where deflated output should be written. + * @param defl the underlying deflater. + */ + public DeflaterOutputStream(OutputStream out, Deflater defl) + { + this(out, defl, 512); + } + + /** + * Creates a new DeflaterOutputStream with the given Deflater and + * buffer size. + * @param out the output stream where deflated output should be written. + * @param defl the underlying deflater. + * @param bufsize the buffer size. + * @exception IllegalArgumentException if bufsize isn't positive. + */ + public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize) + { + super(out); + if (bufsize <= 0) + throw new IllegalArgumentException("bufsize <= 0"); + buf = new byte[bufsize]; + def = defl; + } + + /** + * Flushes the stream by calling flush() on the deflater and then + * on the underlying stream. This ensures that all bytes are + * flushed. This function doesn't work in Sun's JDK, but only in + * jazzlib. + */ + public void flush() throws IOException + { + def.flush(); + deflate(); + out.flush(); + } + + /** + * Finishes the stream by calling finish() on the deflater. This + * was the only way to ensure that all bytes are flushed in Sun's + * JDK. + */ + public void finish() throws IOException + { + def.finish(); + while (! def.finished()) + { + int len = def.deflate(buf, 0, buf.length); + if (len <= 0) + break; + out.write(buf, 0, len); + } + if (! def.finished()) + throw new InternalError("Can't deflate all input?"); + out.flush(); + } + + /** + * Calls finish() and closes the stream. + */ + public void close() throws IOException + { + finish(); + out.close(); + } + + /** + * Writes a single byte to the compressed output stream. + * @param bval the byte value. + */ + public void write(int bval) throws IOException + { + byte[] b = new byte[1]; + b[0] = (byte) bval; + write(b, 0, 1); + } + + /** + * Writes a len bytes from an array to the compressed stream. + * @param buf the byte array. + * @param off the offset into the byte array where to start. + * @param len the number of bytes to write. + */ + public void write(byte[] buf, int off, int len) throws IOException + { + def.setInput(buf, off, len); + deflate(); + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterPending.java b/libjava/classpath/java/util/zip/DeflaterPending.java new file mode 100644 index 00000000000..f38212c352d --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterPending.java @@ -0,0 +1,54 @@ +/* java.util.zip.DeflaterPending + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class stores the pending output of the Deflater. + * + * @author Jochen Hoenicke + * @date Jan 5, 2000 + */ + +class DeflaterPending extends PendingBuffer +{ + public DeflaterPending() + { + super(DeflaterConstants.PENDING_BUF_SIZE); + } +} + diff --git a/libjava/classpath/java/util/zip/GZIPInputStream.java b/libjava/classpath/java/util/zip/GZIPInputStream.java new file mode 100644 index 00000000000..2cea755b3cc --- /dev/null +++ b/libjava/classpath/java/util/zip/GZIPInputStream.java @@ -0,0 +1,355 @@ +/* GZIPInputStream.java - Input filter for reading gzip file + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * This filter stream is used to decompress a "GZIP" format stream. + * The "GZIP" format is described in RFC 1952. + * + * @author John Leuner + * @author Tom Tromey + * @since JDK 1.1 + */ +public class GZIPInputStream + extends InflaterInputStream +{ + /** + * The magic number found at the start of a GZIP stream. + */ + public static final int GZIP_MAGIC = 0x8b1f; + + /** + * The mask for bit 0 of the flag byte. + */ + static final int FTEXT = 0x1; + + /** + * The mask for bit 1 of the flag byte. + */ + static final int FHCRC = 0x2; + + /** + * The mask for bit 2 of the flag byte. + */ + static final int FEXTRA = 0x4; + + /** + * The mask for bit 3 of the flag byte. + */ + static final int FNAME = 0x8; + + /** + * The mask for bit 4 of the flag byte. + */ + static final int FCOMMENT = 0x10; + + /** + * The CRC-32 checksum value for uncompressed data. + */ + protected CRC32 crc; + + /** + * Indicates whether or not the end of the stream has been reached. + */ + protected boolean eos; + + /** + * Indicates whether or not the GZIP header has been read in. + */ + private boolean readGZIPHeader; + + /** + * Creates a GZIPInputStream with the default buffer size. + * + * @param in The stream to read compressed data from + * (in GZIP format). + * + * @throws IOException if an error occurs during an I/O operation. + */ + public GZIPInputStream(InputStream in) + throws IOException + { + this(in, 4096); + } + + /** + * Creates a GZIPInputStream with the specified buffer size. + * + * @param in The stream to read compressed data from + * (in GZIP format). + * @param size The size of the buffer to use. + * + * @throws IOException if an error occurs during an I/O operation. + * @throws IllegalArgumentException if <code>size</code> + * is less than or equal to 0. + */ + public GZIPInputStream(InputStream in, int size) + throws IOException + { + super(in, new Inflater(true), size); + crc = new CRC32(); + readHeader(); + } + + /** + * Closes the input stream. + * + * @throws IOException if an error occurs during an I/O operation. + */ + public void close() + throws IOException + { + // Nothing to do here. + super.close(); + } + + /** + * Reads in GZIP-compressed data and stores it in uncompressed form + * into an array of bytes. The method will block until either + * enough input data becomes available or the compressed stream + * reaches its end. + * + * @param buf the buffer into which the uncompressed data will + * be stored. + * @param offset the offset indicating where in <code>buf</code> + * the uncompressed data should be placed. + * @param len the number of uncompressed bytes to be read. + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + // We first have to slurp in the GZIP header, then we feed all the + // rest of the data to the superclass. + // + // As we do that we continually update the CRC32. Once the data is + // finished, we check the CRC32. + // + // This means we don't need our own buffer, as everything is done + // in the superclass. + if (!readGZIPHeader) + readHeader(); + + if (eos) + return -1; + + // System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len); + + /* We don't have to read the header, + * so we just grab data from the superclass. + */ + int numRead = super.read(buf, offset, len); + if (numRead > 0) + crc.update(buf, offset, numRead); + + if (inf.finished()) + readFooter(); + return numRead; + } + + + /** + * Reads in the GZIP header. + */ + private void readHeader() throws IOException + { + /* 1. Check the two magic bytes */ + CRC32 headCRC = new CRC32(); + int magic = in.read(); + if (magic < 0) + { + eos = true; + return; + } + int magic2 = in.read(); + if ((magic + (magic2 << 8)) != GZIP_MAGIC) + throw new IOException("Error in GZIP header, bad magic code"); + headCRC.update(magic); + headCRC.update(magic2); + + /* 2. Check the compression type (must be 8) */ + int CM = in.read(); + if (CM != 8) + throw new IOException("Error in GZIP header, data not in deflate format"); + headCRC.update(CM); + + /* 3. Check the flags */ + int flags = in.read(); + if (flags < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(flags); + + /* This flag byte is divided into individual bits as follows: + + bit 0 FTEXT + bit 1 FHCRC + bit 2 FEXTRA + bit 3 FNAME + bit 4 FCOMMENT + bit 5 reserved + bit 6 reserved + bit 7 reserved + */ + + /* 3.1 Check the reserved bits are zero */ + if ((flags & 0xd0) != 0) + throw new IOException("Reserved flag bits in GZIP header != 0"); + + /* 4.-6. Skip the modification time, extra flags, and OS type */ + for (int i=0; i< 6; i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + + /* 7. Read extra field */ + if ((flags & FEXTRA) != 0) + { + /* Skip subfield id */ + for (int i=0; i< 2; i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + if (in.read() < 0 || in.read() < 0) + throw new EOFException("Early EOF in GZIP header"); + + int len1, len2, extraLen; + len1 = in.read(); + len2 = in.read(); + if ((len1 < 0) || (len2 < 0)) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(len1); + headCRC.update(len2); + + extraLen = (len1 << 8) | len2; + for (int i = 0; i < extraLen;i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + } + + /* 8. Read file name */ + if ((flags & FNAME) != 0) + { + int readByte; + while ( (readByte = in.read()) > 0) + headCRC.update(readByte); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP file name"); + headCRC.update(readByte); + } + + /* 9. Read comment */ + if ((flags & FCOMMENT) != 0) + { + int readByte; + while ( (readByte = in.read()) > 0) + headCRC.update(readByte); + + if (readByte < 0) + throw new EOFException("Early EOF in GZIP comment"); + headCRC.update(readByte); + } + + /* 10. Read header CRC */ + if ((flags & FHCRC) != 0) + { + int tempByte; + int crcval = in.read(); + if (crcval < 0) + throw new EOFException("Early EOF in GZIP header"); + + tempByte = in.read(); + if (tempByte < 0) + throw new EOFException("Early EOF in GZIP header"); + + crcval = (crcval << 8) | tempByte; + if (crcval != ((int) headCRC.getValue() & 0xffff)) + throw new IOException("Header CRC value mismatch"); + } + + readGZIPHeader = true; + //System.err.println("Read GZIP header"); + } + + private void readFooter() throws IOException + { + byte[] footer = new byte[8]; + int avail = inf.getRemaining(); + if (avail > 8) + avail = 8; + System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail); + int needed = 8 - avail; + while (needed > 0) + { + int count = in.read(footer, 8-needed, needed); + if (count <= 0) + throw new EOFException("Early EOF in GZIP footer"); + needed -= count; //Jewel Jan 16 + } + + int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) + | ((footer[2] & 0xff) << 16) | (footer[3] << 24); + if (crcval != (int) crc.getValue()) + throw new IOException("GZIP crc sum mismatch, theirs \"" + + Integer.toHexString(crcval) + + "\" and ours \"" + + Integer.toHexString( (int) crc.getValue())); + + int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) + | ((footer[6] & 0xff) << 16) | (footer[7] << 24); + if (total != inf.getTotalOut()) + throw new IOException("Number of bytes mismatch"); + + /* FIXME" XXX Should we support multiple members. + * Difficult, since there may be some bytes still in buf + */ + eos = true; + } +} diff --git a/libjava/classpath/java/util/zip/GZIPOutputStream.java b/libjava/classpath/java/util/zip/GZIPOutputStream.java new file mode 100644 index 00000000000..5b7b827a9ff --- /dev/null +++ b/libjava/classpath/java/util/zip/GZIPOutputStream.java @@ -0,0 +1,151 @@ +/* GZIPOutputStream.java - Create a file in gzip format + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * This filter stream is used to compress a stream into a "GZIP" stream. + * The "GZIP" format is described in RFC 1952. + * + * @author John Leuner + * @author Tom Tromey + * @since JDK 1.1 + */ + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +public class GZIPOutputStream extends DeflaterOutputStream +{ + /** + * CRC-32 value for uncompressed data + */ + protected CRC32 crc; + + /** + * Creates a GZIPOutputStream with the default buffer size + * + * @param out The stream to read data (to be compressed) from + * + */ + public GZIPOutputStream(OutputStream out) throws IOException + { + this(out, 4096); + } + + /** + * Creates a GZIPOutputStream with the specified buffer size + * + * @param out The stream to read compressed data from + * @param size Size of the buffer to use + */ + public GZIPOutputStream(OutputStream out, int size) throws IOException + { + super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); + crc = new CRC32(); + int mod_time = (int) (System.currentTimeMillis() / 1000L); + byte[] gzipHeader = + { + /* The two magic bytes */ + (byte) GZIPInputStream.GZIP_MAGIC, + (byte) (GZIPInputStream.GZIP_MAGIC >> 8), + + /* The compression type */ + (byte) Deflater.DEFLATED, + + /* The flags (not set) */ + 0, + + /* The modification time */ + (byte) mod_time, (byte) (mod_time >> 8), + (byte) (mod_time >> 16), (byte) (mod_time >> 24), + + /* The extra flags */ + 0, + + /* The OS type (unknown) */ + (byte) 255 + }; + + out.write(gzipHeader); + // System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )"); + } + + public synchronized void write(byte[] buf, int off, int len) + throws IOException + { + super.write(buf, off, len); + crc.update(buf, off, len); + } + + /** + * Writes remaining compressed output data to the output stream + * and closes it. + */ + public void close() throws IOException + { + finish(); + out.close(); + } + + public void finish() throws IOException + { + super.finish(); + + int totalin = def.getTotalIn(); + int crcval = (int) (crc.getValue() & 0xffffffff); + + // System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin)); + + byte[] gzipFooter = + { + (byte) crcval, (byte) (crcval >> 8), + (byte) (crcval >> 16), (byte) (crcval >> 24), + + (byte) totalin, (byte) (totalin >> 8), + (byte) (totalin >> 16), (byte) (totalin >> 24) + }; + + out.write(gzipFooter); + // System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )"); + } +} diff --git a/libjava/classpath/java/util/zip/Inflater.java b/libjava/classpath/java/util/zip/Inflater.java new file mode 100644 index 00000000000..76de891faa5 --- /dev/null +++ b/libjava/classpath/java/util/zip/Inflater.java @@ -0,0 +1,715 @@ +/* Inflater.java - Decompress a data stream + Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * Inflater is used to decompress data that has been compressed according + * to the "deflate" standard described in rfc1950. + * + * The usage is as following. First you have to set some input with + * <code>setInput()</code>, then inflate() it. If inflate doesn't + * inflate any bytes there may be three reasons: + * <ul> + * <li>needsInput() returns true because the input buffer is empty. + * You have to provide more input with <code>setInput()</code>. + * NOTE: needsInput() also returns true when, the stream is finished. + * </li> + * <li>needsDictionary() returns true, you have to provide a preset + * dictionary with <code>setDictionary()</code>.</li> + * <li>finished() returns true, the inflater has finished.</li> + * </ul> + * Once the first output byte is produced, a dictionary will not be + * needed at a later stage. + * + * @author John Leuner, Jochen Hoenicke + * @author Tom Tromey + * @date May 17, 1999 + * @since JDK 1.1 + */ +public class Inflater +{ + /* Copy lengths for literal codes 257..285 */ + private static final int CPLENS[] = + { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + + /* Extra bits for literal codes 257..285 */ + private static final int CPLEXT[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + /* Copy offsets for distance codes 0..29 */ + private static final int CPDIST[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + /* Extra bits for distance codes */ + private static final int CPDEXT[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 + }; + + /* This are the state in which the inflater can be. */ + private static final int DECODE_HEADER = 0; + private static final int DECODE_DICT = 1; + private static final int DECODE_BLOCKS = 2; + private static final int DECODE_STORED_LEN1 = 3; + private static final int DECODE_STORED_LEN2 = 4; + private static final int DECODE_STORED = 5; + private static final int DECODE_DYN_HEADER = 6; + private static final int DECODE_HUFFMAN = 7; + private static final int DECODE_HUFFMAN_LENBITS = 8; + private static final int DECODE_HUFFMAN_DIST = 9; + private static final int DECODE_HUFFMAN_DISTBITS = 10; + private static final int DECODE_CHKSUM = 11; + private static final int FINISHED = 12; + + /** This variable contains the current state. */ + private int mode; + + /** + * The adler checksum of the dictionary or of the decompressed + * stream, as it is written in the header resp. footer of the + * compressed stream. <br> + * + * Only valid if mode is DECODE_DICT or DECODE_CHKSUM. + */ + private int readAdler; + /** + * The number of bits needed to complete the current state. This + * is valid, if mode is DECODE_DICT, DECODE_CHKSUM, + * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. + */ + private int neededBits; + private int repLength, repDist; + private int uncomprLen; + /** + * True, if the last block flag was set in the last block of the + * inflated stream. This means that the stream ends after the + * current block. + */ + private boolean isLastBlock; + + /** + * The total number of inflated bytes. + */ + private int totalOut; + /** + * The total number of bytes set with setInput(). This is not the + * value returned by getTotalIn(), since this also includes the + * unprocessed input. + */ + private int totalIn; + /** + * This variable stores the nowrap flag that was given to the constructor. + * True means, that the inflated stream doesn't contain a header nor the + * checksum in the footer. + */ + private boolean nowrap; + + private StreamManipulator input; + private OutputWindow outputWindow; + private InflaterDynHeader dynHeader; + private InflaterHuffmanTree litlenTree, distTree; + private Adler32 adler; + + /** + * Creates a new inflater. + */ + public Inflater () + { + this (false); + } + + /** + * Creates a new inflater. + * @param nowrap true if no header and checksum field appears in the + * stream. This is used for GZIPed input. For compatibility with + * Sun JDK you should provide one byte of input more than needed in + * this case. + */ + public Inflater (boolean nowrap) + { + this.nowrap = nowrap; + this.adler = new Adler32(); + input = new StreamManipulator(); + outputWindow = new OutputWindow(); + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + } + + /** + * Finalizes this object. + */ + protected void finalize () + { + /* Exists only for compatibility */ + } + + /** + * Frees all objects allocated by the inflater. There's no reason + * to call this, since you can just rely on garbage collection (even + * for the Sun implementation). Exists only for compatibility + * with Sun's JDK, where the compressor allocates native memory. + * If you call any method (even reset) afterwards the behaviour is + * <i>undefined</i>. + * @deprecated Just clear all references to inflater instead. + */ + public void end () + { + outputWindow = null; + input = null; + dynHeader = null; + litlenTree = null; + distTree = null; + adler = null; + } + + /** + * Returns true, if the inflater has finished. This means, that no + * input is needed and no output can be produced. + */ + public boolean finished() + { + return mode == FINISHED && outputWindow.getAvailable() == 0; + } + + /** + * Gets the adler checksum. This is either the checksum of all + * uncompressed bytes returned by inflate(), or if needsDictionary() + * returns true (and thus no output was yet produced) this is the + * adler checksum of the expected dictionary. + * @returns the adler checksum. + */ + public int getAdler() + { + return needsDictionary() ? readAdler : (int) adler.getValue(); + } + + /** + * Gets the number of unprocessed input. Useful, if the end of the + * stream is reached and you want to further process the bytes after + * the deflate stream. + * @return the number of bytes of the input which were not processed. + */ + public int getRemaining() + { + return input.getAvailableBytes(); + } + + /** + * Gets the total number of processed compressed input bytes. + * @return the total number of bytes of processed input bytes. + */ + public int getTotalIn() + { + return totalIn - getRemaining(); + } + + /** + * Gets the total number of output bytes returned by inflate(). + * @return the total number of output bytes. + */ + public int getTotalOut() + { + return totalOut; + } + + /** + * Inflates the compressed stream to the output buffer. If this + * returns 0, you should check, whether needsDictionary(), + * needsInput() or finished() returns true, to determine why no + * further output is produced. + * @param buf the output buffer. + * @return the number of bytes written to the buffer, 0 if no further + * output can be produced. + * @exception DataFormatException if deflated stream is invalid. + * @exception IllegalArgumentException if buf has length 0. + */ + public int inflate (byte[] buf) throws DataFormatException + { + return inflate (buf, 0, buf.length); + } + + /** + * Inflates the compressed stream to the output buffer. If this + * returns 0, you should check, whether needsDictionary(), + * needsInput() or finished() returns true, to determine why no + * further output is produced. + * @param buf the output buffer. + * @param off the offset into buffer where the output should start. + * @param len the maximum length of the output. + * @return the number of bytes written to the buffer, 0 if no further + * output can be produced. + * @exception DataFormatException if deflated stream is invalid. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public int inflate (byte[] buf, int off, int len) throws DataFormatException + { + /* Special case: len may be zero */ + if (len == 0) + return 0; + /* Check for correct buff, off, len triple */ + if (0 > off || off > off + len || off + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + int count = 0; + int more; + do + { + if (mode != DECODE_CHKSUM) + { + /* Don't give away any output, if we are waiting for the + * checksum in the input stream. + * + * With this trick we have always: + * needsInput() and not finished() + * implies more output can be produced. + */ + more = outputWindow.copyOutput(buf, off, len); + adler.update(buf, off, more); + off += more; + count += more; + totalOut += more; + len -= more; + if (len == 0) + return count; + } + } + while (decode() || (outputWindow.getAvailable() > 0 + && mode != DECODE_CHKSUM)); + return count; + } + + /** + * Returns true, if a preset dictionary is needed to inflate the input. + */ + public boolean needsDictionary () + { + return mode == DECODE_DICT && neededBits == 0; + } + + /** + * Returns true, if the input buffer is empty. + * You should then call setInput(). <br> + * + * <em>NOTE</em>: This method also returns true when the stream is finished. + */ + public boolean needsInput () + { + return input.needsInput (); + } + + /** + * Resets the inflater so that a new stream can be decompressed. All + * pending input and output will be discarded. + */ + public void reset () + { + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + totalIn = totalOut = 0; + input.reset(); + outputWindow.reset(); + dynHeader = null; + litlenTree = null; + distTree = null; + isLastBlock = false; + adler.reset(); + } + + /** + * Sets the preset dictionary. This should only be called, if + * needsDictionary() returns true and it should set the same + * dictionary, that was used for deflating. The getAdler() + * function returns the checksum of the dictionary needed. + * @param buffer the dictionary. + * @exception IllegalStateException if no dictionary is needed. + * @exception IllegalArgumentException if the dictionary checksum is + * wrong. + */ + public void setDictionary (byte[] buffer) + { + setDictionary(buffer, 0, buffer.length); + } + + /** + * Sets the preset dictionary. This should only be called, if + * needsDictionary() returns true and it should set the same + * dictionary, that was used for deflating. The getAdler() + * function returns the checksum of the dictionary needed. + * @param buffer the dictionary. + * @param off the offset into buffer where the dictionary starts. + * @param len the length of the dictionary. + * @exception IllegalStateException if no dictionary is needed. + * @exception IllegalArgumentException if the dictionary checksum is + * wrong. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public void setDictionary (byte[] buffer, int off, int len) + { + if (!needsDictionary()) + throw new IllegalStateException(); + + adler.update(buffer, off, len); + if ((int) adler.getValue() != readAdler) + throw new IllegalArgumentException("Wrong adler checksum"); + adler.reset(); + outputWindow.copyDict(buffer, off, len); + mode = DECODE_BLOCKS; + } + + /** + * Sets the input. This should only be called, if needsInput() + * returns true. + * @param buf the input. + * @exception IllegalStateException if no input is needed. + */ + public void setInput (byte[] buf) + { + setInput (buf, 0, buf.length); + } + + /** + * Sets the input. This should only be called, if needsInput() + * returns true. + * @param buf the input. + * @param off the offset into buffer where the input starts. + * @param len the length of the input. + * @exception IllegalStateException if no input is needed. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public void setInput (byte[] buf, int off, int len) + { + input.setInput (buf, off, len); + totalIn += len; + } + + /** + * Decodes the deflate header. + * @return false if more input is needed. + * @exception DataFormatException if header is invalid. + */ + private boolean decodeHeader () throws DataFormatException + { + int header = input.peekBits(16); + if (header < 0) + return false; + input.dropBits(16); + + /* The header is written in "wrong" byte order */ + header = ((header << 8) | (header >> 8)) & 0xffff; + if (header % 31 != 0) + throw new DataFormatException("Header checksum illegal"); + + if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) + throw new DataFormatException("Compression Method unknown"); + + /* Maximum size of the backwards window in bits. + * We currently ignore this, but we could use it to make the + * inflater window more space efficient. On the other hand the + * full window (15 bits) is needed most times, anyway. + int max_wbits = ((header & 0x7000) >> 12) + 8; + */ + + if ((header & 0x0020) == 0) // Dictionary flag? + { + mode = DECODE_BLOCKS; + } + else + { + mode = DECODE_DICT; + neededBits = 32; + } + return true; + } + + /** + * Decodes the dictionary checksum after the deflate header. + * @return false if more input is needed. + */ + private boolean decodeDict () + { + while (neededBits > 0) + { + int dictByte = input.peekBits(8); + if (dictByte < 0) + return false; + input.dropBits(8); + readAdler = (readAdler << 8) | dictByte; + neededBits -= 8; + } + return false; + } + + /** + * Decodes the huffman encoded symbols in the input stream. + * @return false if more input is needed, true if output window is + * full or the current block ends. + * @exception DataFormatException if deflated stream is invalid. + */ + private boolean decodeHuffman () throws DataFormatException + { + int free = outputWindow.getFreeSpace(); + while (free >= 258) + { + int symbol; + switch (mode) + { + case DECODE_HUFFMAN: + /* This is the inner loop so it is optimized a bit */ + while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0) + { + outputWindow.write(symbol); + if (--free < 258) + return true; + } + if (symbol < 257) + { + if (symbol < 0) + return false; + else + { + /* symbol == 256: end of block */ + distTree = null; + litlenTree = null; + mode = DECODE_BLOCKS; + return true; + } + } + + try + { + repLength = CPLENS[symbol - 257]; + neededBits = CPLEXT[symbol - 257]; + } + catch (ArrayIndexOutOfBoundsException ex) + { + throw new DataFormatException("Illegal rep length code"); + } + /* fall through */ + case DECODE_HUFFMAN_LENBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_LENBITS; + int i = input.peekBits(neededBits); + if (i < 0) + return false; + input.dropBits(neededBits); + repLength += i; + } + mode = DECODE_HUFFMAN_DIST; + /* fall through */ + case DECODE_HUFFMAN_DIST: + symbol = distTree.getSymbol(input); + if (symbol < 0) + return false; + try + { + repDist = CPDIST[symbol]; + neededBits = CPDEXT[symbol]; + } + catch (ArrayIndexOutOfBoundsException ex) + { + throw new DataFormatException("Illegal rep dist code"); + } + /* fall through */ + case DECODE_HUFFMAN_DISTBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_DISTBITS; + int i = input.peekBits(neededBits); + if (i < 0) + return false; + input.dropBits(neededBits); + repDist += i; + } + outputWindow.repeat(repLength, repDist); + free -= repLength; + mode = DECODE_HUFFMAN; + break; + default: + throw new IllegalStateException(); + } + } + return true; + } + + /** + * Decodes the adler checksum after the deflate stream. + * @return false if more input is needed. + * @exception DataFormatException if checksum doesn't match. + */ + private boolean decodeChksum () throws DataFormatException + { + while (neededBits > 0) + { + int chkByte = input.peekBits(8); + if (chkByte < 0) + return false; + input.dropBits(8); + readAdler = (readAdler << 8) | chkByte; + neededBits -= 8; + } + if ((int) adler.getValue() != readAdler) + throw new DataFormatException("Adler chksum doesn't match: " + +Integer.toHexString((int)adler.getValue()) + +" vs. "+Integer.toHexString(readAdler)); + mode = FINISHED; + return false; + } + + /** + * Decodes the deflated stream. + * @return false if more input is needed, or if finished. + * @exception DataFormatException if deflated stream is invalid. + */ + private boolean decode () throws DataFormatException + { + switch (mode) + { + case DECODE_HEADER: + return decodeHeader(); + case DECODE_DICT: + return decodeDict(); + case DECODE_CHKSUM: + return decodeChksum(); + + case DECODE_BLOCKS: + if (isLastBlock) + { + if (nowrap) + { + mode = FINISHED; + return false; + } + else + { + input.skipToByteBoundary(); + neededBits = 32; + mode = DECODE_CHKSUM; + return true; + } + } + + int type = input.peekBits(3); + if (type < 0) + return false; + input.dropBits(3); + + if ((type & 1) != 0) + isLastBlock = true; + switch (type >> 1) + { + case DeflaterConstants.STORED_BLOCK: + input.skipToByteBoundary(); + mode = DECODE_STORED_LEN1; + break; + case DeflaterConstants.STATIC_TREES: + litlenTree = InflaterHuffmanTree.defLitLenTree; + distTree = InflaterHuffmanTree.defDistTree; + mode = DECODE_HUFFMAN; + break; + case DeflaterConstants.DYN_TREES: + dynHeader = new InflaterDynHeader(); + mode = DECODE_DYN_HEADER; + break; + default: + throw new DataFormatException("Unknown block type "+type); + } + return true; + + case DECODE_STORED_LEN1: + { + if ((uncomprLen = input.peekBits(16)) < 0) + return false; + input.dropBits(16); + mode = DECODE_STORED_LEN2; + } + /* fall through */ + case DECODE_STORED_LEN2: + { + int nlen = input.peekBits(16); + if (nlen < 0) + return false; + input.dropBits(16); + if (nlen != (uncomprLen ^ 0xffff)) + throw new DataFormatException("broken uncompressed block"); + mode = DECODE_STORED; + } + /* fall through */ + case DECODE_STORED: + { + int more = outputWindow.copyStored(input, uncomprLen); + uncomprLen -= more; + if (uncomprLen == 0) + { + mode = DECODE_BLOCKS; + return true; + } + return !input.needsInput(); + } + + case DECODE_DYN_HEADER: + if (!dynHeader.decode(input)) + return false; + litlenTree = dynHeader.buildLitLenTree(); + distTree = dynHeader.buildDistTree(); + mode = DECODE_HUFFMAN; + /* fall through */ + case DECODE_HUFFMAN: + case DECODE_HUFFMAN_LENBITS: + case DECODE_HUFFMAN_DIST: + case DECODE_HUFFMAN_DISTBITS: + return decodeHuffman(); + case FINISHED: + return false; + default: + throw new IllegalStateException(); + } + } +} diff --git a/libjava/classpath/java/util/zip/InflaterDynHeader.java b/libjava/classpath/java/util/zip/InflaterDynHeader.java new file mode 100644 index 00000000000..bff84a894d8 --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterDynHeader.java @@ -0,0 +1,203 @@ +/* java.util.zip.InflaterDynHeader + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class InflaterDynHeader +{ + private static final int LNUM = 0; + private static final int DNUM = 1; + private static final int BLNUM = 2; + private static final int BLLENS = 3; + private static final int LENS = 4; + private static final int REPS = 5; + + private static final int repMin[] = { 3, 3, 11 }; + private static final int repBits[] = { 2, 3, 7 }; + + + private byte[] blLens; + private byte[] litdistLens; + + private InflaterHuffmanTree blTree; + + private int mode; + private int lnum, dnum, blnum, num; + private int repSymbol; + private byte lastLen; + private int ptr; + + private static final int[] BL_ORDER = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + public InflaterDynHeader() + { + } + + public boolean decode(StreamManipulator input) throws DataFormatException + { + decode_loop: + for (;;) + { + switch (mode) + { + case LNUM: + lnum = input.peekBits(5); + if (lnum < 0) + return false; + lnum += 257; + input.dropBits(5); +// System.err.println("LNUM: "+lnum); + mode = DNUM; + /* fall through */ + case DNUM: + dnum = input.peekBits(5); + if (dnum < 0) + return false; + dnum++; + input.dropBits(5); +// System.err.println("DNUM: "+dnum); + num = lnum+dnum; + litdistLens = new byte[num]; + mode = BLNUM; + /* fall through */ + case BLNUM: + blnum = input.peekBits(4); + if (blnum < 0) + return false; + blnum += 4; + input.dropBits(4); + blLens = new byte[19]; + ptr = 0; +// System.err.println("BLNUM: "+blnum); + mode = BLLENS; + /* fall through */ + case BLLENS: + while (ptr < blnum) + { + int len = input.peekBits(3); + if (len < 0) + return false; + input.dropBits(3); +// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); + blLens[BL_ORDER[ptr]] = (byte) len; + ptr++; + } + blTree = new InflaterHuffmanTree(blLens); + blLens = null; + ptr = 0; + mode = LENS; + /* fall through */ + case LENS: + { + int symbol; + while (((symbol = blTree.getSymbol(input)) & ~15) == 0) + { + /* Normal case: symbol in [0..15] */ + +// System.err.println("litdistLens["+ptr+"]: "+symbol); + litdistLens[ptr++] = lastLen = (byte) symbol; + + if (ptr == num) + { + /* Finished */ + return true; + } + } + + /* need more input ? */ + if (symbol < 0) + return false; + + /* otherwise repeat code */ + if (symbol >= 17) + { + /* repeat zero */ +// System.err.println("repeating zero"); + lastLen = 0; + } + else + { + if (ptr == 0) + throw new DataFormatException(); + } + repSymbol = symbol-16; + mode = REPS; + } + /* fall through */ + + case REPS: + { + int bits = repBits[repSymbol]; + int count = input.peekBits(bits); + if (count < 0) + return false; + input.dropBits(bits); + count += repMin[repSymbol]; +// System.err.println("litdistLens repeated: "+count); + + if (ptr + count > num) + throw new DataFormatException(); + while (count-- > 0) + litdistLens[ptr++] = lastLen; + + if (ptr == num) + { + /* Finished */ + return true; + } + } + mode = LENS; + continue decode_loop; + } + } + } + + public InflaterHuffmanTree buildLitLenTree() throws DataFormatException + { + byte[] litlenLens = new byte[lnum]; + System.arraycopy(litdistLens, 0, litlenLens, 0, lnum); + return new InflaterHuffmanTree(litlenLens); + } + + public InflaterHuffmanTree buildDistTree() throws DataFormatException + { + byte[] distLens = new byte[dnum]; + System.arraycopy(litdistLens, lnum, distLens, 0, dnum); + return new InflaterHuffmanTree(distLens); + } +} diff --git a/libjava/classpath/java/util/zip/InflaterHuffmanTree.java b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java new file mode 100644 index 00000000000..6c9b2175d33 --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java @@ -0,0 +1,217 @@ +/* InflaterHuffmanTree.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class InflaterHuffmanTree +{ + private static final int MAX_BITLEN = 15; + + private short[] tree; + + static InflaterHuffmanTree defLitLenTree, defDistTree; + + static + { + try + { + byte[] codeLengths = new byte[288]; + int i = 0; + while (i < 144) + codeLengths[i++] = 8; + while (i < 256) + codeLengths[i++] = 9; + while (i < 280) + codeLengths[i++] = 7; + while (i < 288) + codeLengths[i++] = 8; + defLitLenTree = new InflaterHuffmanTree(codeLengths); + + codeLengths = new byte[32]; + i = 0; + while (i < 32) + codeLengths[i++] = 5; + defDistTree = new InflaterHuffmanTree(codeLengths); + } + catch (DataFormatException ex) + { + throw new InternalError + ("InflaterHuffmanTree: static tree length illegal"); + } + } + + /** + * Constructs a Huffman tree from the array of code lengths. + * + * @param codeLengths the array of code lengths + */ + InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException + { + buildTree(codeLengths); + } + + private void buildTree(byte[] codeLengths) throws DataFormatException + { + int[] blCount = new int[MAX_BITLEN+1]; + int[] nextCode = new int[MAX_BITLEN+1]; + for (int i = 0; i < codeLengths.length; i++) + { + int bits = codeLengths[i]; + if (bits > 0) + blCount[bits]++; + } + + int code = 0; + int treeSize = 512; + for (int bits = 1; bits <= MAX_BITLEN; bits++) + { + nextCode[bits] = code; + code += blCount[bits] << (16 - bits); + if (bits >= 10) + { + /* We need an extra table for bit lengths >= 10. */ + int start = nextCode[bits] & 0x1ff80; + int end = code & 0x1ff80; + treeSize += (end - start) >> (16 - bits); + } + } + if (code != 65536) + throw new DataFormatException("Code lengths don't add up properly."); + + /* Now create and fill the extra tables from longest to shortest + * bit len. This way the sub trees will be aligned. + */ + tree = new short[treeSize]; + int treePtr = 512; + for (int bits = MAX_BITLEN; bits >= 10; bits--) + { + int end = code & 0x1ff80; + code -= blCount[bits] << (16 - bits); + int start = code & 0x1ff80; + for (int i = start; i < end; i += 1 << 7) + { + tree[DeflaterHuffman.bitReverse(i)] + = (short) ((-treePtr << 4) | bits); + treePtr += 1 << (bits-9); + } + } + + for (int i = 0; i < codeLengths.length; i++) + { + int bits = codeLengths[i]; + if (bits == 0) + continue; + code = nextCode[bits]; + int revcode = DeflaterHuffman.bitReverse(code); + if (bits <= 9) + { + do + { + tree[revcode] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } + while (revcode < 512); + } + else + { + int subTree = tree[revcode & 511]; + int treeLen = 1 << (subTree & 15); + subTree = -(subTree >> 4); + do + { + tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } + while (revcode < treeLen); + } + nextCode[bits] = code + (1 << (16 - bits)); + } + } + + /** + * Reads the next symbol from input. The symbol is encoded using the + * huffman tree. + * @param input the input source. + * @return the next symbol, or -1 if not enough input is available. + */ + int getSymbol(StreamManipulator input) throws DataFormatException + { + int lookahead, symbol; + if ((lookahead = input.peekBits(9)) >= 0) + { + if ((symbol = tree[lookahead]) >= 0) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + int subtree = -(symbol >> 4); + int bitlen = symbol & 15; + if ((lookahead = input.peekBits(bitlen)) >= 0) + { + symbol = tree[subtree | (lookahead >> 9)]; + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + { + int bits = input.getAvailableBits(); + lookahead = input.peekBits(bits); + symbol = tree[subtree | (lookahead >> 9)]; + if ((symbol & 15) <= bits) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + return -1; + } + } + else + { + int bits = input.getAvailableBits(); + lookahead = input.peekBits(bits); + symbol = tree[lookahead]; + if (symbol >= 0 && (symbol & 15) <= bits) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + return -1; + } + } +} diff --git a/libjava/classpath/java/util/zip/InflaterInputStream.java b/libjava/classpath/java/util/zip/InflaterInputStream.java new file mode 100644 index 00000000000..3c374570602 --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterInputStream.java @@ -0,0 +1,261 @@ +/* InflaterInputStream.java - Input stream filter for decompressing + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * This filter stream is used to decompress data compressed in the "deflate" + * format. The "deflate" format is described in RFC 1951. + * + * This stream may form the basis for other decompression filters, such + * as the <code>GZIPInputStream</code>. + * + * @author John Leuner + * @author Tom Tromey + * @since 1.1 + */ +public class InflaterInputStream extends FilterInputStream +{ + /** + * Decompressor for this filter + */ + protected Inflater inf; + + /** + * Byte array used as a buffer + */ + protected byte[] buf; + + /** + * Size of buffer + */ + protected int len; + + // We just use this if we are decoding one byte at a time with the + // read() call. + private byte[] onebytebuffer = new byte[1]; + + /** + * Create an InflaterInputStream with the default decompresseor + * and a default buffer size. + * + * @param in the InputStream to read bytes from + */ + public InflaterInputStream(InputStream in) + { + this(in, new Inflater(), 4096); + } + + /** + * Create an InflaterInputStream with the specified decompresseor + * and a default buffer size. + * + * @param in the InputStream to read bytes from + * @param inf the decompressor used to decompress data read from in + */ + public InflaterInputStream(InputStream in, Inflater inf) + { + this(in, inf, 4096); + } + + /** + * Create an InflaterInputStream with the specified decompresseor + * and a specified buffer size. + * + * @param in the InputStream to read bytes from + * @param inf the decompressor used to decompress data read from in + * @param size size of the buffer to use + */ + public InflaterInputStream(InputStream in, Inflater inf, int size) + { + super(in); + + if (in == null) + throw new NullPointerException("in may not be null"); + if (inf == null) + throw new NullPointerException("inf may not be null"); + if (size < 0) + throw new IllegalArgumentException("size may not be negative"); + + this.inf = inf; + this.buf = new byte [size]; + } + + /** + * Returns 0 once the end of the stream (EOF) has been reached. + * Otherwise returns 1. + */ + public int available() throws IOException + { + // According to the JDK 1.2 docs, this should only ever return 0 + // or 1 and should not be relied upon by Java programs. + if (inf == null) + throw new IOException("stream closed"); + return inf.finished() ? 0 : 1; + } + + /** + * Closes the input stream + */ + public synchronized void close() throws IOException + { + if (in != null) + in.close(); + in = null; + } + + /** + * Fills the buffer with more data to decompress. + */ + protected void fill() throws IOException + { + if (in == null) + throw new ZipException ("InflaterInputStream is closed"); + + len = in.read(buf, 0, buf.length); + + if (len < 0) + throw new ZipException("Deflated stream ends early."); + + inf.setInput(buf, 0, len); + } + + /** + * Reads one byte of decompressed data. + * + * The byte is in the lower 8 bits of the int. + */ + public int read() throws IOException + { + int nread = read(onebytebuffer, 0, 1); + if (nread > 0) + return onebytebuffer[0] & 0xff; + return -1; + } + + /** + * Decompresses data into the byte array + * + * @param b the array to read and decompress data into + * @param off the offset indicating where the data should be placed + * @param len the number of bytes to decompress + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (inf == null) + throw new IOException("stream closed"); + if (len == 0) + return 0; + + int count = 0; + for (;;) + { + + try + { + count = inf.inflate(b, off, len); + } + catch (DataFormatException dfe) + { + throw new ZipException(dfe.getMessage()); + } + + if (count > 0) + return count; + + if (inf.needsDictionary() + | inf.finished()) + return -1; + else if (inf.needsInput()) + fill(); + else + throw new InternalError("Don't know what to do"); + } + } + + /** + * Skip specified number of bytes of uncompressed data + * + * @param n number of bytes to skip + */ + public long skip(long n) throws IOException + { + if (inf == null) + throw new IOException("stream closed"); + if (n < 0) + throw new IllegalArgumentException(); + + if (n == 0) + return 0; + + int buflen = (int) Math.min(n, 2048); + byte[] tmpbuf = new byte[buflen]; + + long skipped = 0L; + while (n > 0L) + { + int numread = read(tmpbuf, 0, buflen); + if (numread <= 0) + break; + n -= numread; + skipped += numread; + buflen = (int) Math.min(n, 2048); + } + + return skipped; + } + + public boolean markSupported() + { + return false; + } + + public void mark(int readLimit) + { + } + + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } +} diff --git a/libjava/classpath/java/util/zip/OutputWindow.java b/libjava/classpath/java/util/zip/OutputWindow.java new file mode 100644 index 00000000000..1f082a9faa4 --- /dev/null +++ b/libjava/classpath/java/util/zip/OutputWindow.java @@ -0,0 +1,178 @@ +/* OutputWindow.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * Contains the output from the Inflation process. + * + * We need to have a window so that we can refer backwards into the output stream + * to repeat stuff. + * + * @author John Leuner + * @since 1.1 + */ +class OutputWindow +{ + private static final int WINDOW_SIZE = 1 << 15; + private static final int WINDOW_MASK = WINDOW_SIZE - 1; + + private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes + private int window_end = 0; + private int window_filled = 0; + + public void write(int abyte) + { + if (window_filled++ == WINDOW_SIZE) + throw new IllegalStateException("Window full"); + window[window_end++] = (byte) abyte; + window_end &= WINDOW_MASK; + } + + private void slowRepeat(int rep_start, int len, int dist) + { + while (len-- > 0) + { + window[window_end++] = window[rep_start++]; + window_end &= WINDOW_MASK; + rep_start &= WINDOW_MASK; + } + } + + public void repeat(int len, int dist) + { + if ((window_filled += len) > WINDOW_SIZE) + throw new IllegalStateException("Window full"); + + int rep_start = (window_end - dist) & WINDOW_MASK; + int border = WINDOW_SIZE - len; + if (rep_start <= border && window_end < border) + { + if (len <= dist) + { + System.arraycopy(window, rep_start, window, window_end, len); + window_end += len; + } + else + { + /* We have to copy manually, since the repeat pattern overlaps. + */ + while (len-- > 0) + window[window_end++] = window[rep_start++]; + } + } + else + slowRepeat(rep_start, len, dist); + } + + public int copyStored(StreamManipulator input, int len) + { + len = Math.min(Math.min(len, WINDOW_SIZE - window_filled), + input.getAvailableBytes()); + int copied; + + int tailLen = WINDOW_SIZE - window_end; + if (len > tailLen) + { + copied = input.copyBytes(window, window_end, tailLen); + if (copied == tailLen) + copied += input.copyBytes(window, 0, len - tailLen); + } + else + copied = input.copyBytes(window, window_end, len); + + window_end = (window_end + copied) & WINDOW_MASK; + window_filled += copied; + return copied; + } + + public void copyDict(byte[] dict, int offset, int len) + { + if (window_filled > 0) + throw new IllegalStateException(); + + if (len > WINDOW_SIZE) + { + offset += len - WINDOW_SIZE; + len = WINDOW_SIZE; + } + System.arraycopy(dict, offset, window, 0, len); + window_end = len & WINDOW_MASK; + } + + public int getFreeSpace() + { + return WINDOW_SIZE - window_filled; + } + + public int getAvailable() + { + return window_filled; + } + + public int copyOutput(byte[] output, int offset, int len) + { + int copy_end = window_end; + if (len > window_filled) + len = window_filled; + else + copy_end = (window_end - window_filled + len) & WINDOW_MASK; + + int copied = len; + int tailLen = len - copy_end; + + if (tailLen > 0) + { + System.arraycopy(window, WINDOW_SIZE - tailLen, + output, offset, tailLen); + offset += tailLen; + len = copy_end; + } + System.arraycopy(window, copy_end - len, output, offset, len); + window_filled -= copied; + if (window_filled < 0) + throw new IllegalStateException(); + return copied; + } + + public void reset() { + window_filled = window_end = 0; + } +} + + + diff --git a/libjava/classpath/java/util/zip/PendingBuffer.java b/libjava/classpath/java/util/zip/PendingBuffer.java new file mode 100644 index 00000000000..dd7ed1008fb --- /dev/null +++ b/libjava/classpath/java/util/zip/PendingBuffer.java @@ -0,0 +1,200 @@ +/* java.util.zip.PendingBuffer + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class is general purpose class for writing data to a buffer. + * + * It allows you to write bits as well as bytes + * + * Based on DeflaterPending.java + * + * @author Jochen Hoenicke + * @date Jan 5, 2000 + */ + +class PendingBuffer +{ + protected byte[] buf; + int start; + int end; + + int bits; + int bitCount; + + public PendingBuffer() + { + this( 4096 ); + } + + public PendingBuffer(int bufsize) + { + buf = new byte[bufsize]; + } + + public final void reset() { + start = end = bitCount = 0; + } + + public final void writeByte(int b) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) b; + } + + public final void writeShort(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + } + + public final void writeInt(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) (s >> 16); + buf[end++] = (byte) (s >> 24); + } + + public final void writeBlock(byte[] block, int offset, int len) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + System.arraycopy(block, offset, buf, end, len); + end += len; + } + + public final int getBitCount() { + return bitCount; + } + + public final void alignToByte() { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + if (bitCount > 0) + { + buf[end++] = (byte) bits; + if (bitCount > 8) + buf[end++] = (byte) (bits >>> 8); + } + bits = 0; + bitCount = 0; + } + + public final void writeBits(int b, int count) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + if (DeflaterConstants.DEBUGGING) + System.err.println("writeBits("+Integer.toHexString(b)+","+count+")"); + bits |= b << bitCount; + bitCount += count; + if (bitCount >= 16) { + buf[end++] = (byte) bits; + buf[end++] = (byte) (bits >>> 8); + bits >>>= 16; + bitCount -= 16; + } + } + + public final void writeShortMSB(int s) { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) s; + } + + public final boolean isFlushed() { + return end == 0; + } + + /** + * Flushes the pending buffer into the given output array. If the + * output array is to small, only a partial flush is done. + * + * @param output the output array; + * @param offset the offset into output array; + * @param length the maximum number of bytes to store; + * @exception IndexOutOfBoundsException if offset or length are + * invalid. + */ + public final int flush(byte[] output, int offset, int length) { + if (bitCount >= 8) + { + buf[end++] = (byte) bits; + bits >>>= 8; + bitCount -= 8; + } + if (length > end - start) + { + length = end - start; + System.arraycopy(buf, start, output, offset, length); + start = 0; + end = 0; + } + else + { + System.arraycopy(buf, start, output, offset, length); + start += length; + } + return length; + } + + /** + * Flushes the pending buffer and returns that data in a new array + * + * @param output the output stream + */ + + public final byte[] toByteArray() + { + byte[] ret = new byte[ end - start ]; + System.arraycopy(buf, start, ret, 0, ret.length); + start = 0; + end = 0; + return ret; + } + + +} + diff --git a/libjava/classpath/java/util/zip/StreamManipulator.java b/libjava/classpath/java/util/zip/StreamManipulator.java new file mode 100644 index 00000000000..57d15ae283b --- /dev/null +++ b/libjava/classpath/java/util/zip/StreamManipulator.java @@ -0,0 +1,216 @@ +/* java.util.zip.StreamManipulator + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class allows us to retrieve a specified amount of bits from + * the input buffer, as well as copy big byte blocks. + * + * It uses an int buffer to store up to 31 bits for direct + * manipulation. This guarantees that we can get at least 16 bits, + * but we only need at most 15, so this is all safe. + * + * There are some optimizations in this class, for example, you must + * never peek more then 8 bits more than needed, and you must first + * peek bits before you may drop them. This is not a general purpose + * class but optimized for the behaviour of the Inflater. + * + * @author John Leuner, Jochen Hoenicke + */ + +class StreamManipulator +{ + private byte[] window; + private int window_start = 0; + private int window_end = 0; + + private int buffer = 0; + private int bits_in_buffer = 0; + + /** + * Get the next n bits but don't increase input pointer. n must be + * less or equal 16 and if you if this call succeeds, you must drop + * at least n-8 bits in the next call. + * + * @return the value of the bits, or -1 if not enough bits available. */ + public final int peekBits(int n) + { + if (bits_in_buffer < n) + { + if (window_start == window_end) + return -1; + buffer |= (window[window_start++] & 0xff + | (window[window_start++] & 0xff) << 8) << bits_in_buffer; + bits_in_buffer += 16; + } + return buffer & ((1 << n) - 1); + } + + /* Drops the next n bits from the input. You should have called peekBits + * with a bigger or equal n before, to make sure that enough bits are in + * the bit buffer. + */ + public final void dropBits(int n) + { + buffer >>>= n; + bits_in_buffer -= n; + } + + /** + * Gets the next n bits and increases input pointer. This is equivalent + * to peekBits followed by dropBits, except for correct error handling. + * @return the value of the bits, or -1 if not enough bits available. + */ + public final int getBits(int n) + { + int bits = peekBits(n); + if (bits >= 0) + dropBits(n); + return bits; + } + /** + * Gets the number of bits available in the bit buffer. This must be + * only called when a previous peekBits() returned -1. + * @return the number of bits available. + */ + public final int getAvailableBits() + { + return bits_in_buffer; + } + + /** + * Gets the number of bytes available. + * @return the number of bytes available. + */ + public final int getAvailableBytes() + { + return window_end - window_start + (bits_in_buffer >> 3); + } + + /** + * Skips to the next byte boundary. + */ + public void skipToByteBoundary() + { + buffer >>= (bits_in_buffer & 7); + bits_in_buffer &= ~7; + } + + public final boolean needsInput() { + return window_start == window_end; + } + + + /* Copies length bytes from input buffer to output buffer starting + * at output[offset]. You have to make sure, that the buffer is + * byte aligned. If not enough bytes are available, copies fewer + * bytes. + * @param length the length to copy, 0 is allowed. + * @return the number of bytes copied, 0 if no byte is available. + */ + public int copyBytes(byte[] output, int offset, int length) + { + if (length < 0) + throw new IllegalArgumentException("length negative"); + if ((bits_in_buffer & 7) != 0) + /* bits_in_buffer may only be 0 or 8 */ + throw new IllegalStateException("Bit buffer is not aligned!"); + + int count = 0; + while (bits_in_buffer > 0 && length > 0) + { + output[offset++] = (byte) buffer; + buffer >>>= 8; + bits_in_buffer -= 8; + length--; + count++; + } + if (length == 0) + return count; + + int avail = window_end - window_start; + if (length > avail) + length = avail; + System.arraycopy(window, window_start, output, offset, length); + window_start += length; + + if (((window_start - window_end) & 1) != 0) + { + /* We always want an even number of bytes in input, see peekBits */ + buffer = (window[window_start++] & 0xff); + bits_in_buffer = 8; + } + return count + length; + } + + public StreamManipulator() + { + } + + public void reset() + { + window_start = window_end = buffer = bits_in_buffer = 0; + } + + public void setInput(byte[] buf, int off, int len) + { + if (window_start < window_end) + throw new IllegalStateException + ("Old input was not completely processed"); + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + if ((len & 1) != 0) + { + /* We always want an even number of bytes in input, see peekBits */ + buffer |= (buf[off++] & 0xff) << bits_in_buffer; + bits_in_buffer += 8; + } + + window = buf; + window_start = off; + window_end = end; + } +} + diff --git a/libjava/classpath/java/util/zip/ZipConstants.java b/libjava/classpath/java/util/zip/ZipConstants.java new file mode 100644 index 00000000000..952a44def4c --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipConstants.java @@ -0,0 +1,97 @@ +/* java.util.zip.ZipConstants + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +interface ZipConstants +{ + /* The local file header */ + int LOCHDR = 30; + int LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24); + + int LOCVER = 4; + int LOCFLG = 6; + int LOCHOW = 8; + int LOCTIM = 10; + int LOCCRC = 14; + int LOCSIZ = 18; + int LOCLEN = 22; + int LOCNAM = 26; + int LOCEXT = 28; + + /* The Data descriptor */ + int EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24); + int EXTHDR = 16; + + int EXTCRC = 4; + int EXTSIZ = 8; + int EXTLEN = 12; + + /* The central directory file header */ + int CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24); + int CENHDR = 46; + + int CENVEM = 4; + int CENVER = 6; + int CENFLG = 8; + int CENHOW = 10; + int CENTIM = 12; + int CENCRC = 16; + int CENSIZ = 20; + int CENLEN = 24; + int CENNAM = 28; + int CENEXT = 30; + int CENCOM = 32; + int CENDSK = 34; + int CENATT = 36; + int CENATX = 38; + int CENOFF = 42; + + /* The entries in the end of central directory */ + int ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24); + int ENDHDR = 22; + + /* The following two fields are missing in SUN JDK */ + int ENDNRD = 4; + int ENDDCD = 6; + int ENDSUB = 8; + int ENDTOT = 10; + int ENDSIZ = 12; + int ENDOFF = 16; + int ENDCOM = 20; +} + diff --git a/libjava/classpath/java/util/zip/ZipEntry.java b/libjava/classpath/java/util/zip/ZipEntry.java new file mode 100644 index 00000000000..ae21997dba2 --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipEntry.java @@ -0,0 +1,432 @@ +/* ZipEntry.java -- + Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.util.Calendar; +import java.util.Date; + +/** + * This class represents a member of a zip archive. ZipFile and + * ZipInputStream will give you instances of this class as information + * about the members in an archive. On the other hand ZipOutputStream + * needs an instance of this class to create a new member. + * + * @author Jochen Hoenicke + */ +public class ZipEntry implements ZipConstants, Cloneable +{ + private static final int KNOWN_SIZE = 1; + private static final int KNOWN_CSIZE = 2; + private static final int KNOWN_CRC = 4; + private static final int KNOWN_TIME = 8; + private static final int KNOWN_EXTRA = 16; + + private static Calendar cal; + + private String name; + private int size; + private long compressedSize = -1; + private int crc; + private int dostime; + private short known = 0; + private short method = -1; + private byte[] extra = null; + private String comment = null; + + int flags; /* used by ZipOutputStream */ + int offset; /* used by ZipFile and ZipOutputStream */ + + /** + * Compression method. This method doesn't compress at all. + */ + public static final int STORED = 0; + /** + * Compression method. This method uses the Deflater. + */ + public static final int DEFLATED = 8; + + /** + * Creates a zip entry with the given name. + * @param name the name. May include directory components separated + * by '/'. + * + * @exception NullPointerException when name is null. + * @exception IllegalArgumentException when name is bigger then 65535 chars. + */ + public ZipEntry(String name) + { + int length = name.length(); + if (length > 65535) + throw new IllegalArgumentException("name length is " + length); + this.name = name; + } + + /** + * Creates a copy of the given zip entry. + * @param e the entry to copy. + */ + public ZipEntry(ZipEntry e) + { + this(e, e.name); + } + + ZipEntry(ZipEntry e, String name) + { + this.name = name; + known = e.known; + size = e.size; + compressedSize = e.compressedSize; + crc = e.crc; + dostime = e.dostime; + method = e.method; + extra = e.extra; + comment = e.comment; + } + + final void setDOSTime(int dostime) + { + this.dostime = dostime; + known |= KNOWN_TIME; + } + + final int getDOSTime() + { + if ((known & KNOWN_TIME) == 0) + return 0; + else + return dostime; + } + + /** + * Creates a copy of this zip entry. + */ + /** + * Clones the entry. + */ + public Object clone() + { + try + { + // The JCL says that the `extra' field is also copied. + ZipEntry clone = (ZipEntry) super.clone(); + if (extra != null) + clone.extra = (byte[]) extra.clone(); + return clone; + } + catch (CloneNotSupportedException ex) + { + throw new InternalError(); + } + } + + /** + * Returns the entry name. The path components in the entry are + * always separated by slashes ('/'). + */ + public String getName() + { + return name; + } + + /** + * Sets the time of last modification of the entry. + * @time the time of last modification of the entry. + */ + public void setTime(long time) + { + Calendar cal = getCalendar(); + synchronized (cal) + { + cal.setTime(new Date(time)); + dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 + | (cal.get(Calendar.MONTH) + 1) << 21 + | (cal.get(Calendar.DAY_OF_MONTH)) << 16 + | (cal.get(Calendar.HOUR_OF_DAY)) << 11 + | (cal.get(Calendar.MINUTE)) << 5 + | (cal.get(Calendar.SECOND)) >> 1; + } + this.known |= KNOWN_TIME; + } + + /** + * Gets the time of last modification of the entry. + * @return the time of last modification of the entry, or -1 if unknown. + */ + public long getTime() + { + if ((known & KNOWN_TIME) == 0) + return -1; + + // The extra bytes might contain the time (posix/unix extension) + parseExtra (); + + int sec = 2 * (dostime & 0x1f); + int min = (dostime >> 5) & 0x3f; + int hrs = (dostime >> 11) & 0x1f; + int day = (dostime >> 16) & 0x1f; + int mon = ((dostime >> 21) & 0xf) - 1; + int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ + + try + { + cal = getCalendar(); + synchronized (cal) + { + cal.set(year, mon, day, hrs, min, sec); + return cal.getTime().getTime(); + } + } + catch (RuntimeException ex) + { + /* Ignore illegal time stamp */ + known &= ~KNOWN_TIME; + return -1; + } + } + + private static synchronized Calendar getCalendar() + { + if (cal == null) + cal = Calendar.getInstance(); + + return cal; + } + + /** + * Sets the size of the uncompressed data. + * @exception IllegalArgumentException if size is not in 0..0xffffffffL + */ + public void setSize(long size) + { + if ((size & 0xffffffff00000000L) != 0) + throw new IllegalArgumentException(); + this.size = (int) size; + this.known |= KNOWN_SIZE; + } + + /** + * Gets the size of the uncompressed data. + * @return the size or -1 if unknown. + */ + public long getSize() + { + return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; + } + + /** + * Sets the size of the compressed data. + */ + public void setCompressedSize(long csize) + { + this.compressedSize = csize; + } + + /** + * Gets the size of the compressed data. + * @return the size or -1 if unknown. + */ + public long getCompressedSize() + { + return compressedSize; + } + + /** + * Sets the crc of the uncompressed data. + * @exception IllegalArgumentException if crc is not in 0..0xffffffffL + */ + public void setCrc(long crc) + { + if ((crc & 0xffffffff00000000L) != 0) + throw new IllegalArgumentException(); + this.crc = (int) crc; + this.known |= KNOWN_CRC; + } + + /** + * Gets the crc of the uncompressed data. + * @return the crc or -1 if unknown. + */ + public long getCrc() + { + return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; + } + + /** + * Sets the compression method. Only DEFLATED and STORED are + * supported. + * @exception IllegalArgumentException if method is not supported. + * @see ZipOutputStream#DEFLATED + * @see ZipOutputStream#STORED + */ + public void setMethod(int method) + { + if (method != ZipOutputStream.STORED + && method != ZipOutputStream.DEFLATED) + throw new IllegalArgumentException(); + this.method = (short) method; + } + + /** + * Gets the compression method. + * @return the compression method or -1 if unknown. + */ + public int getMethod() + { + return method; + } + + /** + * Sets the extra data. + * @exception IllegalArgumentException if extra is longer than 0xffff bytes. + */ + public void setExtra(byte[] extra) + { + if (extra == null) + { + this.extra = null; + return; + } + if (extra.length > 0xffff) + throw new IllegalArgumentException(); + this.extra = extra; + } + + private void parseExtra() + { + // Already parsed? + if ((known & KNOWN_EXTRA) != 0) + return; + + if (extra == null) + { + known |= KNOWN_EXTRA; + return; + } + + try + { + int pos = 0; + while (pos < extra.length) + { + int sig = (extra[pos++] & 0xff) + | (extra[pos++] & 0xff) << 8; + int len = (extra[pos++] & 0xff) + | (extra[pos++] & 0xff) << 8; + if (sig == 0x5455) + { + /* extended time stamp */ + int flags = extra[pos]; + if ((flags & 1) != 0) + { + long time = ((extra[pos+1] & 0xff) + | (extra[pos+2] & 0xff) << 8 + | (extra[pos+3] & 0xff) << 16 + | (extra[pos+4] & 0xff) << 24); + setTime(time); + } + } + pos += len; + } + } + catch (ArrayIndexOutOfBoundsException ex) + { + /* be lenient */ + return; + } + + known |= KNOWN_EXTRA; + } + + /** + * Gets the extra data. + * @return the extra data or null if not set. + */ + public byte[] getExtra() + { + return extra; + } + + /** + * Sets the entry comment. + * @exception IllegalArgumentException if comment is longer than 0xffff. + */ + public void setComment(String comment) + { + if (comment != null && comment.length() > 0xffff) + throw new IllegalArgumentException(); + this.comment = comment; + } + + /** + * Gets the comment. + * @return the comment or null if not set. + */ + public String getComment() + { + return comment; + } + + /** + * Gets true, if the entry is a directory. This is solely + * determined by the name, a trailing slash '/' marks a directory. + */ + public boolean isDirectory() + { + int nlen = name.length(); + return nlen > 0 && name.charAt(nlen - 1) == '/'; + } + + /** + * Gets the string representation of this ZipEntry. This is just + * the name as returned by getName(). + */ + public String toString() + { + return name; + } + + /** + * Gets the hashCode of this ZipEntry. This is just the hashCode + * of the name. Note that the equals method isn't changed, though. + */ + public int hashCode() + { + return name.hashCode(); + } +} diff --git a/libjava/classpath/java/util/zip/ZipException.java b/libjava/classpath/java/util/zip/ZipException.java new file mode 100644 index 00000000000..c5bfc1e7c39 --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipException.java @@ -0,0 +1,72 @@ +/* ZipException.java - exception representing a zip related error + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +import java.io.IOException; + +/** + * Thrown during the creation or input of a zip file. + * + * @author Jochen Hoenicke + * @author Per Bothner + * @status updated to 1.4 + */ +public class ZipException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8000196834066748623L; + + /** + * Create an exception without a message. + */ + public ZipException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public ZipException (String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/zip/ZipFile.java b/libjava/classpath/java/util/zip/ZipFile.java new file mode 100644 index 00000000000..33f67c78bfe --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipFile.java @@ -0,0 +1,595 @@ +/* ZipFile.java -- + Copyright (C) 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import gnu.java.util.EmptyEnumeration; + +import java.io.BufferedInputStream; +import java.io.DataInput; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; + +/** + * This class represents a Zip archive. You can ask for the contained + * entries, or get an input stream for a file entry. The entry is + * automatically decompressed. + * + * This class is thread safe: You can open input streams for arbitrary + * entries in different threads. + * + * @author Jochen Hoenicke + * @author Artur Biesiadowski + */ +public class ZipFile implements ZipConstants +{ + + /** + * Mode flag to open a zip file for reading. + */ + public static final int OPEN_READ = 0x1; + + /** + * Mode flag to delete a zip file after reading. + */ + public static final int OPEN_DELETE = 0x4; + + // Name of this zip file. + private final String name; + + // File from which zip entries are read. + private final RandomAccessFile raf; + + // The entries of this zip file when initialized and not yet closed. + private HashMap entries; + + private boolean closed = false; + + /** + * Opens a Zip file with the given name for reading. + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(String name) throws ZipException, IOException + { + this.raf = new RandomAccessFile(name, "r"); + this.name = name; + checkZipFile(); + } + + /** + * Opens a Zip file reading the given File. + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(File file) throws ZipException, IOException + { + this.raf = new RandomAccessFile(file, "r"); + this.name = file.getPath(); + checkZipFile(); + } + + /** + * Opens a Zip file reading the given File in the given mode. + * + * If the OPEN_DELETE mode is specified, the zip file will be deleted at + * some time moment after it is opened. It will be deleted before the zip + * file is closed or the Virtual Machine exits. + * + * The contents of the zip file will be accessible until it is closed. + * + * @since JDK1.3 + * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE + * + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(File file, int mode) throws ZipException, IOException + { + if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) + throw new IllegalArgumentException("invalid mode"); + if ((mode & OPEN_DELETE) != 0) + file.deleteOnExit(); + this.raf = new RandomAccessFile(file, "r"); + this.name = file.getPath(); + checkZipFile(); + } + + private void checkZipFile() throws IOException, ZipException + { + byte[] magicBuf = new byte[4]; + raf.read(magicBuf); + + if (readLeInt(magicBuf, 0) != LOCSIG) + { + raf.close(); + throw new ZipException("Not a valid zip file"); + } + } + + /** + * Checks if file is closed and throws an exception. + */ + private void checkClosed() + { + if (closed) + throw new IllegalStateException("ZipFile has closed: " + name); + } + + /** + * Read an unsigned short in little endian byte order from the given + * DataInput stream using the given byte buffer. + * + * @param di DataInput stream to read from. + * @param b the byte buffer to read in (must be at least 2 bytes long). + * @return The value read. + * + * @exception IOException if a i/o error occured. + * @exception EOFException if the file ends prematurely + */ + private int readLeShort(DataInput di, byte[] b) throws IOException + { + di.readFully(b, 0, 2); + return (b[0] & 0xff) | (b[1] & 0xff) << 8; + } + + /** + * Read an int in little endian byte order from the given + * DataInput stream using the given byte buffer. + * + * @param di DataInput stream to read from. + * @param b the byte buffer to read in (must be at least 4 bytes long). + * @return The value read. + * + * @exception IOException if a i/o error occured. + * @exception EOFException if the file ends prematurely + */ + private int readLeInt(DataInput di, byte[] b) throws IOException + { + di.readFully(b, 0, 4); + return ((b[0] & 0xff) | (b[1] & 0xff) << 8) + | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16; + } + + /** + * Read an unsigned short in little endian byte order from the given + * byte buffer at the given offset. + * + * @param b the byte array to read from. + * @param off the offset to read from. + * @return The value read. + */ + private int readLeShort(byte[] b, int off) + { + return (b[off] & 0xff) | (b[off+1] & 0xff) << 8; + } + + /** + * Read an int in little endian byte order from the given + * byte buffer at the given offset. + * + * @param b the byte array to read from. + * @param off the offset to read from. + * @return The value read. + */ + private int readLeInt(byte[] b, int off) + { + return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8) + | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16; + } + + + /** + * Read the central directory of a zip file and fill the entries + * array. This is called exactly once when first needed. It is called + * while holding the lock on <code>raf</code>. + * + * @exception IOException if a i/o error occured. + * @exception ZipException if the central directory is malformed + */ + private void readEntries() throws ZipException, IOException + { + /* Search for the End Of Central Directory. When a zip comment is + * present the directory may start earlier. + * FIXME: This searches the whole file in a very slow manner if the + * file isn't a zip file. + */ + long pos = raf.length() - ENDHDR; + byte[] ebs = new byte[CENHDR]; + + do + { + if (pos < 0) + throw new ZipException + ("central directory not found, probably not a zip file: " + name); + raf.seek(pos--); + } + while (readLeInt(raf, ebs) != ENDSIG); + + if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD) + throw new EOFException(name); + int count = readLeShort(raf, ebs); + if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ) + throw new EOFException(name); + int centralOffset = readLeInt(raf, ebs); + + entries = new HashMap(count+count/2); + raf.seek(centralOffset); + + byte[] buffer = new byte[16]; + for (int i = 0; i < count; i++) + { + raf.readFully(ebs); + if (readLeInt(ebs, 0) != CENSIG) + throw new ZipException("Wrong Central Directory signature: " + name); + + int method = readLeShort(ebs, CENHOW); + int dostime = readLeInt(ebs, CENTIM); + int crc = readLeInt(ebs, CENCRC); + int csize = readLeInt(ebs, CENSIZ); + int size = readLeInt(ebs, CENLEN); + int nameLen = readLeShort(ebs, CENNAM); + int extraLen = readLeShort(ebs, CENEXT); + int commentLen = readLeShort(ebs, CENCOM); + + int offset = readLeInt(ebs, CENOFF); + + int needBuffer = Math.max(nameLen, commentLen); + if (buffer.length < needBuffer) + buffer = new byte[needBuffer]; + + raf.readFully(buffer, 0, nameLen); + String name = new String(buffer, 0, 0, nameLen); + + ZipEntry entry = new ZipEntry(name); + entry.setMethod(method); + entry.setCrc(crc & 0xffffffffL); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + entry.setDOSTime(dostime); + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + raf.readFully(extra); + entry.setExtra(extra); + } + if (commentLen > 0) + { + raf.readFully(buffer, 0, commentLen); + entry.setComment(new String(buffer, 0, commentLen)); + } + entry.offset = offset; + entries.put(name, entry); + } + } + + /** + * Closes the ZipFile. This also closes all input streams given by + * this class. After this is called, no further method should be + * called. + * + * @exception IOException if a i/o error occured. + */ + public void close() throws IOException + { + synchronized (raf) + { + closed = true; + entries = null; + raf.close(); + } + } + + /** + * Calls the <code>close()</code> method when this ZipFile has not yet + * been explicitly closed. + */ + protected void finalize() throws IOException + { + if (!closed && raf != null) close(); + } + + /** + * Returns an enumeration of all Zip entries in this Zip file. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public Enumeration entries() + { + checkClosed(); + + try + { + return new ZipEntryEnumeration(getEntries().values().iterator()); + } + catch (IOException ioe) + { + return EmptyEnumeration.getInstance(); + } + } + + /** + * Checks that the ZipFile is still open and reads entries when necessary. + * + * @exception IllegalStateException when the ZipFile has already been closed. + * @exception IOEexception when the entries could not be read. + */ + private HashMap getEntries() throws IOException + { + synchronized(raf) + { + checkClosed(); + + if (entries == null) + readEntries(); + + return entries; + } + } + + /** + * Searches for a zip entry in this archive with the given name. + * + * @param the name. May contain directory components separated by + * slashes ('/'). + * @return the zip entry, or null if no entry with that name exists. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public ZipEntry getEntry(String name) + { + checkClosed(); + + try + { + HashMap entries = getEntries(); + ZipEntry entry = (ZipEntry) entries.get(name); + // If we didn't find it, maybe it's a directory. + if (entry == null && !name.endsWith("/")) + entry = (ZipEntry) entries.get(name + '/'); + return entry != null ? new ZipEntry(entry, name) : null; + } + catch (IOException ioe) + { + return null; + } + } + + + //access should be protected by synchronized(raf) + private byte[] locBuf = new byte[LOCHDR]; + + /** + * Checks, if the local header of the entry at index i matches the + * central directory, and returns the offset to the data. + * + * @param entry to check. + * @return the start offset of the (compressed) data. + * + * @exception IOException if a i/o error occured. + * @exception ZipException if the local header doesn't match the + * central directory header + */ + private long checkLocalHeader(ZipEntry entry) throws IOException + { + synchronized (raf) + { + raf.seek(entry.offset); + raf.readFully(locBuf); + + if (readLeInt(locBuf, 0) != LOCSIG) + throw new ZipException("Wrong Local header signature: " + name); + + if (entry.getMethod() != readLeShort(locBuf, LOCHOW)) + throw new ZipException("Compression method mismatch: " + name); + + if (entry.getName().length() != readLeShort(locBuf, LOCNAM)) + throw new ZipException("file name length mismatch: " + name); + + int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT); + return entry.offset + LOCHDR + extraLen; + } + } + + /** + * Creates an input stream reading the given zip entry as + * uncompressed data. Normally zip entry should be an entry + * returned by getEntry() or entries(). + * + * This implementation returns null if the requested entry does not + * exist. This decision is not obviously correct, however, it does + * appear to mirror Sun's implementation, and it is consistant with + * their javadoc. On the other hand, the old JCL book, 2nd Edition, + * claims that this should return a "non-null ZIP entry". We have + * chosen for now ignore the old book, as modern versions of Ant (an + * important application) depend on this behaviour. See discussion + * in this thread: + * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html + * + * @param entry the entry to create an InputStream for. + * @return the input stream, or null if the requested entry does not exist. + * + * @exception IllegalStateException when the ZipFile has already been closed + * @exception IOException if a i/o error occured. + * @exception ZipException if the Zip archive is malformed. + */ + public InputStream getInputStream(ZipEntry entry) throws IOException + { + checkClosed(); + + HashMap entries = getEntries(); + String name = entry.getName(); + ZipEntry zipEntry = (ZipEntry) entries.get(name); + if (zipEntry == null) + return null; + + long start = checkLocalHeader(zipEntry); + int method = zipEntry.getMethod(); + InputStream is = new BufferedInputStream(new PartialInputStream + (raf, start, zipEntry.getCompressedSize())); + switch (method) + { + case ZipOutputStream.STORED: + return is; + case ZipOutputStream.DEFLATED: + return new InflaterInputStream(is, new Inflater(true)); + default: + throw new ZipException("Unknown compression method " + method); + } + } + + /** + * Returns the (path) name of this zip file. + */ + public String getName() + { + return name; + } + + /** + * Returns the number of entries in this zip file. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public int size() + { + checkClosed(); + + try + { + return getEntries().size(); + } + catch (IOException ioe) + { + return 0; + } + } + + private static class ZipEntryEnumeration implements Enumeration + { + private final Iterator elements; + + public ZipEntryEnumeration(Iterator elements) + { + this.elements = elements; + } + + public boolean hasMoreElements() + { + return elements.hasNext(); + } + + public Object nextElement() + { + /* We return a clone, just to be safe that the user doesn't + * change the entry. + */ + return ((ZipEntry)elements.next()).clone(); + } + } + + private static class PartialInputStream extends InputStream + { + private final RandomAccessFile raf; + long filepos, end; + + public PartialInputStream(RandomAccessFile raf, long start, long len) + { + this.raf = raf; + filepos = start; + end = start + len; + } + + public int available() + { + long amount = end - filepos; + if (amount > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + return (int) amount; + } + + public int read() throws IOException + { + if (filepos == end) + return -1; + synchronized (raf) + { + raf.seek(filepos++); + return raf.read(); + } + } + + public int read(byte[] b, int off, int len) throws IOException + { + if (len > end - filepos) + { + len = (int) (end - filepos); + if (len == 0) + return -1; + } + synchronized (raf) + { + raf.seek(filepos); + int count = raf.read(b, off, len); + if (count > 0) + filepos += len; + return count; + } + } + + public long skip(long amount) + { + if (amount < 0) + throw new IllegalArgumentException(); + if (amount > end - filepos) + amount = end - filepos; + filepos += amount; + return amount; + } + } +} diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java new file mode 100644 index 00000000000..5732523238e --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipInputStream.java @@ -0,0 +1,371 @@ +/* ZipInputStream.java -- + Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * This is a FilterInputStream that reads the files in an zip archive + * one after another. It has a special method to get the zip entry of + * the next file. The zip entry contains information about the file name + * size, compressed size, CRC, etc. + * + * It includes support for STORED and DEFLATED entries. + * + * @author Jochen Hoenicke + */ +public class ZipInputStream extends InflaterInputStream implements ZipConstants +{ + private CRC32 crc = new CRC32(); + private ZipEntry entry = null; + + private int csize; + private int size; + private int method; + private int flags; + private int avail; + private boolean entryAtEOF; + + /** + * Creates a new Zip input stream, reading a zip archive. + */ + public ZipInputStream(InputStream in) + { + super(in, new Inflater(true)); + } + + private void fillBuf() throws IOException + { + avail = len = in.read(buf, 0, buf.length); + } + + private int readBuf(byte[] out, int offset, int length) throws IOException + { + if (avail <= 0) + { + fillBuf(); + if (avail <= 0) + return -1; + } + if (length > avail) + length = avail; + System.arraycopy(buf, len - avail, out, offset, length); + avail -= length; + return length; + } + + private void readFully(byte[] out) throws IOException + { + int off = 0; + int len = out.length; + while (len > 0) + { + int count = readBuf(out, off, len); + if (count == -1) + throw new EOFException(); + off += count; + len -= count; + } + } + + private int readLeByte() throws IOException + { + if (avail <= 0) + { + fillBuf(); + if (avail <= 0) + throw new ZipException("EOF in header"); + } + return buf[len - avail--] & 0xff; + } + + /** + * Read an unsigned short in little endian byte order. + */ + private int readLeShort() throws IOException + { + return readLeByte() | (readLeByte() << 8); + } + + /** + * Read an int in little endian byte order. + */ + private int readLeInt() throws IOException + { + return readLeShort() | (readLeShort() << 16); + } + + /** + * Open the next entry from the zip archive, and return its description. + * If the previous entry wasn't closed, this method will close it. + */ + public ZipEntry getNextEntry() throws IOException + { + if (crc == null) + throw new IOException("Stream closed."); + if (entry != null) + closeEntry(); + + int header = readLeInt(); + if (header == CENSIG) + { + /* Central Header reached. */ + close(); + return null; + } + if (header != LOCSIG) + throw new ZipException("Wrong Local header signature: " + + Integer.toHexString(header)); + /* skip version */ + readLeShort(); + flags = readLeShort(); + method = readLeShort(); + int dostime = readLeInt(); + int crc = readLeInt(); + csize = readLeInt(); + size = readLeInt(); + int nameLen = readLeShort(); + int extraLen = readLeShort(); + + if (method == ZipOutputStream.STORED && csize != size) + throw new ZipException("Stored, but compressed != uncompressed"); + + + byte[] buffer = new byte[nameLen]; + readFully(buffer); + String name = new String(buffer); + + entry = createZipEntry(name); + entryAtEOF = false; + entry.setMethod(method); + if ((flags & 8) == 0) + { + entry.setCrc(crc & 0xffffffffL); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + } + entry.setDOSTime(dostime); + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + readFully(extra); + entry.setExtra(extra); + } + + if (method == ZipOutputStream.DEFLATED && avail > 0) + { + System.arraycopy(buf, len - avail, buf, 0, avail); + len = avail; + avail = 0; + inf.setInput(buf, 0, len); + } + return entry; + } + + private void readDataDescr() throws IOException + { + if (readLeInt() != EXTSIG) + throw new ZipException("Data descriptor signature not found"); + entry.setCrc(readLeInt() & 0xffffffffL); + csize = readLeInt(); + size = readLeInt(); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + } + + /** + * Closes the current zip entry and moves to the next one. + */ + public void closeEntry() throws IOException + { + if (crc == null) + throw new IOException("Stream closed."); + if (entry == null) + return; + + if (method == ZipOutputStream.DEFLATED) + { + if ((flags & 8) != 0) + { + /* We don't know how much we must skip, read until end. */ + byte[] tmp = new byte[2048]; + while (read(tmp) > 0) + ; + /* read will close this entry */ + return; + } + csize -= inf.getTotalIn(); + avail = inf.getRemaining(); + } + + if (avail > csize && csize >= 0) + avail -= csize; + else + { + csize -= avail; + avail = 0; + while (csize != 0) + { + long skipped = in.skip(csize & 0xffffffffL); + if (skipped <= 0) + throw new ZipException("zip archive ends early."); + csize -= skipped; + } + } + + size = 0; + crc.reset(); + if (method == ZipOutputStream.DEFLATED) + inf.reset(); + entry = null; + entryAtEOF = true; + } + + public int available() throws IOException + { + return entryAtEOF ? 0 : 1; + } + + /** + * Reads a byte from the current zip entry. + * @return the byte or -1 on EOF. + * @exception IOException if a i/o error occured. + * @exception ZipException if the deflated stream is corrupted. + */ + public int read() throws IOException + { + byte[] b = new byte[1]; + if (read(b, 0, 1) <= 0) + return -1; + return b[0] & 0xff; + } + + /** + * Reads a block of bytes from the current zip entry. + * @return the number of bytes read (may be smaller, even before + * EOF), or -1 on EOF. + * @exception IOException if a i/o error occured. + * @exception ZipException if the deflated stream is corrupted. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (len == 0) + return 0; + if (crc == null) + throw new IOException("Stream closed."); + if (entry == null) + return -1; + boolean finished = false; + switch (method) + { + case ZipOutputStream.DEFLATED: + len = super.read(b, off, len); + if (len < 0) + { + if (!inf.finished()) + throw new ZipException("Inflater not finished!?"); + avail = inf.getRemaining(); + if ((flags & 8) != 0) + readDataDescr(); + + if (inf.getTotalIn() != csize + || inf.getTotalOut() != size) + throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut()); + inf.reset(); + finished = true; + } + break; + + case ZipOutputStream.STORED: + + if (len > csize && csize >= 0) + len = csize; + + len = readBuf(b, off, len); + if (len > 0) + { + csize -= len; + size -= len; + } + + if (csize == 0) + finished = true; + else if (len < 0) + throw new ZipException("EOF in stored block"); + break; + } + + if (len > 0) + crc.update(b, off, len); + + if (finished) + { + if ((crc.getValue() & 0xffffffffL) != entry.getCrc()) + throw new ZipException("CRC mismatch"); + crc.reset(); + entry = null; + entryAtEOF = true; + } + return len; + } + + /** + * Closes the zip file. + * @exception IOException if a i/o error occured. + */ + public void close() throws IOException + { + super.close(); + crc = null; + entry = null; + entryAtEOF = true; + } + + /** + * Creates a new zip entry for the given name. This is equivalent + * to new ZipEntry(name). + * @param name the name of the zip entry. + */ + protected ZipEntry createZipEntry(String name) + { + return new ZipEntry(name); + } +} diff --git a/libjava/classpath/java/util/zip/ZipOutputStream.java b/libjava/classpath/java/util/zip/ZipOutputStream.java new file mode 100644 index 00000000000..5699ff046f5 --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipOutputStream.java @@ -0,0 +1,399 @@ +/* ZipOutputStream.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Vector; + +/** + * This is a FilterOutputStream that writes the files into a zip + * archive one after another. It has a special method to start a new + * zip entry. The zip entries contains information about the file name + * size, compressed size, CRC, etc. + * + * It includes support for STORED and DEFLATED entries. + * + * This class is not thread safe. + * + * @author Jochen Hoenicke + */ +public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants +{ + private Vector entries = new Vector(); + private CRC32 crc = new CRC32(); + private ZipEntry curEntry = null; + + private int curMethod; + private int size; + private int offset = 0; + + private byte[] zipComment = new byte[0]; + private int defaultMethod = DEFLATED; + + /** + * Our Zip version is hard coded to 1.0 resp. 2.0 + */ + private static final int ZIP_STORED_VERSION = 10; + private static final int ZIP_DEFLATED_VERSION = 20; + + /** + * Compression method. This method doesn't compress at all. + */ + public static final int STORED = 0; + + /** + * Compression method. This method uses the Deflater. + */ + public static final int DEFLATED = 8; + + /** + * Creates a new Zip output stream, writing a zip archive. + * @param out the output stream to which the zip archive is written. + */ + public ZipOutputStream(OutputStream out) + { + super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); + } + + /** + * Set the zip file comment. + * @param comment the comment. + * @exception IllegalArgumentException if encoding of comment is + * longer than 0xffff bytes. + */ + public void setComment(String comment) + { + byte[] commentBytes; + commentBytes = comment.getBytes(); + if (commentBytes.length > 0xffff) + throw new IllegalArgumentException("Comment too long."); + zipComment = commentBytes; + } + + /** + * Sets default compression method. If the Zip entry specifies + * another method its method takes precedence. + * @param method the method. + * @exception IllegalArgumentException if method is not supported. + * @see #STORED + * @see #DEFLATED + */ + public void setMethod(int method) + { + if (method != STORED && method != DEFLATED) + throw new IllegalArgumentException("Method not supported."); + defaultMethod = method; + } + + /** + * Sets default compression level. The new level will be activated + * immediately. + * @exception IllegalArgumentException if level is not supported. + * @see Deflater + */ + public void setLevel(int level) + { + def.setLevel(level); + } + + /** + * Write an unsigned short in little endian byte order. + */ + private void writeLeShort(int value) throws IOException + { + out.write(value & 0xff); + out.write((value >> 8) & 0xff); + } + + /** + * Write an int in little endian byte order. + */ + private void writeLeInt(int value) throws IOException + { + writeLeShort(value); + writeLeShort(value >> 16); + } + + /** + * Starts a new Zip entry. It automatically closes the previous + * entry if present. If the compression method is stored, the entry + * must have a valid size and crc, otherwise all elements (except + * name) are optional, but must be correct if present. If the time + * is not set in the entry, the current time is used. + * @param entry the entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if stream was finished. + */ + public void putNextEntry(ZipEntry entry) throws IOException + { + if (entries == null) + throw new ZipException("ZipOutputStream was finished"); + + int method = entry.getMethod(); + int flags = 0; + if (method == -1) + method = defaultMethod; + + if (method == STORED) + { + if (entry.getCompressedSize() >= 0) + { + if (entry.getSize() < 0) + entry.setSize(entry.getCompressedSize()); + else if (entry.getSize() != entry.getCompressedSize()) + throw new ZipException + ("Method STORED, but compressed size != size"); + } + else + entry.setCompressedSize(entry.getSize()); + + if (entry.getSize() < 0) + throw new ZipException("Method STORED, but size not set"); + if (entry.getCrc() < 0) + throw new ZipException("Method STORED, but crc not set"); + } + else if (method == DEFLATED) + { + if (entry.getCompressedSize() < 0 + || entry.getSize() < 0 || entry.getCrc() < 0) + flags |= 8; + } + + if (curEntry != null) + closeEntry(); + + if (entry.getTime() < 0) + entry.setTime(System.currentTimeMillis()); + + entry.flags = flags; + entry.offset = offset; + entry.setMethod(method); + curMethod = method; + /* Write the local file header */ + writeLeInt(LOCSIG); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(flags); + writeLeShort(method); + writeLeInt(entry.getDOSTime()); + if ((flags & 8) == 0) + { + writeLeInt((int)entry.getCrc()); + writeLeInt((int)entry.getCompressedSize()); + writeLeInt((int)entry.getSize()); + } + else + { + writeLeInt(0); + writeLeInt(0); + writeLeInt(0); + } + byte[] name = entry.getName().getBytes(); + if (name.length > 0xffff) + throw new ZipException("Name too long."); + byte[] extra = entry.getExtra(); + if (extra == null) + extra = new byte[0]; + writeLeShort(name.length); + writeLeShort(extra.length); + out.write(name); + out.write(extra); + + offset += LOCHDR + name.length + extra.length; + + /* Activate the entry. */ + + curEntry = entry; + crc.reset(); + if (method == DEFLATED) + def.reset(); + size = 0; + } + + /** + * Closes the current entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if no entry is active. + */ + public void closeEntry() throws IOException + { + if (curEntry == null) + throw new ZipException("No open entry"); + + /* First finish the deflater, if appropriate */ + if (curMethod == DEFLATED) + super.finish(); + + int csize = curMethod == DEFLATED ? def.getTotalOut() : size; + + if (curEntry.getSize() < 0) + curEntry.setSize(size); + else if (curEntry.getSize() != size) + throw new ZipException("size was "+size + +", but I expected "+curEntry.getSize()); + + if (curEntry.getCompressedSize() < 0) + curEntry.setCompressedSize(csize); + else if (curEntry.getCompressedSize() != csize) + throw new ZipException("compressed size was "+csize + +", but I expected "+curEntry.getSize()); + + if (curEntry.getCrc() < 0) + curEntry.setCrc(crc.getValue()); + else if (curEntry.getCrc() != crc.getValue()) + throw new ZipException("crc was " + Long.toHexString(crc.getValue()) + + ", but I expected " + + Long.toHexString(curEntry.getCrc())); + + offset += csize; + + /* Now write the data descriptor entry if needed. */ + if (curMethod == DEFLATED && (curEntry.flags & 8) != 0) + { + writeLeInt(EXTSIG); + writeLeInt((int)curEntry.getCrc()); + writeLeInt((int)curEntry.getCompressedSize()); + writeLeInt((int)curEntry.getSize()); + offset += EXTHDR; + } + + entries.addElement(curEntry); + curEntry = null; + } + + /** + * Writes the given buffer to the current entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if no entry is active. + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (curEntry == null) + throw new ZipException("No open entry."); + + switch (curMethod) + { + case DEFLATED: + super.write(b, off, len); + break; + + case STORED: + out.write(b, off, len); + break; + } + + crc.update(b, off, len); + size += len; + } + + /** + * Finishes the stream. This will write the central directory at the + * end of the zip file and flush the stream. + * @exception IOException if an I/O error occured. + */ + public void finish() throws IOException + { + if (entries == null) + return; + if (curEntry != null) + closeEntry(); + + int numEntries = 0; + int sizeEntries = 0; + + Enumeration e = entries.elements(); + while (e.hasMoreElements()) + { + ZipEntry entry = (ZipEntry) e.nextElement(); + + int method = entry.getMethod(); + writeLeInt(CENSIG); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(entry.flags); + writeLeShort(method); + writeLeInt(entry.getDOSTime()); + writeLeInt((int)entry.getCrc()); + writeLeInt((int)entry.getCompressedSize()); + writeLeInt((int)entry.getSize()); + + byte[] name = entry.getName().getBytes(); + if (name.length > 0xffff) + throw new ZipException("Name too long."); + byte[] extra = entry.getExtra(); + if (extra == null) + extra = new byte[0]; + String strComment = entry.getComment(); + byte[] comment = strComment != null + ? strComment.getBytes() : new byte[0]; + if (comment.length > 0xffff) + throw new ZipException("Comment too long."); + + writeLeShort(name.length); + writeLeShort(extra.length); + writeLeShort(comment.length); + writeLeShort(0); /* disk number */ + writeLeShort(0); /* internal file attr */ + writeLeInt(0); /* external file attr */ + writeLeInt(entry.offset); + + out.write(name); + out.write(extra); + out.write(comment); + numEntries++; + sizeEntries += CENHDR + name.length + extra.length + comment.length; + } + + writeLeInt(ENDSIG); + writeLeShort(0); /* disk number */ + writeLeShort(0); /* disk with start of central dir */ + writeLeShort(numEntries); + writeLeShort(numEntries); + writeLeInt(sizeEntries); + writeLeInt(offset); + writeLeShort(zipComment.length); + out.write(zipComment); + out.flush(); + entries = null; + } +} diff --git a/libjava/classpath/java/util/zip/package.html b/libjava/classpath/java/util/zip/package.html new file mode 100644 index 00000000000..5f3ac491776 --- /dev/null +++ b/libjava/classpath/java/util/zip/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.util.zip package. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.util.zip</title></head> + +<body> +<p>Utility classes to manipulate zip and gzip archives as files or streams, +includes checksum and compression support.</p> + +</body> +</html> |